稀土掘金技术社区

我本是写react的,公司让我换赛道搞web3D

从前端到 Web3D,不是换条赛道,而是打开新维度。

❝

韩老师说过:再牛的程序员都是从小白开始,既然开始了,就全心投入学好技术。

❞

🔴 工具

所有的api都可以通过threejs官网的document,切成中文,去搜:

Image

🔴 平面

⭕️ Scene 场景

场景能够让你在什么地方、摆放什么东西来交给three.js来渲染,这是你放置物体、灯光和摄像机的地方。

Image
import * as THREE from "three";
// console.log(THREE);
// 目标:了解three.js最基本的内容
// 1、创建场景const scene = new THREE.Scene();

⭕️ camera 相机

示例:threejs.org/examples/?q=camera#webgl_camera

Image
import * as THREE from "three";
// console.log(THREE);
// 目标:了解three.js最基本的内容
// 1、创建场景const scene = new THREE.Scene();
// 2、创建相机const camera = new THREE.PerspectiveCamera(  75, // 相机的角度  window.innerWidth / window.innerHeight, // 相机的宽高比  0.1, // 相机的近截面  1000 // 相机的远截面);
// 设置相机位置camera.position.set(0, 0, 10); // 相机位置 (X轴坐标, Y轴坐标, Z轴坐标)scene.add(camera); // 相机添加到场景中

⭕️ 物体 cube

import * as THREE from "three";
// console.log(THREE);
// 目标:了解three.js最基本的内容
// 1、创建场景const scene = new THREE.Scene();
// 2、创建相机const camera = new THREE.PerspectiveCamera(  75, // 相机的角度  window.innerWidth / window.innerHeight, // 相机的宽高比  0.1, // 相机的近截面  1000 // 相机的远截面);
// 设置相机位置camera.position.set(0, 0, 10); // 相机位置 (X轴坐标, Y轴坐标, Z轴坐标)scene.add(camera); // 相机添加到场景中
// 添加物体// 创建几何体const cubeGeometry = new THREE.BoxGeometry(1, 1, 1); // 创建立方体的几何体 (长, 宽, 高)const cubeMaterial = new THREE.MeshBasicMaterial({ color: 0xffff00 }); // MeshBasicMaterial 基础网格材质 ({ color: 0xffff00 }) 颜色// 根据几何体和材质创建物体const cube = new THREE.Mesh(cubeGeometry, cubeMaterial); // 创建立方体的物体 (几何体, 材质)// 将几何体添加到场景中scene.add(cube); // 物体添加到场景中

⭕️ 渲染 render

// 初始化渲染器const renderer = new THREE.WebGLRenderer();// 设置渲染的尺寸大小renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染的尺寸大小 (窗口宽度, 窗口高度)// console.log(renderer);// 将webgl渲染的canvas内容添加到bodydocument.body.appendChild(renderer.domElement); // 将webgl渲染的canvas内容添加到body
// 使用渲染器,通过相机将场景渲染进来renderer.render(scene, camera); // 使用渲染器,通过相机将场景渲染进来 (场景, 相机)

⭕️ 效果

效果是平面的:

Image

到这里,还不是 3d 的,如果要加 3d,要加一下控制器。

🔴 3d

⭕️ 控制器

添加轨道。像卫星☄围绕地球🌏,环绕查看的视角:

// 导入轨道控制器import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 目标:使用控制器查看3d物体
// // 使用渲染器,通过相机将场景渲染进来// renderer.render(scene, camera);
// 创建轨道控制器const controls = new OrbitControls(camera, renderer.domElement); // 创建轨道控制器 (相机, 渲染器dom元素)controls.enableDamping = true; // 设置控制器阻尼,让控制器更有真实效果。
function render() {  renderer.render(scene, camera); // 浏览器每渲染一帧,就重新渲染一次  //   渲染下一帧的时候就会调用render函数  requestAnimationFrame(render); // 浏览器渲染下一帧的时候就会执行render函数,执行完会再次调用render函数,形成循环,每秒60次}
render();

⭕️ 加坐标轴辅助器

// 添加坐标轴辅助器const axesHelper = new THREE.AxesHelper(5); // 坐标轴(size轴的大小)scene.add(axesHelper);
Image

⭕️ 设置物体移动

// 设置相机位置camera.position.set(0, 0, 10);scene.add(camera);
Image
cube.position.x = 3;
// 往返移动function render() {  cube.position.x += 0.01;  if (cube.position.x > 5) {    cube.position.x = 0;  }  renderer.render(scene, camera);  //   渲染下一帧的时候就会调用render函数  requestAnimationFrame(render);}
render();

⭕️ 缩放

cube.scale.set(3, 2, 1); // xyz, x3倍, y2倍

单独设置

cube.position.x = 3;

⭕️ 旋转

cube.rotation.set(Math.PI / 4, 0, 0, "XZY"); // x轴旋转45度

单独设置

cube.rotation.x = Math.PI / 4;

⭕️ requestAnimationFrame

function render(time) {  //   console.log(time);  //   cube.position.x += 0.01;  //   cube.rotation.x += 0.01;
  // time 是一个不断递增的数字,代表当前的时间  let t = (time / 1000) % 5; // 为什么求余数,物体移动的距离就是t,物体移动的距离是0-5,所以求余数  cube.position.x = t * 1; // 0-5秒,物体移动0-5距离
  //   if (cube.position.x > 5) {  //     cube.position.x = 0;  //   }  renderer.render(scene, camera);  //   渲染下一帧的时候就会调用render函数  requestAnimationFrame(render);}
render();

⭕️ Clock 跟踪事件处理动画

// 设置时钟const clock = new THREE.Clock();function render() {  // 获取时钟运行的总时长  let time = clock.getElapsedTime();  console.log("时钟运行总时长:", time);  //   let deltaTime = clock.getDelta();  //     console.log("两次获取时间的间隔时间:", deltaTime);  let t = time % 5;  cube.position.x = t * 1;
  renderer.render(scene, camera);  //   渲染下一帧的时候就会调用render函数  requestAnimationFrame(render);}
render();

大概是 8 毫秒一次渲染时间.

⭕️ 不用算 用 Gsap 动画库

https://gsap.com/
// 导入动画库import gsap from "gsap";
// 设置动画var animate1 = gsap.to(cube.position, {  x: 5,  duration: 5,  ease: "power1.inOut", // 动画属性  //   设置重复的次数,无限次循环-1  repeat: -1,  //   往返运动  yoyo: true,  //   delay,延迟2秒运动  delay: 2,  onComplete: () => {    console.log("动画完成");  },  onStart: () => {    console.log("动画开始");  },});gsap.to(cube.rotation, { x: 2 * Math.PI, duration: 5, ease: "power1.inOut" });
// 双击停止和恢复运动window.addEventListener("dblclick", () => {  //   console.log(animate1);  if (animate1.isActive()) {    //   暂停    animate1.pause();  } else {    //   恢复    animate1.resume();  }});
function render() {  renderer.render(scene, camera);  //   渲染下一帧的时候就会调用render函数  requestAnimationFrame(render);}
render();

⭕️ 根据尺寸变化 实现自适应

// 监听画面变化,更新渲染画面window.addEventListener("resize", () => {  //   console.log("画面变化了");  // 更新摄像头  camera.aspect = window.innerWidth / window.innerHeight;  //   更新摄像机的投影矩阵  camera.updateProjectionMatrix();
  //   更新渲染器  renderer.setSize(window.innerWidth, window.innerHeight);  //   设置渲染器的像素比  renderer.setPixelRatio(window.devicePixelRatio);});

⭕️ 用 js 控制画布 全屏 和 退出全屏

window.addEventListener("dblclick", () => {  const fullScreenElement = document.fullscreenElement;  if (!fullScreenElement) {    //   双击控制屏幕进入全屏,退出全屏    // 让画布对象全屏    renderer.domElement.requestFullscreen();  } else {    //   退出全屏,使用document对象    document.exitFullscreen();  }  //   console.log(fullScreenElement);});

⭕️ 应用 图形 用户界面 更改变量

// 导入dat.guiimport * as dat from "dat.gui";
const gui = new dat.GUI();gui  .add(cube.position, "x")  .min(0)  .max(5)  .step(0.01)  .name("移动x轴")  .onChange((value) => {    console.log("值被修改:", value);  })  .onFinishChange((value) => {    console.log("完全停下来:", value);  });
//   修改物体的颜色const params = {  color: "#ffff00",  fn: () => {    //   让立方体运动起来    gsap.to(cube.position, { x: 5, duration: 2, yoyo: true, repeat: -1 });  },};gui.addColor(params, "color").onChange((value) => {  console.log("值被修改:", value);  cube.material.color.set(value);});// 设置选项框gui.add(cube, "visible").name("是否显示");
var folder = gui.addFolder("设置立方体");folder.add(cube.material, "wireframe");// 设置按钮点击触发某个事件folder.add(params, "fn").name("立方体运动");
Image

🔴 结语

❝

前端的世界,不该只有Vue和React——还有 「WebGPU」 里等待你征服的星辰大海。

❞

“当 WebGL 成为下一代前端的基础设施,愿你是最早站在三维坐标系里的那个人。”