近日见闻
- RuoYi-Vue-Plus 发布 4.8.2 正式进入维护状态,由于 springboot 2.X 与 vue 2.X 官方均宣布停止维护,故而 框架 4.X 版本 进入维护状态 (只处理问题不更新功能),停止维护时间预计: 2024 年 6-10 月具体根据使用人数动态决定,此版本已经相当稳定 即便不更新功能也不影响使用。如果依旧选择使用 jdk8 或者 jdk11 可以放心使用此版本,如果希望使用 jdk17 或者 jdk21 可以选择使用 5.X 分支。
- Spring Boot 3.2.0 现已发布,更新版本添加了大量新功能和改进。
前端量子纠缠效果实现
最近,程序员群里流行一个比较火的一个视频,尤其是很多前端程序员都不由的赞叹此视频。视频内容是一个完全由前端完成的类似"量子纠缠"效果的项目。看完之后很多人表示前端白学了。原视频如下,作者为nonfigurativ。想象一下,当你在多个显示器前操作,每个显示器就像是一个窗口,通过这些窗口你可以观察到同一个3D场景的不同部分,而这一切都实现了无缝连接。这不仅仅是技术上的创新,更是用户体验上的大跃进!
先直接看视频:
看完后是不是会由衷的说出那两个表示惊叹的词语。没错效果就是这么炫酷。
作者是这么介绍自己的:独立艺术家、程序员和研究员。生成艺术、互动装置和拓展思维的技术。前@voidtm的创意总监。禅宗男士。
长这样的:
那么作者也快速的开源了简易版代码,仓库地址:https://github.com/bgstaal/multipleWindow3dScene
项目中是这么介绍的:
- 使用three.js和localStorage跨多个窗口设置3D场景
- 一个简单的例子展示了如何使用three.js和localStorage在同一源上跨窗口设置一个3D场景。代码应该是不言自明的。
那么我们如何在本地运行查看效果呢,首先Git clone仓库到本地,使用编辑器打开,运行你自己的live server插件即可。
看看我这边运行的效果,虽然只是一个正方体,但还是很炫酷啊!
然后看看代码部分:
代码语言:javascript复制<!DOCTYPE html>
<html lang="en">
<head>
<title>跨多个窗口设置3D场景</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>
index文件引入了three.js的压缩包,版本号是124。Three.js是一个强大的3D图形库,用于在网页上创建和显示3D图形。以及main.js,实际的3D场景和逻辑都包含在main.js文件中。并且定义了所有元素(*)的默认边距(margin)和内边距(padding)都是0。
说明关键在于main.js,这里借助AI进行详细的解释,我们一块看看代码:
代码语言:javascript复制// 引入 WindowManager 类
import WindowManager from './WindowManager.js'
// 设置 Three.js 的别名为 t 用于简化代码
const t = THREE;
// 定义一些用于3D场景的变量
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();
// 声明 WindowManager 变量
let windowManager;
// 初始化标志变量
let initialized = false;
// 定义一个获取从当天开始的秒数的函数,确保所有窗口都使用相同的时间
function getTime() {
return (new Date().getTime() - today) / 1000.0;
}
// 如果URL参数包含 clear,则清空 localStorage
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 = {foo: "bar"};
// 初始化窗口管理器并将此窗口添加到集中的窗口池中
windowManager.init(metaData);
// 最初调用更新窗口函数(稍后将由 win 更改回调调用)
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 < cubes.length; i ) {
let cube = cubes[i];
let win = wins[i];
let _t = t;
let posTarget = {x: win.shape.x (win.shape.w * .5), y: win.shape.y (win.shape.h * .5)}
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.rotation.x = _t * .5;
cube.rotation.y = _t * .3;
};
// 渲染场景
renderer.render(scene, camera);
// 请求动画帧
requestAnimationFrame(render);
}
// 调整渲染器以适应窗口大小的函数
function resize() {
let width = window.innerWidth;
let height = window.innerHeight
camera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000);
camera.updateProjectionMatrix();
renderer.setSize(width, height);
}
}
这段代码是一个用于在同一来源的多个窗口中设置和同步3D场景的JavaScript模块示例。它利用了Three.js图形库来创建3D对象,并通过WindowManager
类处理不同窗口间的交互和数据同步。这个模块的主要目的是跨窗口展示和同步立方体的3D图形表示。
实现主要步骤
初始化和设置
代码开始初始化一系列变量,包括Three.js的场景、相机和渲染器。然后,通过getTime
函数获取相对于当天开始的时间,这样所有窗口都可以基于相同的时间参考点进行更新。
页面加载和可视状态处理
代码检查页面的URL参数,如果发现clear
参数,则会清空localStorage,这是一个可以通过URL触发的重置机制。
接下来,代码监听文档的可见性改变事件(visibilitychange
)和页面加载(onload
)事件来初始化3D场景。这是为了防止在某些浏览器中,页面内容在用户实际访问URL之前预加载时,可能出现的问题。
初始化3D场景和窗口管理
初始化函数init
负责设置场景、窗口管理器、调整渲染器大小以适应窗口,并开始渲染循环。setupScene
函数创建了相机、场景、渲染器和3D世界对象,并将渲染器的DOM元素添加到文档体中。
窗口管理器的设置通过setupWindowManager
函数完成,它实例化WindowManager
,并定义窗口形状变化和窗口更新的回调函数。窗口形状变化用于跟踪和反应窗口位置的移动。
动态3D场景更新
windowsUpdated
和updateNumberOfCubes
函数一起工作,根据窗口的数量和状态,动态添加或移除立方体对象。立方体的颜色和大小随着它们在窗口数组中的位置而变化,提供了一种视觉上的区分。
窗口形状更新通过调整sceneOffset
来实现,这样可以使3D世界的位置与窗口在屏幕上的位置相匹配,从而实现跨窗口的立体效果。
渲染循环
render
函数是这段代码的核心,它不断地更新时间,调用windowManager.update()
来处理窗口的变化,并应用新的位置和旋转到立方体对象。通过requestAnimationFrame
来创建一个平滑的动画效果。
窗口尺寸调整
最后,resize
函数确保当浏览器窗口大小改变时,相机和渲染器也相应地更新,以维持3D场景的正确透视和比例。
到这大致对于这个效果有了解了,但想实现自己的效果,比如改成其他形状,还需要不断学习,这边尝试借助ai编码更改还是有些问题,果然学无止境