ThreeJS 开发实例

形状和材质

虚线

画虚线

function DrawDashLine(){var geometry = new THREE.Geometry();/*** vertexColors: false 关闭使用点颜色来生成线的颜色,这个值默认是false,如果设置为true,那么后面设置的color将不起作用* dashSize:30 点长度30个单位* gapSize:20 点与点之间间隔长度20个单位* @type {THREE.LineDashedMaterial}*/var material = new THREE.LineDashedMaterial({ vertexColors: false,dashSize:30,gapSize:20,color:0x839848});var p1 = new THREE.Vector3(0, 0,0);var p2 = new THREE.Vector3(500,300,0);geometry.vertices.push(p1);geometry.vertices.push(p2);/*** 记得加上这句,用于计算上面设置的点和间隔长度,不设置则画出来是实线*/geometry.computeLineDistances();          var line = new THREE.Line(geometry,material,THREE.LineSegments );scene.add(line);}

注意:threejs定义了很多computeXXX方法和XXXneedsUpdate属性,这表示XXX是默认不更新的,你要想获得其真实值,就必须手动调用computeXXX方法或设置XXXneedsUpdate属性。threejs这么做,是出于性能考虑

给立方体画上虚线边框

思路,遍历立方体的所有顶点,对每一个顶点,画上与其相接的三条边,每条边画一半长度。


function drawUnVisiableBorder(){for(var i=0 ; i< cube.geometry.vertices.length ; i ++ ){var v1 = new THREE.Vector3();v1.copy(cube.geometry.vertices[i]);var v2 = new THREE.Vector3();v2.copy(v1);v2.x = -v2.x/2;DrawDashLine(v1,v2);var v2 = new THREE.Vector3();v2.copy(v1);v2.y = -v2.y/2;DrawDashLine(v1,v2);var v2 = new THREE.Vector3();v2.copy(v1);v2.z = -v2.z/2;DrawDashLine(v1,v2);}}

注意:画虚线只能画一次,如果画两次,第一次从A到B,第二次从B到A,虚线就会变成实线,因为两次画的线段和空缺刚好互补

另外,新的threejs提供了EdgesGeometry,也可以用来描边

线的粗细

在Threejs中,线本身是二维的,没有办法设置粗细,这样要选择、操作一条线就不太好操作。这时,可以在线外面包一层不可见的圆柱,这样线就很好操作了。

function drawCylinder(topRadius,bottomRadius ,height){//四个参数分别是上底面半径、下底面半径,高度,半径段数var geometry = new THREE.CylinderGeometry(topRadius,bottomRadius,height,16);var material = new THREE.MeshBasicMaterial({visible:true,color:0x879378});var cylinder = new THREE.Mesh(geometry,material);return cylinder;}

线的颜色

纯色的线

function colorLine(){var geometry = new THREE.Geometry();/*** vertexColors: false,表示使用color来设置颜色* @type {THREE.LineBasicMaterial}*/var material = new THREE.LineBasicMaterial({ vertexColors: false,color:#cccccc });var p1 = new THREE.Vector3(0, 0,0);var p2 = new THREE.Vector3(500,300,0);geometry.vertices.push(p1);geometry.vertices.push(p2);var line = new THREE.Line(geometry,material,THREE.LineSegments );scene.add(line);}

渐变线:使用端点的颜色,从端点A的颜色变化到端点B的颜色

function initObject(){var geometry = new THREE.Geometry();/*** vertexColors: true 表示使用端点颜色* @type {THREE.LineBasicMaterial}*/var material = new THREE.LineBasicMaterial({ vertexColors: true });var p1 = new THREE.Vector3(0, 0,0);var p2 = new THREE.Vector3(500,300,0);geometry.vertices.push(p1);geometry.vertices.push(p2);/*** 将端点颜色加到geometry.colors中,这是一个数组,和端点数组一一对应*/var color1 = new THREE.Color(0x3966FF);var color2 = new THREE.Color(0x629729 );geometry.colors.push(color1,color2);var line = new THREE.Line(geometry,material,THREE.LineSegments );scene.add(line);}

打包对象

使用Group对象(实质就是一个Object3D对象):

    var group = new THREE.Group();function initObject(){var obj1 = ...;var obj2 = ...;group.add(obj1);group.add(obj2);scene.add(group);}

打包后对象可以一起操作。打包过后,对象就有里层对象和外层对象,如上面例子中group是外层对象,obj1obj2是里层对象。里层对象的参考系是外层对象,即里层对象的位置是相对于外层对象而言的。

深度遍历所有子对象

可以使用traverse,相比于直接使用Object3D.childrentraverse会递归遍历所有子孙

group.traverse(function (e) {if (e instanceof THREE.Mesh && e != plane) {e.rotation.x += 0.01;e.rotation.y += 0.01;e.rotation.z += 0.01;}});

设置面的可见性

在threejs里,物体都有两个面,里面和外面,并且两个面可以独立设置可见性


var meshMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff});
//下面三个语句只需要一个即可。
meshMaterial.side = THREE.FrontSide; //只显示外面
meshMaterial.side = THREE.BackSide; //只显示里面
meshMaterial.side = THREE.DoubleSide;//两面都显示

改变物体的形状和材质

为了提高性能,threejs默认物体的形状和材质一经创建,就永不改变。所以,修改物体的形状和材质,就需要手动告诉threejs,方法就是使用各种computeXXX方法和needsUpdate属性。

对于形状

各种computeXXX方法。

对于材质

  • needsUpdate属性

    只有需要体现在webGL上的改变才需要将该属性设置为true,一些简单属性如color,就不需要设置该属性。

function render(){renderer.render(scene,camera);cube.material.opacity -= 0.3;//表示透明度小于0.4时,不显示cube.material.alphaTest = 0.4;//有这句时,立方体不显示,没有时,显示cube.material.needsUpdate = true;requestAnimationFrame(render,1000);}

设置透明度

设置透明度需要设置材质的transparent:true

var material = new THREE.MeshBasicMaterial({transparent:true});
material.opacity = 0.5;

滚轮缩放

可以参考下面的实现

            function onDocumentMouseWheel(event){if(event.wheelDelta>0){      //判断滚轮滚动方向cube.scale.multiplyScalar(1.02);}else{cube.scale.multiplyScalar(0.98);}render();}

深度测试

深度测试可以实现:远的看着模糊,近的看着清楚,被遮住的看不见等效果

例如一个立方体,怎么设置为看得见得边为实线,看不见的边为虚线?

可以给立方体每条边都画上实线和虚线,实线的depthTest设为true,虚线的depthTest设为false

            /*** 画实线* */function DrawLine(v1,v2){var geometry = new THREE.Geometry();//设置depthTest属性为true,表示被挡住的边就看不见var lineMaterial = new THREE.LineBasicMaterial({ vertexColors: false,depthTest:true,color:0x894834});var p1 = new THREE.Vector3();p1.copy(v1);var p2 = new THREE.Vector3();p2.copy(v2);geometry.vertices.push(p1);geometry.vertices.push(p2);var line = new THREE.Line(geometry,lineMaterial,THREE.LineSegments );return line;}/*** 画虚线* */function DrawDashLine(v1,v2){var geometry = new THREE.Geometry();//设置depthTest属性为false,表示被挡住的边也看得见var lineMaterial = new THREE.LineDashedMaterial({ color:0x010101 ,dashSize:5,gapSize:10,depthTest:false });var p1 = new THREE.Vector3();p1.copy(v1);var p2 = new THREE.Vector3();p2.copy(v2);geometry.vertices.push(p1);geometry.vertices.push(p2);geometry.computeLineDistances();var line = new THREE.Line(geometry,lineMaterial,THREE.LineSegments);cube_Border_Group.add(line);}

z-fighting和多边形偏移

深度测试的例子,你会发现有几条边即可以看见实线,也可以看见虚线,两种重叠到一起,若隐若现。这种问题就是z-fighting导致的。因为实线和虚线靠得太近,它们的深度太接近,导致threejs无法正确的区分出谁前谁后,就会出现闪烁问题。这时可以给正方形设置polygonOffset属性,这个属性的作用是:当两个图形在同一个像素上的深度相同时(也就是深度发生了冲突),将设置了polygonOffset属性的图形的深度值加上一小段偏移,从而避免冲突。

            /*** 画立方体* */function drawCube(){var geometry = new THREE.BoxGeometry( 100, 100,100);//下面设置了polygonOffset:true,polygonOffsetFactor:0.5var material = new THREE.MeshBasicMaterial({color:0xCCCCCC,transparent:false,polygonOffset:true,polygonOffsetFactor:0.5});.....}

关于z-fighting,请看:threejs- z-fighting 问题

创建2D文字

             /***使用示例:创建XYZ* */function createXYZText(){//创建Zvar spritey = makeTextSprite( "Z", { fontsize: 100} );spritey.position.set(0,10,200);scene.add(spritey);//创建Xspritey = makeTextSprite( "X", { fontsize: 100 } );spritey.position.set(230,30,0);scene.add(spritey);//创建Yspritey = makeTextSprite( "Y", { fontsize: 100} );spritey.position.set(0,200,0);scene.add(spritey);}/*** 创建永远面向相机的2D文字* */function makeTextSprite( message, parameters ){if ( parameters === undefined ) parameters = {};var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial";var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 18;var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 4;var textColor = parameters.hasOwnProperty("textColor") ?parameters["textColor"] : { r:0, g:0, b:0, a:1.0 };var canvas = document.createElement('canvas');var context = canvas.getContext('2d');context.font = "Bold " + fontsize + "px " + fontface;var metrics = context.measureText( message );var textWidth = metrics.width;context.lineWidth = borderThickness;context.fillStyle = "rgba("+textColor.r+", "+textColor.g+", "+textColor.b+", 1.0)";context.fillText( message, borderThickness, fontsize + borderThickness);var texture = new THREE.Texture(canvas)texture.needsUpdate = true;var spriteMaterial = new THREE.SpriteMaterial( { map: texture, useScreenCoordinates: false } );var sprite = new THREE.Sprite( spriteMaterial );sprite.scale.set(0.5 * fontsize, 0.25 * fontsize, 0.75 * fontsize);return sprite;}

参考three-js-2d-text-sprite-labels

创建自定义形状

定义出点数组、面数组就可以了

var vertices = [new THREE.Vector3(1, 3, 1),new THREE.Vector3(1, 3, -1),new THREE.Vector3(1, -1, 1),new THREE.Vector3(1, -1, -1),new THREE.Vector3(-1, 3, -1),new THREE.Vector3(-1, 3, 1),new THREE.Vector3(-1, -1, -1),new THREE.Vector3(-1, -1, 1)];var faces = [new THREE.Face3(0, 2, 1),new THREE.Face3(2, 3, 1),new THREE.Face3(4, 6, 5),new THREE.Face3(6, 7, 5),new THREE.Face3(4, 5, 1),new THREE.Face3(5, 0, 1),new THREE.Face3(7, 6, 2),new THREE.Face3(6, 3, 2),new THREE.Face3(5, 7, 0),new THREE.Face3(7, 2, 0),new THREE.Face3(1, 3, 4),new THREE.Face3(3, 6, 4),];var geom = new THREE.Geometry();geom.vertices = vertices;geom.faces = faces;geom.computeFaceNormals();   //注意要加这个

创建多材质物体

                var materials = [new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff, shading: THREE.FlatShading}),new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})];var mesh = THREE.SceneUtils.createMultiMaterialObject(geometry, materials);

添加控制面板

当控制面板上的“position”文件夹下的”positionX”改变时,触发事件

var gui = new dat.GUI();
guiPosition = gui.addFolder('position');
var contX = guiPosition.add(controls, 'positionX', -10, 10);contX.listen();
contX.onChange(function (value) {cube.position.x = controls.positionX;
});

需要引入:

<script type="text/javascript" src="../libs/dat.gui.js"></script>

scene、camera和renderer

消除锯齿

创建renderer的时候加上antialias参数

renderer = new THREE.WebGLRenderer({antialias:true});

设置屏幕背景颜色

renderer.setClearColor(0x348934,0.7);

第一个参数是颜色,第二个参数是透明度。屏幕背景默认是黑色的,所以透明度的值越小,屏幕越暗。

添加网格参考

  scene.add( new THREE.GridHelper( 500, 25 ) );

添加上面语句之后,屏幕中心会显示一个边长为500的水平面,水平面规格为25*25,threejs有很多helper,可以查阅官方文档

事件和坐标

获取鼠标点击位置在threejs中的坐标

也就是将屏幕坐标转成threejs空间坐标。

function transform(x,y){let mouse = new THREE.Vector2();mouse.x = x-window.innerWidth/2;mouse.y = window.innerHeight/2 - y;console.log(x+"->"+mouse.x);console.log(y+"->"+mouse.y);
}

原理请看【对象拾取】

对象拾取

可以使用Raycaster对象获得鼠标点击处的对象数组。这个对象可以从某个位置(一般是相机)发射一条射线,穿过鼠标点击的位置,返回所有相交的对象。

/**首先,要给document注册事件*/function initRenerer(){renderer = new THREE.WebGLRenderer({antialias : true});renderer.setSize(window.innerWidth,window.innerHeight);document.body.appendChild(renderer.domElement);renderer.setClearColor(0xFFFFFF,1.0);document.addEventListener("mousedown",onMouseDown,false);}/**使用射线投影获得点击的形状,再对形状进行处理*/var mouse = new THREE.Vector3();var raycaster =  new THREE.Raycaster();function onMouseDown(event){event.preventDefault();//将屏幕像素坐标转化成camare坐标mouse.x = (event.clientX/renderer.domElement.clientWidth)*2-1;mouse.y = - (event.clientY/renderer.domElement.clientHeight)*2+1;//设置射线的起点是相机raycaster.setFromCamera( mouse, camera );//将射线投影到屏幕,如果scene.children里的某个或多个形状相交,则返回这些形状//第二个参数是设置是否递归,默认是false,也就是不递归。当scene里面添加了Group对象的实例时,就需要设置这个参数为true//第一个参数不传scene.children也可以,传一个group.children或一个形状数组都可以(这样可以实现一些特别的效果如点击内部的效果)//另外,因为返回的是一个数组,所以遍历数组就可以获得所有相交的对象,当元素重叠时,特别有用var intersects = raycaster.intersectObjects(scene.children,true);  if(intersects.length>0){var currObj = intersects[0].object;console.log(currObj);currObj.material.color = new THREE.Color(0x493048);currObj.position.x += 50;}render();}

注意:如果intersectObjectsobjects : Array, recursive : Boolean)方法不带 recursive 参数只能检测 Mesh 类型的数组,所以,如果数组里面有Group,请带上recursive 参数。这是在拾取加载进来的对象时非常容易犯的一个错误。

旋转参考系(世界坐标旋转)

/*** 旋转参考系* @param object 需要旋转的对象* @param axis  旋转轴,是一个向量,new Vetor3(1,0,0)表示绕x轴顺时针旋转,Vetor3(1,0,0)表示绕X轴逆时针旋转* @param radians 旋转的角度*/function rotateAroundWorldAxis(object, axis, radians) {rotWorldMatrix = new THREE.Matrix4();rotWorldMatrix.makeRotationAxis(axis.normalize(), radians);// old code for Three.JS pre r54://  rotWorldMatrix.multiply(object.matrix);// new code for Three.JS r55+:rotWorldMatrix.multiply(object.matrix);                // pre-multiplyobject.matrix = rotWorldMatrix;// old code for Three.js pre r49:// object.rotation.getRotationFromMatrix(object.matrix, object.scale);// old code for Three.js pre r59:// object.rotation.setEulerFromRotationMatrix(object.matrix);// code for r59+:object.rotation.setFromRotationMatrix(object.matrix);}

物体动的效果可以是物体本身在动,比如,风车的转动;也可以是世界在动,比如,仰望星空,斗转星移。

旋转参考系和旋转物体本身是不一样的:旋转参考系就好像通过东南西北找路,东西南北永远不变;旋转物体就本身就好像通过左手右手找路,随着物体的改变,左右也变了。下面这个例子是朝右转的:


function render(){renderer.render(scene,camera);cube.rotation.x = Math.PIcube.rotation.y += Math.PI*5/ 180;requestAnimationFrame(render,1000);}

将鼠标移动转换成转动

首先要算出鼠标移动的速度:

function onMouseMove(event){var xDisp = event.clientX - prevX;   var yDisp = event.clientY - prevY;//do somethingprevX = event.clientX;prevY = event.clientY;render();}

onMouseMove不停的触发,相当于单位时间内触发一次,所以,单位时间内的位移即是速度。

求出了鼠标的移动的速度,就可以乘以一定的系数转化成形状的转动,具体的系数可以尝试得出。

注意:这样求出的速度并不精确,因为mousemove事件触发的时间间隔并不是恒定的,而是跟机器的实时性能有关,如果要精确速度,请看: 计算鼠标移动的精确速度

参考

  • threejs教程
  • 里面挺多例子
  • 要学理论可以参看:Three.js入门指南

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/430062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【poj3070】Fibonacci

矩乘快速幂入门 题目已经把题解讲得很清楚了 1 #include<cstdio>2 #include<cstring>3 #include<cassert>4 #include<algorithm>5 using namespace std;6 #define p 100007 #define N 108 #define ll long long9 struct matrix{ 10 int n,m; 11 …

调用线程必须为sta_Java手写分布式系统远程调用RPC框架

一、RPC简介最近看hadoop底层通信&#xff0c;都是通过RPC实现的。RPC(Remote Procedure Call Protocol)远程调用&#xff1a; 远程过程调用是一种常用的分布式网络通信协议,它允许运行于 一台计算机的程序调用另一台计算机的子程序&#xff0c;同时将网络的通信细节隐藏起来&a…

ThreeJS阴影

在threejs中&#xff0c;阴影可以定义为“在渲染器支持的情况下&#xff0c;有一个可以产生阴影的光&#xff0c;照射在若干个可以产生阴影的物体上&#xff0c;并在某个可以接受阴影的物体上产生阴影”。所以&#xff0c;要产生阴影&#xff0c;渲染器、光、物体&#xff0c;接…

python2发送http不编码_[转]Python 2.x中常见字符编码和解码方面的错误及其解决办法...

Python 2.x中的字符编码&#xff0c;设计的的确不好&#xff0c;导致初学者&#xff0c;甚至是即使用Python很长时间的人&#xff0c;都会经常遇到字符编解码方面的错误。下面就把一些常见情&#xff0c;尽量的都整理出来&#xff0c;并给出相应的解决办法。看此文之前Python中…

shell总结(0基础入门)

一、简介 shell是用户和操作系统交互的命令行解释器。 shell有很多种&#xff1a; bash、csh、sh、ksh、、、 我们等了linux时看到的命令行就是一个bash。 二、第一个脚本&#xff1a; [rootlinux1 script]# vim first.sh #!/bin/bash#auther:xiaofan#time:2016.10.4#井号是注…

使用webpack打包ES6代码

安装nodejs 根目录下运行 npm init根据提示输入项目相关信息&#xff0c;然后运行&#xff1a; npm install 安装webpack 在项目根目录下运行 npm install webpack --save-dev 安装babel loader 和 转码规则 在根目运行命令&#xff1a; npm install babel-loader --sa…

c语言代码可以python运行吗_c语言如何运行python脚本

目前的C/C与python的混合编程分为两大类&#xff1a;1、使用python调用c/c编写的扩展库&#xff0c;扩展python的功能&#xff0c;我们暂且称为python调用c/c2、使用c/c程序调用执行python中定义的函数等&#xff0c;我们暂且称为c/c调用python本文只针对c/c调用执行python作一…

十万个为什么 —— 为什么一个数的 0 次方只能是 1

1. 幂的排列 212,224,238 ⇒ 20?,2−112 2012. 指数乘法运算 对于所有的实数 x,y&#xff0c;ax⋅ayaxy 因此&#xff1a; 当 y0 时&#xff0c;ax⋅a0ax ⇒ a01当 y−x 时&#xff0c;ax⋅a−xa0 ⇒ a01转载于:https://www.cnblogs.com/mtcnn/p/9423448.html

Threejs性能监视和可视化控制

概述 性能监视是监视threejs运行中的刷新频率&#xff0c;要使用到threejs提供的stat.js&#xff0c;可视化控制是在窗口中显示一个可见的控制框&#xff0c;可以通过鼠标改变其中的值&#xff0c;达到改变threejs对象参数的目的&#xff0c;需要使用到dat.gui.js 性能监视 …

python autoit上传文件_结合python+selenium使用AutoIt V3实现文件、图片上传

1.下载、安装AutoIt V3下载官网&#xff1a;https://www.autoitscript.com/site/autoit/downloads/2.AutoIt V3组件介绍AutoIt Windows Info 或者Au3Info_x64.exe 用于帮助我们识别Windows控件信息。Compile Script to.exe 或者Aut2exe_x64.exe 用于将AutoIt生成 exe …

百度贴吧的数据抓取和分析(一):指定条目帖子信息抓取

这个教程使用BeautifulSoup库爬取指定贴吧的帖子信息。 本教程的代码托管于github: https://github.com/w392807287/spider_baidu_bar 数据分析部分请移步&#xff1a; python版本&#xff1a;3.5.2 使用BeautifulSoup库获取网页信息 引入相关库&#xff1a; from bs4 import …

在ES6类中绑定事件

概述 ES6提供了类&#xff0c;给模块化带来了很大的帮助。在类里面绑定事件&#xff0c;一来是为了使得代码结构清晰&#xff0c;二来是为了可以使用类的变量和方法。但是&#xff0c;由于事件的回调函数并不是由类的实例对象触发&#xff0c;所以&#xff0c;事件回调函数里面…

DevExpress.Utils.ToolTipLocation

private void textBox1_TextChanged(object sender, EventArgs e){if ((sender as TextBox).Text.Length 0){DevExpress.Utils.ToolTipLocation tipLocation new DevExpress.Utils.ToolTipLocation();toolTipController1.ShowHint("不能为空", textBox1, ToolTipLo…

github基本使用教程

2017-10-22 更新 目前已经有一个很好的Github教程—— Git it&#xff0c;可以花半个小时比较全面的了解github的使用&#xff0c;直接在relaese里下载一个最新版&#xff0c;双击运行就可以看到教程了。下面的教程也还可使用。 如果不熟悉 git命令,可以使用 learngitbranchi…

python while九九乘法表儿歌_python使用while循环实现九九乘法表

protocol http not supported or disabled in libcurl apt-getubuntu 14.04 碰到了这个莫名其妙的问题.谷歌了一把,解决方案如下:http://askubuntu.com/questions/683857/curl-1-protocol-https-n ...linux基础-基本命令的讲解&lpar;1-7单元)基本命令的讲解 主要内容介绍 1…

EF+postgresql中的一些问题

需要基于MVC的WebAPI搭建一个服务接口&#xff0c;数据存储在postgresql数据库&#xff0c;在使用的过程中遇到了很多问题。做一下记录&#xff1a; &#xff08;1&#xff09; System.IO.FileLoadException: 未能加载文件或程序集“Npgsql, Version3.1.2.0, Cultureneutral, P…

Navicat Error 2003 - can't connect to mysql server 'localhost' (10061)

项详细出错对象&#xff1a;Navicat for mysql出错信息&#xff1a;2003 - can’t connect to mysql server ‘localhost’ (10061)出错原因&#xff1a;mysql 服务器没有启动解决办法&#xff1a;去mysql的安装目录下找到“\bin\mysqld.exe”&#xff0c;双击运行&#xff01;…

▲数据结构 笛卡尔树【2011】五2 C++版

转载于:https://www.cnblogs.com/qilinart/articles/5940726.html

运行时异常与一般异常有何异同_Java修行第015天,异常机制和常用类

1. 异常概念_分类1) 什么叫异常&#xff1f;答:异常(Expection)就是在程序运行过程中所发生的不正常的事件,它会中断正在运行的程序2) 请简述异常的分类&#xff1f;答:异常分为Error(仅靠程序本身无法恢复的严重错误)和由Java应用程序 抛出和处理的非严重错误.非严重错误又分为…

js 自定义事件

自定义事件很难派上用场&#xff1f; 为什么自定义事件很难派上用场&#xff0c;因为以前js不是模块化开发&#xff0c;也很少协作。因为事件本质是一种通信方式&#xff0c;是一种消息&#xff0c;只有存在多个对象&#xff0c;多个模块的情况下&#xff0c;才有可能需要用到…