🔥🔥🔥结合 vue 或 react,去写three.js

政府的新基建项目(如数字孪生智慧城市室内设计)广泛应用3D可视化。对于开发者,可以Three.js库入手,它简化了WebGL的底层开发,适合快速入门。

学习基础概念:
几何体 (Geometry):基本形状如 BoxSphere,自定义顶点结构
材质 (Material):基础材质纹理贴图、透明度等属性
场景 (Scene):创建三维世界,管理物体光照相机
相机 (Camera):透视相机与正交相机,视角控制位置调整

掌握 PBR(基于物理的渲染)光照体系:
-> 理解 PBR 渲染的原理:金属度、粗糙度、能量守恒
-> 使用标准材质:MeshStandardMaterialMeshPhysicalMaterial
-> 加载环境贴图 (IBL):实现真实反射与高光 -> 应用贴图通道:法线贴图金属贴图AO贴图
-> 配置光源类型与参数:点光源、环境光、平行光等实现立体感
-> 引入HDR、色调映射与 Gamma 校正优化真实感

进阶阶段需要掌握交互设计物理引擎(如重力、摩擦力)和着色器编程(如顶点着色器、片元着色器),以实现动态效果(如旗帜飘扬、烟雾模拟)。

后期处理可打造特殊视觉(如雪花屏、水下视角),而Blender等建模工具能优化复杂模型和动画。最终,结合CSS3D渲染器地理信息数据

可以构建逼真的智慧园区数字城市,实现3D与网页元素的深度互动。

1. 基础Three.js应用搭建

首先我们来创建一个最基础的Three.js应用,展示一个旋转的立方体

javascript 复制代码
// 引入Three.js库
import * as THREE from 'three';

// 1. 创建场景 - 相当于一个3D世界容器
const scene = new THREE.Scene();

// 2. 创建相机 - 决定我们能看到什么
// 参数分别是:视野角度(FOV)、宽高比、近裁剪面、远裁剪面
const camera = new THREE.PerspectiveCamera(
  75, // 视野角度,越大看到范围越广
  window.innerWidth / window.innerHeight, // 相机宽高比
  0.1, // 近裁剪面,小于此距离的物体不显示
  1000 // 远裁剪面,大于此距离的物体不显示
);

// 3. 创建渲染器 - 负责将3D场景渲染到2D屏幕上
const renderer = new THREE.WebGLRenderer();
// 设置渲染器大小为窗口尺寸
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器的canvas元素(画布)添加到HTML文档中
document.body.appendChild(renderer.domElement);

// 4. 创建立方体几何体
const geometry = new THREE.BoxGeometry(1, 1, 1); // 长宽高各为1的立方体
// 创建材质 - 决定物体外观
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); // 绿色
// 创建网格物体 = 几何体 + 材质
const cube = new THREE.Mesh(geometry, material);
// 将立方体添加到场景中
scene.add(cube);

// 设置相机位置,默认在(0,0,0),需要向后移动才能看到物体
camera.position.z = 5;

// 5. 动画循环函数
function animate() {
  requestAnimationFrame(animate); // 请求下一帧动画
  
  // 旋转立方体
  cube.rotation.x += 0.01; // 绕X轴旋转
  cube.rotation.y += 0.01; // 绕Y轴旋转
  
  // 渲染场景
  renderer.render(scene, camera);
}

// 启动动画循环
animate();

// 6. 响应窗口大小变化
window.addEventListener('resize', () => {
  // 更新相机宽高比
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  // 更新渲染器大小
  renderer.setSize(window.innerWidth, window.innerHeight);
});

关键点说明:

🔥场景(Scene) : 所有3D对象的容器,相当于一个虚拟世界

🔥相机(Camera) : 决定我们能看到的视角范围,常用的是透视相机(PerspectiveCamera)

🔥渲染器(Renderer) : 负责将3D场景渲染到2D屏幕上

🔥几何体(Geometry) : 定义物体的形状

🔥材质(Material) : 定义物体的外观(颜色纹理等)

🔥网格(Mesh) : 几何体材质的结合体,构成可渲染的物体

2. 结合Vue框架写Three.js

Vue项目中用Three.js的代码。

javascript 复制代码
<template>
  <!-- 空的div容器,Three.js会自动添加canvas元素 -->
  <div ref="threeContainer"></div>
</template>

<script setup>
import * as THREE from 'three';
import { onMounted, onUnmounted, ref } from 'vue';

const threeContainer = ref(null);

// 定义响应式变量存储Three.js实例
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
const renderer = new THREE.WebGLRenderer();
const cube = ref(null);

// 动画循环ID
let animationId = null;

// 初始化Three.js场景
const initThreeScene = () => {
  // 设置渲染器大小并添加到DOM
  renderer.setSize(window.innerWidth, window.innerHeight);
  threeContainer.value.appendChild(renderer.domElement);
  
  // 创建立方体
  const geometry = new THREE.BoxGeometry();
  const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
  cube.value = new THREE.Mesh(geometry, material);
  scene.add(cube.value);
  
  camera.position.z = 5;
};

// 动画循环
const animate = () => {
  cube.value.rotation.x += 0.01;
  cube.value.rotation.y += 0.01;
  renderer.render(scene, camera);
  animationId = requestAnimationFrame(animate);
};

// 响应式调整大小
const handleResize = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

onMounted(() => {
  initThreeScene();
  animate();
  window.addEventListener('resize', handleResize);
});

// 组件卸载时清理
onUnmounted(() => {
  cancelAnimationFrame(animationId);
  window.removeEventListener('resize', handleResize);
  threeContainer.value?.removeChild(renderer.domElement);
});
</script>

<style scoped>
/* 确保canvas填满整个屏幕 */
:root, body, html {
  margin: 0;
  padding: 0;
  overflow: hidden;
  width: 100%;
  height: 100%;
}

div {
  width: 100%;
  height: 100%;
}
</style>

Vue整合要点:

  1. 使用ref获取DOM容器
  2. onMounted生命周期钩子中初始化
  3. 组件卸载时记得清理资源和卸掉事件监听
  4. 动画循环依然使用requestAnimationFrame

3. 结合React框架写Three.js

React中用Three.js,提供函数组件类组件两种写法。

函数组件写法:

jsx 复制代码
import React, { useEffect, useRef } from 'react';
import * as THREE from 'three';

function ThreeScene() {
  const mountRef = useRef(null);
  
  useEffect(() => {
    // 1. 初始化场景
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
    const renderer = new THREE.WebGLRenderer();
    
    // 设置渲染器并添加到DOM
    renderer.setSize(window.innerWidth, window.innerHeight);
    mountRef.current.appendChild(renderer.domElement);
    
    // 2. 创建立方体
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    const cube = new THREE.Mesh(geometry, material);
    scene.add(cube);
    
    camera.position.z = 5;
    
    // 3. 动画循环
    const animate = () => {
      requestAnimationFrame(animate);
      cube.rotation.x += 0.01;
      cube.rotation.y += 0.01;
      renderer.render(scene, camera);
    };
    
    animate();
    
    // 4. 响应式调整
    const handleResize = () => {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    };
    
    window.addEventListener('resize', handleResize);
    
    // 清理函数
    return () => {
      window.removeEventListener('resize', handleResize);
      mountRef.current?.removeChild(renderer.domElement);
    };
  }, []);
  
  return <div ref={mountRef} />;
}

export default ThreeScene;

类组件写法:

jsx 复制代码
import React, { Component } from 'react';
import * as THREE from 'three';

class ThreeScene extends Component {
  componentDidMount() {
    // 1. 初始化场景
    this.scene = new THREE.Scene();
    this.camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    this.renderer = new THREE.WebGLRenderer();
    
    // 设置渲染器并添加到DOM
    this.renderer.setSize(window.innerWidth, window.innerHeight);
    this.mount.appendChild(this.renderer.domElement);
    
    // 2. 创建立方体
    const geometry = new THREE.BoxGeometry();
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
    this.cube = new THREE.Mesh(geometry, material);
    this.scene.add(this.cube);
    
    this.camera.position.z = 5;
    
    // 启动动画循环
    this.startAnimationLoop();
    
    // 3. 添加窗口大小变化监听
    window.addEventListener('resize', this.handleResize);
  }
  
  componentWillUnmount() {
    // 清理资源
    window.removeEventListener('resize', this.handleResize);
    this.mount.removeChild(this.renderer.domElement);
    cancelAnimationFrame(this.requestId);
  }
  
  startAnimationLoop = () => {
    this.requestId = requestAnimationFrame(this.startAnimationLoop);
    this.cube.rotation.x += 0.01;
    this.cube.rotation.y += 0.01;
    this.renderer.render(this.scene, this.camera);
  };
  
  handleResize = () => {
    this.camera.aspect = window.innerWidth / window.innerHeight;
    this.camera.updateProjectionMatrix();
    this.renderer.setSize(window.innerWidth, window.innerHeight);
  };
  
  render() {
    return <div ref={ref => (this.mount = ref)} />;
  }
}

export default ThreeScene;

React整合要点:

  1. 使用useRef(函数组件)或实例属性(类组件)获取DOM容器
  2. useEffect(函数组件)或componentDidMount(类组件)中初始化Three.js
  3. 在清理函数或componentWillUnmount中释放资源
  4. 动画循环依然使用requestAnimationFrame

总结

用Vue和React框架整合开发3D可视化threejs应用,注意的点:

  1. Three.js核心概念:场景、相机、渲染器三位一体构成基础框架
  2. 动画原理 :使用requestAnimationFrame实现平滑动画
  3. 响应式设计:监听窗口大小变化并调整相机和渲染器
  4. 框架整合
    • Vue中使用组合式API和生命周期钩子
    • React中使用hooks或类组件生命周期
  5. 资源管理:组件卸载时remove和cancel掉动画frame
相关推荐
奕辰杰3 小时前
关于npm前端项目编译时栈溢出 Maximum call stack size exceeded的处理方案
前端·npm·node.js
JiaLin_Denny5 小时前
如何在NPM上发布自己的React组件(包)
前端·react.js·npm·npm包·npm发布组件·npm发布包
路光.6 小时前
触发事件,按钮loading状态,封装hooks
前端·typescript·vue3hooks
我爱996!6 小时前
SpringMVC——响应
java·服务器·前端
咔咔一顿操作7 小时前
Vue 3 入门教程7 - 状态管理工具 Pinia
前端·javascript·vue.js·vue3
kk爱闹7 小时前
用el-table实现的可编辑的动态表格组件
前端·vue.js
漂流瓶jz8 小时前
JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具
前端·javascript·编译原理
换日线°8 小时前
css 不错的按钮动画
前端·css·微信小程序
风象南8 小时前
前端渲染三国杀:SSR、SPA、SSG
前端
90后的晨仔8 小时前
表单输入绑定详解:Vue 中的 v-model 实践指南
前端·vue.js