“阅读本文大概需要4分钟。
前言
你好,我是测试蔡坨坨。
这两天被一段纯前端代码实现的炫酷网页版量子纠缠
效果刷屏了。
原视频如下:
其作者是国外的一名程序员,也是艺术家,一经发布就一夜爆火。
作者的推文:
从视频中可以看出,当我们打开两个窗口时,两个量子之间竟然还存在相互纠缠,简直把前端代码发挥到了极致,如此奇妙的效果到底是如何实现的呢?
作者为给粉丝一个答复,开源了一个简易版本的实现效果,虽然没有原视频那么炫酷,但是基本原理应该差不多,话不多说,我们一起来看下它到底是如何实现的。
下载项目
GitHub仓库地址:https://github.com/bgstaal/multipleWindow3dScene
从项目简介中得知作者是使用three.js
和localStorage
实现在同一源上跨窗口设置3D场景。
我们将项目克隆至本地:
代码语言:javascript复制git clone https://github.com/bgstaal/multipleWindow3dScene.git
首先看项目的目录结构,可以发现它是纯js实现的,主要文件包括:
- index.html
- main.js
- WindowManager.js
启动项目
打开index.html入口文件,可以发现这里直接在根目录下给项目开个端口就能启用。
那么如何开端口呢?
推荐使用Live Server
插件,在扩展商店中搜索Live Server并点击安装:
安装完成后,右下角就会出现一个Go live
的按钮,点击按钮,就能给项目开启一个5500的端口:
在浏览器中访问项目127.0.0.1:5500,同时打开两个窗口,可以看到两个窗口都能感知到对方的存在:
这到底是如何实现的呢?接着我们就打开代码来一探究竟。
源码解析
index.html
首先在index.html文件中引入了一个压缩版的three.js,这个库就是用来生成网页3D模型的,也就是项目中生成旋转立方体需要用到的库。接着就是引入main.js文件,可以看出项目的主要逻辑就是在这个文件里面。
main.js
定义变量
打开main.js文件,首先是定义了一些变量,比如:把THREE库赋值给t变量;一些存放3D场景的变量;当前时间的变量,后续每个立方体旋转相同的角度也是通过这个时间来同步的:
init()项目初始化
在网页加载成功后触发onload方法,判断当前dom是否可见,可见则执行init()初始化方法:
初始化场景和渲染器
init()方法中首先是通过setupScene()函数创建场景和渲染器:
- 先设置了一个正交相机camera,并定义相机的位置
- 再创建场景scene对象,把相机添加到场景对象中
- 之后就是创建renderer渲染对象和world对象,并将渲染器的dom元素添加到页面中
初始化窗口管理器
在setupWindowManager()函数中创建了一个窗口管理器实例,并初始化了窗口并添加到窗口池中:
窗口管理器就是一个名为WindowManager的类,用于管理窗口的创建、更新和删除等操作,也是最核心的实现。内部先用#
号开头定义了一些私有变量:
#windows; // 存储所有窗口的数组
#count; // 当前窗口的数量
#id; // 当前窗口的唯一标识
#winData; // 当前窗口的数据,包括形状、自定义数据等
#winShapeChangeCallback; // 当前窗口形状发生变化时的回调函数
#winChangeCallback; // 当前窗口列表发生变化时的回调函数
再通过addEventListener()监听storage本地存储数据是否发生变化,改变就触发回调函数winChangeCallback(),刷新渲染立方体的位置:
这也是多窗口为何能感知其他窗口发生变化的原因,其实就是每添加一个新窗口
,它就会计算窗口内生成的立方体的位置信息
并添加到本地存储
里,其他窗口监听到storage的改变就会刷新渲染立方体的位置:
我们再来看看localStorage里面到底存了哪些数据:
- 每个窗口唯一的id标识
- 立方体的位置和大小,包括距离屏幕左上角的x轴(window.screenLeft)、y轴位置(window.screenTop),浏览器窗口的w宽(window.innerWidth)、h高(window.innerHeight)
然后就是通过beforeunload方法监听窗口是否关闭,关闭就删除本地存储里面那个对应立方体的数据:
更新立方体位置和数量
updateNumberOfCubes()更新当前页面立方体的数量和位置,首先通过窗口管理器的getWindows()方法获取到所有立方体的数组,接着遍历这个数组,然后动态创建立方体并根据窗口位置更新其在场景中的位置:
调整窗口大小
通过resize()方法调整渲染窗口的大小,获取当前窗口的innerWidth和innerHeight,再使用window.addEventListener('resize', resize)来动态监听窗口大小的改变,在窗口大小发生改变时重新设置相机的宽高比和渲染器的大小,以适应新窗口的尺寸:
循环渲染
render()函数实现渲染:
render()方法通过获取当前时间,再计算出每个立方体每一帧的动画,并渲染到页面上:
这里还使用到了浏览器的requestAnimationFrame()方法,这个方法的作用就是在render()方法在下一次浏览器重绘之前执行,通常是每秒执行60次,以匹配大多数显示器的刷新率,起到了优化动画性能的作用:
以上就是量子纠缠源码的实现原理,想要了解更多源码内容,自己去GitHub下载吧,拜了个拜~
以上,完。
脚踏实地,仰望星空,和坨坨一起学习软件测试,升职加薪!