前端“量子纠缠”:multipleWindow3dScene 来了

news2025/1/20 21:50:12

最近前端实现的量子纠缠在网络上火了起来,作者bgstaal的推文:09bf3aa0ed5a32f3209186ca11c948b2.png效果如下:

622bf0c2aec4332be1852b4dd6276c62.gif
量子纠缠

那我们一起来看下什么是量子纠缠,以及前端是如何实现的。

什么是量子纠缠?

在量子力学里,当几个粒子在彼此相互作用后,由于各个粒子所拥有的特性已综合成为整体性质,无法单独描述各个粒子的性质,只能描述整体系统的性质,则称这现象为量子缠结或量子纠缠。量子纠缠是一种奇怪的量子力学现象,处于纠缠态的两个量子不论相距多远都存在一种关联,其中一个量子状态发生改变,另一个的状态会瞬时发生相应改变。

前端如何来实现?

作者bgstaal在github上开源了一个项目,r说明如何使用 Three.js 和 localStorage 在多个窗口中设置3D场景。一起来看下代码如何实现的。

首先,从 Github 上克隆 multipleWindow3dScene 项目:

git clone https://github.com/bgstaal/multipleWindow3dScene.git

接下来,通过 vscode 中的 Live Server, 启动该项目,并在浏览器中打开项目主页 http://127.0.0.1:5500/index.html。效果如下:

04d0cda5e4f5b374f309798ebdcd9858.gif
展示效果

现在我们看下项目目录,如下图所示:3ecfe7f33306502a6340bed5605c5c1e.png

  • index.html:设置 HTML 结构的入口点。

  • main.js:使用 Three.js 初始化 3D 场景,管理窗口的调整大小事件,并根据窗口交互更新场景。

  • three.r124.min.js:用于 3D 图形渲染的 Three.js 库的压缩版本。

  • WindowManager.js:处理多个浏览器窗口的生命周期,包括创建、同步和删除。它使用 localStorage 来维护跨窗口的状态。

index.html

index.html:设置 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="three.r124.min.js"></script>
 <style type="text/css">
  *{
   margin: 0;
   padding: 0;
  }
 </style>
</head>
<body>
 <script type="module" src="main.js"></script>
</body>
</html>

下来我们先看下main.js。

main.js

定义了存放3d场景以及时间变量,在网站加载成功,页面可见时,则执行初始化init函数,设置场景,窗口管理器等相关配置。

init函数

init 函数负责设置场景、窗口管理器、调整渲染器大小以适应窗口,并开始渲染循环。

function init () {
 initialized = true;
 // add a short timeout because window.offsetX reports wrong values before a short period 
 setTimeout(() => {
  setupScene();
  setupWindowManager();
  resize();
  updateWindowShape(false);
  render();
  window.addEventListener('resize', resize);
 }, 500) 
}

这里添加了一个定时器,主要是因为window.offsetX在短时间内会返回错误的值。

setupScene函数创建了相机、场景、渲染器和3D世界对象,并将渲染器的DOM元素添加到文档体中。

// 设置场景
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);

 // 创建对象3D
 world = new t.Object3D();
 scene.add(world);

 // 设置渲染器的id
 renderer.domElement.setAttribute("id", "scene");
 // 将渲染器添加到body中
 document.body.appendChild(renderer.domElement);
}

窗口管理器的设置通过setupWindowManager函数完成,它实例化WindowManager,并定义窗口形状变化和窗口改变的回调函数。

窗口形状变化用于跟踪和反应窗口位置的移动。窗口改变的回调函数用于更新场景中的立方体数量。

// 创建一个WindowManager实例
function setupWindowManager() {
 windowManager = new WindowManager();
 // 设置窗口形状改变回调函数
 windowManager.setWinShapeChangeCallback(updateWindowShape);
 // 设置窗口改变回调函数
 windowManager.setWinChangeCallback(windowsUpdated);

 // here you can add your custom metadata to each windows instance
 // 在这里,您可以向每个窗口实例添加自定义元数据
 let metaData = { foo: "bar" };

 // this will init the windowmanager and add this window to the centralised pool of windows
 // 这将初始化窗口管理器并将此窗口添加到集中的窗口池中
 windowManager.init(metaData);
 // call update windows initially (it will later be called by the win change callback)
 // 调用updateWindows函数
 windowsUpdated();
}

// 当窗口更新时调用该函数
function windowsUpdated() {
 // 更新立方体数量
 updateNumberOfCubes();
}
// 更新立方体数量
function updateNumberOfCubes() {
 // 获取所有窗口
 let wins = windowManager.getWindows();

 // remove all cubes
 // 移除所有立方体
 cubes.forEach((c) => {
  world.remove(c);
 })

 // 重新初始化立方体数组
 cubes = [];

 // add new cubes based on the current window setup
 // 根据当前窗口设置添加新的立方体
 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);
 }
}

// 更新窗口形状函数,easing参数默认为true
function updateWindowShape(easing = true) {
 // storing the actual offset in a proxy that we update against in the render function
 // 将当前的偏移量存储在代理中,以便在渲染函数中更新
 sceneOffsetTarget = { x: -window.screenX, y: -window.screenY };
 // 如果easing参数为false,则将sceneOffset设置为sceneOffsetTarget
 if (!easing) sceneOffset = sceneOffsetTarget;
}
render函数

render函数是这段代码的核心,主要是获取当前时间,计算出每个立方体每一帧的动画,来处理窗口的变化,并渲染到页面上。还使用了浏览器的 requestAnimationFrame 方法,让render方法在下一次浏览器重绘之前执行,通常最常见的刷新率是 60hz(每秒 60 个周期/帧),以匹配大多数显示器的刷新率,起到优化动画性能的作用。

// 渲染函数,更新和渲染场景 
function render() {
 // 获取当前时间
 let t = getTime();
 // update the window manager
 // 更新窗口管理器
 windowManager.update();
 // update the scene offset based on the current window manager state
 // this will create a smooth transition between the current scene offset and the target scene offset
 // calculate the new position based on the delta between current offset and new offset times a falloff value (to create the nice smoothing effect)
 let falloff = .05;
 // 计算场景偏移量
 sceneOffset.x = sceneOffset.x + ((sceneOffsetTarget.x - sceneOffset.x) * falloff);
 sceneOffset.y = sceneOffset.y + ((sceneOffsetTarget.y - sceneOffset.y) * falloff);
 // set the world position to the offset
 //设置场景偏移量
 world.position.x = sceneOffset.x;
 world.position.y = sceneOffset.y;
 // get the window manager and the window
 // 获取所有窗口
 let wins = windowManager.getWindows();
 // loop through all our cubes and update their positions based on current window positions
 // 遍历cubes数组,更新立方体的位置
 for (let i = 0; i < cubes.length; i++) {
  // 获取cubes数组中的第i个元素
  let cube = cubes[i];
  // 获取wins数组中的第i个元素
  let win = wins[i];
  // 将t赋值给_t
  let _t = t;
  // + i * .2;
  let posTarget = { x: win.shape.x + (win.shape.w * .5), y: win.shape.y + (win.shape.h * .5) }
  // 计算cube当前位置到目标位置的距离,并乘以衰减系数
  cube.position.x = cube.position.x + (posTarget.x - cube.position.x) * falloff;
  cube.position.y = cube.position.y + (posTarget.y - cube.position.y) * falloff;
  // 计算cube的旋转角度
  cube.rotation.x = _t * .5;
  cube.rotation.y = _t * .3;
 };
 // render the scene
 renderer.render(scene, camera);
 requestAnimationFrame(render);
}
resize函数

resize函数,在浏览器窗口大小改变时,调整渲染器的尺寸以适应窗口大小,相机和渲染器也进行更新调整。

// 调整渲染器的尺寸以适应窗口大小
function resize() {
 // 获取窗口的宽度
 let width = window.innerWidth;
 // 获取窗口的高度
 let height = window.innerHeight
 // 创建一个正交相机,参数为:left,right,top,bottom,near,far
 camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000);
 // 更新相机的投影矩阵
 camera.updateProjectionMatrix();
 // 设置渲染器的尺寸
 renderer.setSize(width, height);
}

接下来看下我们看下WindowManager文件

WindowManager.js

窗口管理器WindowManager函数,主要是监听 localStorage 变化,刷新渲染立方体的位置。其中 localStorage,存储了立方体在浏览器窗口的位置,包含距离屏幕左上角x轴y轴的距离,和浏览器窗口的宽和高这些信息。

我们看下localStorage的信息,如下图所示:c709cddebcedb532dd6710f1fc23bba0.png

通过监听beforeunload事件监听窗口是否关闭,关闭则删除浏览器对应的立方体的信息。

// 当前窗口即将关闭时的事件监听器
 window.addEventListener('beforeunload', function (e) {
  // 获取窗口索引
  let index = that.getWindowIndexFromId(that.#id);
  //remove this window from the list and update local storage
  // 从列表中删除此窗口并更新本地存储
  that.#windows.splice(index, 1);
  that.updateWindowsLocalStorage();
 });

窗口管理器的init方法,根据当前窗口的位置,创建当前窗口唯一的id,创建一个立方体的位置数据,存储在localStorage,方便监听,最后执行了windowsUpdated 方法,更新立方体数量,首先通过 getWindows方法,拿到所有立方体的数据,绘制出新的立方体信息。

// initiate current window (add metadata for custom data to store with each window instance)
 // 初始化当前窗口(添加元数据以将自定义数据存储到每个窗口实例中)
 init(metaData) {
  //将本地存储中的windows数据转换为JSON格式,若未存储,则初始化为空数组
  this.#windows = JSON.parse(localStorage.getItem("windows")) || [];
  //获取本地存储中的count值,若未存储,则初始化为0
  this.#count = localStorage.getItem("count") || 0;
  this.#count++;
  this.#id = this.#count;
  //获取窗口形状
  let shape = this.getWinShape();
  //将窗口数据赋值给this.#winData
  this.#winData = { id: this.#id, shape: shape, metaData: metaData };
  //将this.#winData添加到this.#windows数组中
  this.#windows.push(this.#winData);
  //将this.#count存储到本地
  localStorage.setItem("count", this.#count);
  //更新本地存储中的windows数据
  this.updateWindowsLocalStorage();
 }

以上就是主要的核心效果代码。

参考来源

https://github.com/bgstaal/multipleWindow3dScene

https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame#browser_compatibility

https://zh.wikipedia.org/wiki/%E9%87%8F%E5%AD%90%E7%BA%8F%E7%B5%90

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

ae5856646391668944d80e780537ba60.png

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

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

相关文章

【Java】实现顺序表基本的操作(数据结构)

文章目录 前言顺序表1、打印顺序表2、增加元素3、在任意位置增加元素4、判断是否包含某个元素5、查找某个元素对于的位置6、获取任意位置的元素7、将任意位置的元素设为value8、删除第一次出现的关键字9、获取顺序表长度10、清空顺序表总结 前言 在了解顺序表之前我们要先了解…

rust高级 异步编程 二 pin

文章目录 定海神针 Pin 和 Unpin为何需要 PinUnpin深入理解 PinPin 在实践中的运用固定到堆上将固定住的 Future 变为 Unpin总结 定海神针 Pin 和 Unpin 在 Rust 中&#xff0c;所有的类型可以分为两类: 类型的值可以在内存中安全地被移动&#xff0c;例如数值、字符串、布尔…

OpenCV图像相似性比对算法

背景 在做图像处理或者计算机视觉相关的项目的时候&#xff0c;很多时候需要我们对当前获得的图像和上一次的图像做相似性比对&#xff0c;从而找出当前图像针对上一次的图像的差异性和变化点&#xff0c;这需要用到OpenCV中的一些图像相似性和差异性的比对算法&#xff0c;在O…

华为数通---配置端口安全案例

端口安全简介 端口安全&#xff08;Port Security&#xff09;通过将接口学习到的动态MAC地址转换为安全MAC地址&#xff08;包括安全动态MAC、安全静态MAC和Sticky MAC&#xff09;&#xff0c;阻止非法用户通过本接口和交换机通信&#xff0c;从而增强设备的安全性。 组网需…

二百一十三、Flume——Flume拓扑结构介绍

一、目的 最近在看尚硅谷的Flume资料&#xff0c;看到拓扑结构这一块&#xff0c;觉得蛮有意思&#xff0c;于是整理一下Flume的4种拓扑结构 二、拓扑结构 &#xff08;一&#xff09;简单串联 1、结构含义 这种模式是将多个flume顺序连接起来了&#xff0c;从最初的sourc…

一键抠图|3个智能AI抠图软件实现抠图自由!

听说你对如何利用AI抠图技术去除白色背景感兴趣&#xff1f;设想一下&#xff0c;你有一张某人站在白色背景前的照片&#xff0c;而你只希望能留下这个人物。在过去&#xff0c;你可能需要花费大量时间和精力手动进行抠图。但现在&#xff0c;AI技术来拯救你了&#xff01;AI可…

计网实验7

解决&#xff1a;路由器用rip连接&#xff0c;主机通过域名访问&#xff0c;主机之间发送电子邮件 实验步骤 1.搞好部件 2.配好两台主机的ip,掩码&#xff0c;网关 3.连接一下两台主机&#xff0c;由于两台路由器没有连接&#xff0c;所以两台主机也无法连通&#xff0c;丢包率…

搭建个人网盘应用Nextcloud

使用DNF管理软件包 1 使用winscp工具将openeuler-20.03-LTS-x86_64-dvd.iso上传至openeuler虚拟机的/root目录下&#xff0c;然后执行如下命令挂载ISO [rootopenEuler ~]# mount -o loop /root/openEuler-20.03-LTS-everything-x86_64-dvd.iso /mnt/2 添加软件源 [rootope…

智能优化算法应用:基于社交网络算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于社交网络算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于社交网络算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.社交网络算法4.实验参数设定5.算法结果6.参考…

ChatGPT,作为一种强大的自然语言处理模型,具备显著优势,能够帮助您在各个领域取得突破

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

vue2+typescript使用高德地图2.0版本

高德地图 webjs api 2.0官网教程 AMap.Driving使用说明 <div class"mmp"><div id"map" ref"mapcontainer"></div></div><script lang"ts"> //安全密钥 window._AMapSecurityConfig{securityJsCode: &qu…

【南京站-EI会议征稿中】第三届网络安全、人工智能与数字经济国际学术会议(CSAIDE 2024)

第三届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2024&#xff09; 2024 3rd International Conference on Cyber Security, Artificial Intelligence and Digital Economy 第三届网络安全、人工智能与数字经济国际学术会议&#xff08;CSAIDE 2024&…

〖大前端 - 基础入门三大核心之JS篇㊼〗- BOM基础之window对象

说明&#xff1a;该文属于 大前端全栈架构白宝书专栏&#xff0c;目前阶段免费&#xff0c;如需要项目实战或者是体系化资源&#xff0c;文末名片加V&#xff01;作者&#xff1a;不渴望力量的哈士奇(哈哥)&#xff0c;十余年工作经验, 从事过全栈研发、产品经理等工作&#xf…

SpringBoot3-集成mybatis

1、pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.…

编程过程中出现bug如何应对?

编程过程中出现bug如何应对&#xff1f; 1.找错误原因 如果完全不知道出错的原因&#xff0c;或者说存在着很多错误的有原因&#xff0c;----》控制变量法 例如&#xff0c;昨天我在使用torchrun 多卡并行一个程序的时候&#xff0c;出现了大量的bug, 于是我将报错信息放在网…

uni-app中vue3表单校验失败

目录 1.问题 2.原因及解决方式 3.表单校验方式&#xff08;vue3&#xff09; 1.问题 在app中使用uni-forms表单&#xff0c;并添加校验规则&#xff0c;问题是即使输入内容&#xff0c;表单校验依然失败。 代码&#xff1a; <template><view><uni-forms r…

0基础学java-day14

一、集合 前面我们保存多个数据使用的是数组&#xff0c;那么数组有不足的地方&#xff0c;我们分析一下 1.数组 2 集合 数据类型也可以不一样 3.集合的框架体系 Java 的集合类很多&#xff0c;主要分为两大类&#xff0c;如图 &#xff1a;[背下来] package com.hspedu.c…

2022年第十一届数学建模国际赛小美赛C题人类活动分类解题全过程文档及程序

2022年第十一届数学建模国际赛小美赛 C题 人类活动分类 原题再现&#xff1a; 人类行为理解的一个重要方面是对日常活动的识别和监控。可穿戴式活动识别系统可以改善许多关键领域的生活质量&#xff0c;如动态监测、家庭康复和跌倒检测。基于惯性传感器的活动识别系统用于通过…

10.Java程序设计-基于SSM框架的微信小程序家教信息管理系统的设计与实现

摘要是论文的开篇&#xff0c;用于简要概述研究的目的、方法、主要结果和结论。以下是一个简化的摘要示例&#xff0c;你可以根据实际情况进行修改和扩展&#xff1a; 摘要 随着社会的发展和教育需求的增长&#xff0c;家教服务作为一种个性化的学习方式受到了广泛关注。为了更…

Flink State 状态原理解析 | 京东物流技术团队

一、Flink State 概念 State 用于记录 Flink 应用在运行过程中&#xff0c;算子的中间计算结果或者元数据信息。运行中的 Flink 应用如果需要上次计算结果进行处理的&#xff0c;则需要使用状态存储中间计算结果。如 Join、窗口聚合场景。 Flink 应用运行中会保存状态信息到 …