尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

Three.js 多浏览器窗口连接教程

Three.js 多浏览器窗口连接教程
📅 发布时间:2026/7/2 12:14:41

多浏览器窗口连接 ·Mult Window· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • requestAnimationFrame渲染循环与resize自适应

效果说明

Three.js 接第三方库或扩展能力。入口在WindowManager。

核心概念

  • CubeTexture六面贴图作scene.background;scene.environment供 PBR 材质反射。
  • Reflector/Water基于 renderTarget 的平面反射或动态水面法线。

实现步骤

  • 搭建 Scene / Camera / Renderer 与 OrbitControls
  • rAF 循环中 update 并 render
  • 代码要点

    import * as THREE from 'three'

    confirm("是否打开多窗口?") ? window.open(

    HOST + '#/codeMirror?navigation=ThreeJS&classify=expand&id=multWindowScene',

    '_blank',

    width=800,height=600,left=${Math.random()500},top=${Math.random()200}) : null

    class WindowManager { #windows; #count = localStorage.getItem("count") || 0; #id; #winData; #winShapeChangeCallback; #winChangeCallback;

    constructor() { addEventListener("storage", (event) => { if (event.key == "windows") { let newWindows = JSON.parse(event.newValue); let winChange = this.#didWindowsChange(this.#windows, newWindows); this.#windows = newWindows; if (winChange && this.#winChangeCallback) this.#winChangeCallback(); } });

    window.addEventListener('beforeunload', () => { let index = this.getWindowIndexFromId(this.#id); this.#windows.splice(index, 1); this.updateWindowsLocalStorage(); }); }

    #didWindowsChange(pWins, nWins) { if (pWins.length != nWins.length) return true; return pWins.some((pWin, i) => pWin.id != nWins[i].id); }

    init(metaData) { this.#windows = JSON.parse(localStorage.getItem("windows")) || []; 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() { return { x: window.screenLeft, y: window.screenTop, w: window.innerWidth, h: window.innerHeight }; }

    getWindowIndexFromId(id) { return this.#windows.findIndex(win => win.id == id); }

    updateWindowsLocalStorage() { localStorage.setItem("windows", JSON.stringify(this.#windows)); }

    update() { let winShape = this.getWinShape(); if (Object.values(winShape).some((value, i) => value != Object.values(this.#winData.shape)[i])) { 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; } }

    let camera, scene, renderer, world, near, far, pixR = window.devicePixelRatio || 1, cubes = [], sceneOffsetTarget = { x: 0, y: 0 }, sceneOffset = { x: 0, y: 0 }; let today = new Date().setHours(0, 0, 0, 0), internalTime = (new Date().getTime() - today) / 1000.0, windowManager, initialized = false;

    if (new URLSearchParams(window.location.search).get("clear")) localStorage.clear(); else { document.addEventListener("visibilitychange", () => { if (document.visibilityState != 'hidden' && !initialized) init(); }); window.onload = () => { if (document.visibilityState != 'hidden') init(); };

    function init() { initialized = true; setTimeout(() => { setupScene(); setupWindowManager(); resize(); updateWindowShape(false); render(); window.addEventListener('resize', resize); }, 500) }

    function setupScene() { camera = new THREE.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 THREE.Scene(); scene.background = new THREE.Color(0.0); scene.add(camera); renderer = new THREE.WebGLRenderer({ antialias: true, depthBuffer: true }); renderer.setPixelRatio(pixR); world = new THREE.Object3D(); scene.add(world); renderer.domElement.setAttribute("id", "scene"); document.body.appendChild(renderer.domElement); }

    function setupWindowManager() { windowManager = new WindowManager(); windowManager.setWinShapeChangeCallback(updateWindowShape); windowManager.setWinChangeCallback(updateNumberOfCubes); windowManager.init({ foo: "bar" }); updateNumberOfCubes(); }

    function updateNumberOfCubes() { cubes.forEach((c) => { world.remove(c); }); cubes = []; windowManager.getWindows().forEach((win, i) => { let c = new THREE.Color(); c.setHSL(i * .1, 1.0, .5); let s = 100 + i * 50, cube = new THREE.Mesh(new THREE.BoxGeometry(s, s, s), new THREE.MeshBasicMaterial({ color: c , transparent: true, opacity: 0.5 })); 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 = (new Date().getTime() - today) / 1000.0, falloff = .05; windowManager.update(); sceneOffset.x += (sceneOffsetTarget.x - sceneOffset.x)falloff; sceneOffset.y += (sceneOffsetTarget.y - sceneOffset.y)falloff; world.position.x = sceneOffset.x; world.position.y = sceneOffset.y; cubes.forEach((cube, i) => { let win = windowManager.getWindows()[i], _t = t, posTarget = { x: win.shape.x + (win.shape.w.5), y: win.shape.y + (win.shape.h.5) }; cube.position.x += (posTarget.x - cube.position.x)falloff; 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, height = window.innerHeight; camera = new THREE.OrthographicCamera(0, width, 0, height, -10000, 10000); camera.updateProjectionMatrix(); renderer.setSize(width, height); } }

    完整源码:GitHub

    小结

    • 本文提供多浏览器窗口连接完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库

相关新闻

  • 三步解锁WeMod Pro:Wand-Enhancer开源增强工具全攻略
  • 简历自我评价别再写“积极向上“了!实测6款AI工具,3分钟生成HR想看的版本
  • 一次陪家人看牙的简单记录

最新新闻

  • STM32G431KB与LV3296嵌入式数据采集系统设计
  • STM32与Si4731打造低成本可编程收音机系统
  • STM32F765ZI与BMI270的6DoF IMU开发指南
  • ICM-42605与PIC18LF4515实现运动追踪系统设计
  • 2026海南省黄金回收白银回收铂金回收旧料回收怎么选?五家高实价铂金白银线下门店测评清单 + 联系方式
  • PIC18F86J16实现DC-DC降压电源设计与优化

日新闻

  • Python Playwright录制功能:从零到一构建自动化测试脚本
  • 如何用开源工具永久保存你心爱的小说:novel-downloader全攻略
  • In-Context Learning不是教知识,而是模式对齐:从5个示例到100个工业级样本的真相

周新闻

  • Windows字体自定义终极方案:No!! MeiryoUI完全指南
  • Deepin Boot Maker:告别命令行,3分钟制作Linux启动盘的智能解决方案
  • Plain Craft Launcher 2:重新定义你的Minecraft游戏体验

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号