我本是写react的,公司让我换赛道搞web3D
从前端到 Web3D,不是换条赛道,而是打开新维度。
❝韩老师说过:再牛的程序员都是从小白开始,既然开始了,就全心投入学好技术。
❞
🔴 工具
所有的api都可以通过threejs官网的document,切成中文,去搜:
🔴 平面
⭕️ Scene 场景
场景能够让你在什么地方、摆放什么东西来交给three.js来渲染,这是你放置物体、灯光和摄像机的地方。
import * as THREE from "three";// console.log(THREE);// 目标:了解three.js最基本的内容// 1、创建场景const scene = new THREE.Scene();
⭕️ camera 相机
示例:threejs.org/examples/?q=camera#webgl_camera
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); // 使用渲染器,通过相机将场景渲染进来 (场景, 相机)
⭕️ 效果
效果是平面的:
到这里,还不是 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);
⭕️ 设置物体移动
// 设置相机位置camera.position.set(0, 0, 10);scene.add(camera);
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 动画库
// 导入动画库import gsap from "gsap";// 设置动画var animate1 = gsap.to(cube.position, {x: 5,duration: 5,ease: "power1.inOut", // 动画属性// 设置重复的次数,无限次循环-1repeat: -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("立方体运动");
🔴 结语
❝前端的世界,不该只有
❞Vue和React——还有 「WebGPU」 里等待你征服的星辰大海。
“当 WebGL 成为下一代前端的基础设施,愿你是最早站在三维坐标系里的那个人。”