全网疯传的前端量子纠缠效果,源码来了!

昨天,很多群里都在疯传一个视频,视频演示了纯前端实现的“量子纠缠”效果 , 不少前端er表示:“前端白学了” 。

全网疯传的前端量子纠缠效果,源码来了!

文章插图
视频作者昨晚开源一个简化版的实现源码(截止发文,该项目在 Github 上已获得超过 1k Star) , 本文就来看看他是怎么实现的!
简化版根据作者的描述,该项目是使用 three.js 和 localStorage 实现的在同一源上设置跨窗口的 3D 场景 。
 
全网疯传的前端量子纠缠效果,源码来了!

文章插图
虽然没有原视频那么炫酷,但基本原理应该差不多 。
源码包含多个文件 , 最主要的文件如下:
  • index.html
  • mAIn.js:主文件
  • WindowManager.js:窗口管理
 
在线体验:https://bgstaal.github.io/multipleWindow3dScene/
源码index.html 文件中引入了 three.js 的压缩包,以及main.js:
<!DOCTYPE html><html lang="en"><head><title>3d example using three.js and multiple windows</title><script type="text/JAVAscript" src=https://www.isolves.com/it/cxkf/ky/ym/2023-11-24/"./three.r124.min.js">这没啥可说的,下面就来看看 main.js 中都写了点啥 。代码如下:
import WindowManager from './WindowManager.js'const t = THREE;let camera, scene, renderer, world;let near, far;let pixR = window.devicePixelRatio ? window.devicePixelRatio : 1;let cubes = [];let sceneOffsetTarget = {x: 0, y: 0};let sceneOffset = {x: 0, y: 0};let today = new Date();today.setHours(0);today.setMinutes(0);today.setSeconds(0);today.setMilliseconds(0);today = today.getTime();let internalTime = getTime();let windowManager;let initialized = false;// // 获取从一天开始以来的秒数(以便所有窗口使用相同的时间)function getTime () { return (new Date().getTime() - today) / 1000.0;}if (new URLSearchParams(window.location.search).get("clear")) { localStorage.clear();}else {// 在某些浏览器中避免在实际点击URL之前预加载页面内容 document.addEventListener("visibilitychange", () => {if (document.visibilityState != 'hidden' && !initialized) {init();} });// 确保在窗口完全加载后,只有在页面可见时才执行初始化逻辑 window.onload = () => {if (document.visibilityState != 'hidden') {init();} };// 初始化操作 function init () {initialized = true;// 短时间内window.offsetX属性返回的值可能不准确,需要添加一个短暂的延迟,等待一段时间后再执行相关操作 。setTimeout(() => {setupScene();setupWindowManager();resize();updateWindowShape(false);render();window.addEventListener('resize', resize);}, 500)}// 设置场景相关的配置 function setupScene () {camera = new t.OrthographicCamera(0, 0, window.innerWidth, window.innerHeight, -10000, 10000);camera.position.z = 2.5;near = camera.position.z - .5;far = camera.position.z + 0.5;scene = new t.Scene();scene.background = new t.Color(0.0);scene.add( camera );renderer = new t.WebGLRenderer({antialias: true, depthBuffer: true});renderer.setPixelRatio(pixR);world = new t.Object3D();scene.add(world);renderer.domElement.setAttribute("id", "scene");document.body.AppendChild( renderer.domElement ); }// 设置窗口管理器的相关配置 function setupWindowManager () {windowManager = new WindowManager();windowManager.setWinShapeChangeCallback(updateWindowShape);windowManager.setWinChangeCallback(windowsUpdated);let metaData = https://www.isolves.com/it/cxkf/ky/ym/2023-11-24/{foo: "bar"};// 初始化窗口管理器(windowmanager)并将当前窗口添加到窗口池中 。windowManager.init(metaData);windowsUpdated(); } function windowsUpdated () {updateNumberOfCubes(); } function updateNumberOfCubes () {let wins = windowManager.getWindows();cubes.forEach((c) => {world.remove(c);})cubes = [];for (let i = 0; i < wins.length; i++) {let win = wins[i];let c = new t.Color();c.setHSL(i * .1, 1.0, .5);let s = 100 + i * 50;let cube = new t.Mesh(new t.BoxGeometry(s, s, s), new t.MeshBasicMaterial({color: c , wireframe: true}));cube.position.x = win.shape.x + (win.shape.w * .5);cube.position.y = win.shape.y + (win.shape.h * .5);world.add(cube);cubes.push(cube);} } function updateWindowShape (easing = true) {sceneOffsetTarget = {x: -window.screenX, y: -window.screenY};if (!easing) sceneOffset = sceneOffsetTarget; } function render () {let t = getTime();windowManager.update();// 根据当前位置和新位置之间的偏移量以及一个平滑系数来计算出窗口的新位置let falloff = .05;sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff);sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff);world.position.x = sceneOffset.x;world.position.y = sceneOffset.y;let wins = windowManager.getWindows();// 遍历立方体对象,并根据当前窗口位置的变化更新它们的位置 。for (let i = 0; i


推荐阅读