< cubes.length; i++) {let cube = cubes[i];let win = wins[i];let _t = t;// + i * .2;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.innerHeightcamera = new t.OrthographicCamera(0, width, 0, height, -10000, 10000);camera.updateProjectionMatrix();renderer.setSize( width, height ); }}这段代码主要实现以下几点:
- 初始化场景和渲染器:在 setupScene 函数中,设置了一个正交相机、场景和渲染器,并将渲染器的 DOM 元素添加到页面中 。
- 初始化窗口管理器:在 setupWindowManager 函数中,创建了一个窗口管理器实例,并初始化了窗口并添加到窗口池中 。
- 更新立方体数量和位置:通过 updateNumberOfCubes 函数,根据窗口管理器中窗口的数量和位置信息,动态创建立方体并根据窗口位置更新其在场景中的位置 。
- 渲染循环:在 render 函数中 , 使用 requestAnimationFrame 不断循环渲染场景,并根据窗口管理器中窗口的位置更新立方体的位置和旋转 。
- 响应窗口大小变化:通过 resize 函数,在窗口大小变化时重新设置相机的宽高比和渲染器的大?。?以适应新的窗口尺寸 。
接下来看看最核心的实现:WindowManager,代码如下:
class WindowManager { #windows; #count; #id; #winData; #winShapeChangeCallback; #winChangeCallback;constructor () {let that = this;// 监听 localStorage 是否被其他窗口更改addEventListener("storage", (event) => {if (event.key == "windows") {let newWindows = JSON.parse(event.newValue);let winChange = that.#didWindowsChange(that.#windows, newWindows);that.#windows = newWindows;if (winChange) {if (that.#winChangeCallback) that.#winChangeCallback();}}});// 监听当前窗口是否即将关闭window.addEventListener('beforeunload', function (e) {let index = that.getWindowIndexFromId(that.#id);// 从窗口列表中移除当前窗口并更新 localStoragethat.#windows.splice(index, 1);that.updateWindowsLocalStorage();}); } // 检查窗口列表是否有变化 #didWindowsChange (pWins, nWins) {if (pWins.length != nWins.length) {return true;}else {let c = false;for (let i = 0; i < pWins.length; i++) {if (pWins[i].id != nWins[i].id) c = true;}return c;} } // 初始化当前窗口(添加元数据以将自定义数据存储在每个窗口实例中) init (metaData) {this.#windows = JSON.parse(localStorage.getItem("windows")) || [];this.#count= localStorage.getItem("count") || 0;this.#count++;this.#id = this.#count;let shape = this.getWinShape();this.#winData = {id: this.#id, shape: shape, metaData: metaData};this.#windows.push(this.#winData);localStorage.setItem("count", this.#count);this.updateWindowsLocalStorage(); } getWinShape () {let shape = {x: window.screenLeft, y: window.screenTop, w: window.innerWidth, h: window.innerHeight};return shape; } getWindowIndexFromId (id) {let index = -1;for (let i = 0; i < this.#windows.length; i++) {if (this.#windows[i].id == id) index = i;}return index; } updateWindowsLocalStorage () {localStorage.setItem("windows", JSON.stringify(this.#windows)); } update () {let winShape = this.getWinShape();if (winShape.x != this.#winData.shape.x ||winShape.y != this.#winData.shape.y ||winShape.w != this.#winData.shape.w ||winShape.h != this.#winData.shape.h) {this.#winData.shape = winShape;let index = this.getWindowIndexFromId(this.#id);this.#windows[index].shape = winShape;if (this.#winShapeChangeCallback) this.#winShapeChangeCallback();this.updateWindowsLocalStorage();} } setWinShapeChangeCallback (callback) {this.#winShapeChangeCallback = callback; } setWinChangeCallback (callback) {this.#winChangeCallback = callback; } getWindows () {return this.#windows; } getThisWindowData () {return this.#winData; } getThisWindowID () {return this.#id; }}export default WindowManager;
这段代码定义了一个 WindowManager 类 , 用于管理窗口的创建、更新和删除等操作,并将其作为模块导出 。该类包含以下私有属性:
- #windows: 存储所有窗口的数组 。
- #count: 记录窗口的数量 。
- #id: 当前窗口的唯一标识符 。
- #winData: 当前窗口的元数据,包括窗口的形状、自定义数据等 。
推荐阅读
- 免费获取GPT-4的五种工具
- HashMap:Java中的高效数据结构
- 上数据中台的第一步:理清数据中台5个建设步骤
- Nodejs 的 CommonJS 规范实现原理
- 架构模式解析,探索常见架构模式的优势与适用性
- AI绘画揭示未知的特异功能
- 电脑选择单条内存或两条内存组建双通道内存的区别
- EPON和GPON:无源光纤网络的区别大揭秘
- 无监督学习中的聚类算法综述
- 关于 Redis ,这里有你不知道的知识