量子纠缠 "前端代码已经开源,你可以体验一下!
“阅读本文大概需要4分钟。
前言
你好,我是测试蔡坨坨。
这两天被一段纯前端代码实现的炫酷网页版量子纠缠
效果刷屏了。
原视频如下:
其作者是国外的一名程序员,也是艺术家,一经发布就一夜爆火。
作者的推文:
从视频中可以看出,当我们打开两个窗口时,两个量子之间竟然还存在相互纠缠,简直把前端代码发挥到了极致,如此奇妙的效果到底是如何实现的呢?
作者为给粉丝一个答复,开源了一个简易版本的实现效果,虽然没有原视频那么炫酷,但是基本原理应该差不多,话不多说,我们一起来看下它到底是如何实现的。
下载项目
GitHub仓库地址:https://github.com/bgstaal/multipleWindow3dScene
从项目简介中得知作者是使用three.js
和localStorage
实现在同一源上跨窗口设置3D场景。
我们将项目克隆至本地:
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下载吧,拜了个拜~
以上,完。
脚踏实地,仰望星空,和坨坨一起学习软件测试,升职加薪!
上一篇: 微服务链接跟踪 - SkyWalking
下一篇: 蜘蛛侠:平行宇宙》的视觉分析和滤镜实现
推荐阅读
-
趣谈留言队列,搞清楚留言队列到底是什么!-说到消息队列,洪觉大概能猜到人们听到消息队列的反应,大致可以分为以下几类人。 第一类人,懵懵懂懂,刚上大学接触编程,还没用过消息队列,甚至还以为消息队列就是代码里面要新建一个List之类的;第二类人,听过消息队列,了解消息队列,但具体是什么还不是太明白,只知道一说到消息队列,脑海里马上出现了三组词,削峰、异步、解耦;第三类人,用过消息队列,对它有一定了解,但不知道为什么要这样设计,消息队列有什么样的前世今生,是如何演化到现在的模式的?**第四类人,已经对消息队列有了足够的了解,可以阅读本帖作为复习和温习。**你属于哪一类?无论你对消息队列了解多少,读完这篇文章后,我相信你都会有所收获。 什么是消息队列?我们为什么要使用消息队列?真的只是因为它看起来很勉强、很常用吗?当然不是,一项技术的出现往往是为了解决某种痛点,我们就从这个痛点出发,看看消息队列到底是为了解决什么问题而诞生的。 相信大家在工作之前,或者工作中接触单片机的次数会多一点,不管什么业务都一股脑塞进一个系统里,这种情况下接触消息队列的场景会比较少。但随着业务的增长,量上去了,单机系统就很难维护了,也扛不住并发量的增长,就需要把原来的单体应用拆分成多个服务。例如,牛奇网采用分布式架构,将原来的单体系统拆分成用户服务、题库服务、求职服务、论坛服务等,每个分布式节点都有一个集群,保证高可用性。 那虽然在这样的微服务架构下,如果某个核心业务并发量过大,系统就扛不住了。比如淘宝、淘票票、拼多多、京东等电商场景中的支付场景,你在某宝下单并支付后,调用支付服务,完成支付后,还需要更新订单的状态,这个时候就需要调用订单服务,那我们平时也下单,除了简单完成这些操作外,还会给你相应的积分;商家也会收到订单消息,并给您发送旺旺消息,确认订单无误;同时,也会给您发送消息,确认订单无误。确认订单无误;同时您还可以查看您的物流状态;还有系统为了给您推荐更适合您的商品,会根据您的订单做类似的推荐等等,我说的这些都是当我们下单后,肉眼可以感知到系统所做的动作。 **一个支付动作如果还需要调用那么多服务,等他们响应成功,最后再告诉用户你支付成功了,用户在系统中的整个体验会非常糟糕。**设想一下,假设请求服务+处理请求+响应总共需要 50ms,我们上面列出的场景:支付服务、订单服务、积分服务、商家服务、物流服务、推荐服务,总共需要 300ms。
-
量子纠缠 "前端代码已经开源,你可以体验一下!