Three.js 巡检教程
2026/8/3 16:05:13 网站建设 项目流程

巡检 ·Inspection View· ▶ 在线运行案例

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

你将学到什么

  • OrbitControls 相机轨道交互
  • glTF/Draco 模型加载与优化
  • requestAnimationFrame渲染循环与resize自适应

效果说明

本案例演示巡检效果:基于 WebGL 实现「巡检」可视化效果,附完整可运行源码;核心用到 OrbitControls、glTF/Draco。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Loader异步加载模型;glTF 返回gltf.scene,加载后注意scale与坐标系。Draco 需配置DRACOLoader
  • OrbitControls轨道旋转缩放;开enableDamping时每帧需controls.update()

实现步骤

  • 搭建 Scene / Camera / Renderer 与 OrbitControls
  • Loader 异步加载模型/纹理资源
  • rAF 循环中 update 并 render
  • 代码要点

    import * as THREE from "three";

    import {GLTFLoader} from "three/examples/jsm/loaders/GLTFLoader.js"; import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' import {DRACOLoader} from "three/examples/jsm/loaders/DRACOLoader.js"; import { PointerLockControls } from "three/examples/jsm/controls/PointerLockControls.js"; import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js"; import { animate } from "animejs";

    let scene, renderer, camera, orbitControls;

    let isRoaming = false; // 漫游模式标志

    let camera2; // 主要用于动画的相机 const eye1 = new THREE.Object3D();

    let pointerlockControls;

    // 用于动画的位置/旋转数据对象(animejs 直接修改此对象属性) const itemData = { px: 0, py: 0, pz: 0, rx: 0, ry: 0, rz: 0 };

    window.addEventListener('load', () => { init(); render(); eventBinding(); });

    // 初始化 function init() { scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 2000); camera.position.set(100, 100, 100);

    orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.enableDamping = true;

    camera2 = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000); camera2.position.set(0, 2, 0);

    const cameraHelper = new THREE.CameraHelper(camera2); scene.add(cameraHelper);

    // 将相机放置于 eye1 对象中,通过移动 eye1 驱动相机路径 eye1.add(camera2); scene.add(eye1);

    pointerlockControls = new PointerLockControls(camera2, renderer.domElement);

    scene.add(new THREE.AmbientLight(0xffffff, 1)); scene.add(new THREE.DirectionalLight(0xffffff, 1.5)); scene.add(new THREE.GridHelper(200, 20));

    const gltfLoader = new GLTFLoader() const dracoLoader = new DRACOLoader() dracoLoader.setDecoderPath(FILE_HOST + 'js/three/draco/') gltfLoader.setDRACOLoader(dracoLoader) gltfLoader.load(FILE_HOST + 'models/modern_city.glb', (gltf) => { gltf.scene.scale.set(0.03, 0.03, 0.03); scene.add(gltf.scene) })

    const gui = new GUI(); const btns = { startRoam: autoRun, stopAutoRun }; gui.add(btns, 'startRoam').name('开启漫游/巡检'); gui.add(btns, 'stopAutoRun').name('结束漫游/巡检'); gui.add(cameraHelper, 'visible').name('相机辅助线');

    window.addEventListener('resize', onResize); }

    // 窗口尺寸变化响应 function onResize() { const w = window.innerWidth; const h = window.innerHeight; renderer.setSize(w, h); camera.aspect = w / h; camera.updateProjectionMatrix(); camera2.aspect = w / h; camera2.updateProjectionMatrix(); }

    // 开启漫游 function autoRun() { isRoaming = true; orbitControls.enabled = false;

    // 同步 itemData 为 eye1 当前状态,避免重复调用时跳变 itemData.px = eye1.position.x; itemData.py = eye1.position.y; itemData.pz = eye1.position.z; itemData.rx = eye1.rotation.x; itemData.ry = eye1.rotation.y; itemData.rz = eye1.rotation.z;

    const a90 = -Math.PI / 2; const time1 = 6000; // 直线段时长 const time2 = 6000; // 等待转弯时长 const time3 = 1200; // 转弯时长

    // 巡检路径:沿城市街道绕行一圈(坐标根据城市模型缩放后的街道位置设定) animate(itemData, { px: [ { to: 0, duration: time1 }, { to: 30, duration: time1, delay: time3 }, { to: 30, duration: time1, delay: time3 }, { to: 0, duration: time1, delay: time3 } ], pz: [ { to: -30, duration: time1 }, { to: -30, duration: time1, delay: time3 }, { to: 0, duration: time1, delay: time3 }, { to: 0, duration: time1, delay: time3 } ], ry: [ { to: a90, duration: time3, delay: time2 }, { to: a90 * 2, duration: time3, delay: time2 }, { to: a90 * 3, duration: time3, delay: time2 }, { to: a90 * 4, duration: time3, delay: time2 } ], ease: "linear", onUpdate: () => { const { px, py, pz, rx, ry, rz } = itemData; eye1.position.set(px, py, pz); eye1.rotation.set(rx, ry, rz); }, onComplete: () => resetEye() }); }

    // 停止漫游 function stopAutoRun() { isRoaming = false; resetEye(); }

    // 重置 eye1 位置及 itemData 状态 function resetEye() { eye1.position.set(0, 0, 0); eye1.rotation.set(0, 0, 0); itemData.px = 0; itemData.py = 0; itemData.pz = 0; itemData.rx = 0; itemData.ry = 0; itemData.rz = 0; orbitControls.enabled = true; }

    // 绑定交互事件 function eventBinding() { window.addEventListener("mousedown", onMouseDown); window.addEventListener("mouseup", onMouseRelease); window.addEventListener("mouseleave", onMouseRelease); }

    function onMouseDown() { if (isRoaming) { pointerlockControls.isLocked = true; } }

    function onMouseRelease() { if (isRoaming) { pointerlockControls.isLocked = false; cameraReturn(); } }

    /*

    • 关键开发思路
    • 1. 使用 eye1 对象执行路径移动
    • 2. 利用鼠标事件对 camera2 进行方向控制
    • 3. 松开鼠标时,执行相机回归中心方向的动画
    */ function render() { requestAnimationFrame(render); orbitControls.update(); renderer.render(scene, isRoaming ? camera2 : camera); }

    // 相机视角回归动画(使用四元数避免 rotation 动画的万向锁问题) function cameraReturn() { animate(camera2.quaternion, { x: 0, y: 0, z: 0, w: 1, ease: "linear", duration: 500 }); }

    完整源码:GitHub

    小结

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询