当前位置:   article > 正文

three.js基础_threejs

threejs

三要素

1.场 景(scene):放置物体的容器

2.摄像机(camera):类似人眼,可调位置,角度等信息,展示不同画面

3.渲染器(renderer):接收场景和摄像机,计算在浏览器上渲染的最终 2D 画面

import * as THREE from 'three';

let scene, camera, renderer  // 场景,摄像机,渲染器

let controls   // 轨道控制器 作用:调整轨道控制器属性,影响摄像机细节

let cube  // 物体对象

  1. function init() {
  2. // 1 创建3D场景对象Scene
  3. scene = new THREE.Scene();
  4. // 1.1 实例化一个透视投影相机对象
  5. camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  6. camera.position.z = 5
  7. // 1.2 创建渲染器对象 antialias: true开启抗锯齿效果
  8. renderer = new THREE.WebGLRenderer({ antialias: true });
  9. // 1.3 定义threejs输出画布的尺寸(单位:像素px)
  10. // const width = 800; //宽度
  11. // const height = 500; //高度
  12. renderer.setSize(window.innerWidth, window.innerHeight); //设置three.js渲染区域的尺寸(像素px)
  13. document.body.append(renderer.domElement)
  14. // 传入场景摄像机渲染画面,注意+后续这段代码挪到循环渲染函数中
  15. renderer.render(scene, camera)
  16. }

立方体(cube)绘制流程

1.几何图形 

2.材质:网格基础材质-线面纯颜色描绘表面

3.网格物体(Mesh):将有材质的图形放置网格物体中

  1. function createCube() {
  2. // 2. 创建图形,宽高深为 1 单位(立方缓冲几何体)
  3. const geometry = new THREE.BoxGeometry(1, 1, 1);
  4. // 2.1. 创建材质,颜色为绿色 0x00ff00 (网格基础材质-线面纯颜色描绘表面)
  5. const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
  6. // 2.2. 创建网格物体对象,传入图形和材质(网格物体对象)
  7. cube = new THREE.Mesh(geometry, material);
  8. // 2.3. 把物体加入到场景中
  9. scene.add(cube);
  10. }

轨道控制器:

  1. // 3. 单独引入 OrbitControls 轨道控制器构造函数
  2. import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
  3. function controlsCreate() {
  4. // 3.1 创建轨道控制器
  5. controls = new OrbitControls(camera, renderer.domElement)
  6. }

详细控制

  垂直旋转范围控制                                                        水平旋转范围控制

     

  1. function controlsCreate() {
  2. // 3.1 创建轨道控制器
  3. controls = new OrbitControls(camera, renderer.domElement)
  4. // 5 添加阻尼效果
  5. controls.enableDamping = true
  6. // 5.2 开启自动旋转轨道控制器带动摄像机一起旋转
  7. // controls.autoRotate = true
  8. // 5.3垂直角度范围控制(0 上面,Math.PI 下面 上=>下 范围0-Math.PI)
  9. // controls.maxPolarAngle = Math.PI
  10. // controls.minPolarAngle = 0
  11. // // 5.4水平角度范围控制(0 上面,Math.PI 下面 上=>下 范围0-Math.PI)
  12. // controls.maxAzimuthAngle = 1.5 * Math.PI
  13. // controls.minAzimuthAngle = 0.5 * Math.PI
  14. // 5.5控制摄像机缩放比例
  15. // controls.maxDistance = 10
  16. // controls.minDistance = 2
  17. }

渲染循环-更新场景渲染 

  1. function renderLoop() {
  2. // 4. 在渲染循环中更新场景渲染
  3. renderer.render(scene, camera)
  4. // 4.1手动 JS 代码更新过摄像机信息,必须调用轨道控制器 update 方法
  5. controls.update()
  6. // 4.2 根据当前计算机浏览器刷新帧率(默认 60/秒),不断递归调用此函数渲染最新的画面状态
  7. // 好处:当前页面切换到后台,暂停递归
  8. requestAnimationFrame(renderLoop)
  9. }

坐标轴

  1. // 添加坐标轴辅助对象
  2. function createHelper() {
  3. // 5. 创建坐标轴对象,设置长度
  4. const axesHelper = new THREE.AxesHelper(5)
  5. // 5.1. 添加到场景中
  6. scene.add(axesHelper)
  7. }

适配场景大小

  1. // 适配窗口函数
  2. function renderResize() {
  3. window.addEventListener('resize', () => {
  4. // 调整渲染器画布大小
  5. renderer.setSize(window.innerWidth, window.innerHeight)
  6. // 调整摄像机宽高比
  7. camera.aspect = window.innerWidth / window.innerHeight
  8. // 更新视椎体空间
  9. camera.updateProjectionMatrix()
  10. })
  11. }

移动立方体

  1. // 移动立方体
  2. function moveCube() {
  3. // 位移 position
  4. cube.position.x = 5
  5. // cube.position.set(5, 5, 0)
  6. // 旋转 rotation 值为eular角对象 (弧度制)
  7. // 在轴正方向来看物体,默认是绕着轴进行逆时针旋转
  8. cube.rotation.x = Math.PI / 4
  9. // // cube.rotation.set(Math.PI / 4, 0, 0)
  10. // // 缩放 scale (Vector3 三维向量对象,中心原点不动,向 2 边拉伸/缩小)
  11. cube.scale.z = 2
  12. // cube.scale.set(1, 1, 2)
  13. }

上述方法顺序调用

  1. /**准备环境--S */
  2. init() // 初始化
  3. controlsCreate() // 创建轨道控制器
  4. createHelper() // 添加坐标轴辅助
  5. renderResize() // 适配窗口函数
  6. /**准备环境--E */
  7. /**准备内容--S */
  8. createCube() // 创建立方体
  9. moveCube() // 移动立方体
  10. /**准备内容--E */
  11. /**准备逻辑--S */
  12. renderLoop() // 渲染循环
  13. /**准备逻辑--E */

GUI工具的使用

  1. // 6. 引入dat.gui工具库
  2. import * as dat from 'dat.gui';
  3. 创建gui函数
  4. function createGUI() {
  5. // 创建gui工具对象
  6. const gui = new dat.GUI();
  7. // 添加具体控制器使用
  8. //gui.add()添加图形用户界面工具
  9. //参数1:关联D0M对象,JS对象,3D物体对象
  10. //参数2:对象其中的某个属性,给这个属性关联用户界面工具(从而快速调整它的值)
  11. //6.0(字符串->输入框)
  12. gui.add(document, 'title')
  13. //6.1控制立方体显示/隐藏(布尔->多选框)
  14. gui.add(cube, 'visible')
  15. //6.2轨道控制器回归初始角度(函数->按钮)
  16. gui.add(controls, 'reset')
  17. // 6.3控制立方体颜色(找属性方式:文档 -> 打印 -> 百度
  18. // 效果:立方体默认颜色和文字 <=> 显示在工具标签上
  19. const colorObj = {
  20. 'col': `#${cube.material.color.getHexString()}`
  21. }
  22. gui.addColor(colorObj, 'col').onChange(val => {
  23. cube.material.color = new THREE.Color(val);
  24. })
  25. // 6.4创建gui分组
  26. const folder = gui.addFolder('位移')
  27. folder.add(cube.position, 'x', 0, 5, 0.1)
  28. folder.add(cube.position, 'y', 0, 5, 0.1)
  29. folder.add(cube.position, 'z', 0, 5, 0.1)
  30. // 6.5 下拉菜单(关键:第三个参数为对象时->下拉菜单)
  31. // 对象中属性名->下拉菜单选项名
  32. // 初始值匹配后会影响下拉菜单默认选中哪一项
  33. gui.add({ type: '1' }, 'type', { '方案1': '1', '方案2': '2' }).onChange(val => {
  34. // val 方案对象的 '1','2'
  35. switch (val) {
  36. case '1':
  37. cube.position.set(0, 0, 0)
  38. break;
  39. case '2':
  40. cube.position.set(2, 2, 2)
  41. break;
  42. }
  43. })
  44. }
  45. 注意:在创建gui工具中需要使用到物体,所以要在创建完物体后调这个方法

案例-五颜六色立方体

修改创建立方体材质函数

  1. function createCube() {
  2. const geometry = new THREE.BoxGeometry(1, 1, 1);
  3. // 1. 定义颜色数组(x 正负,y 正负,z 正负)
  4. const colorArr = ['red', 'green', 'blue', 'pink', 'orange', 'write']
  5. // 2. 每个颜色字符串映射成材质对象
  6. const materialArr = colorArr.map(colorStr => {
  7. return new THREE.MeshBasicMaterial({
  8. color: colorStr
  9. })
  10. })
  11. // 3. 把材质数组传入 Mesh 构造新的物体
  12. cube = new THREE.Mesh(geometry, materialArr)
  13. // 把物体加入到场景中
  14. scene.add(cube);
  15. }

 案例-多个立方体

修改创建立方体材质函数-思路:数据影响视图

数据包含:立方体的颜色(color),立方体的大小(whd) ,立方体的位置(xyz)

扩展:

Math.floor();   向下取整  Math.floor(1.6);//1

Math.random(); 生成一个大于0.0,小于1的随机数,不包括1.0,即[0.0,1.0)

  1. function createCube() {
  2. // 1. 定义数据对象,描绘每个立方体的信息
  3. const cubeIndolist = []
  4. for (let i = 0; i < 5; i++) {
  5. cubeIndolist.push({
  6. color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)},${Math.floor(Math.random() * (255 - 0 + 1) + 0)},${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
  7. w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  8. h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  9. d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  10. x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  11. y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  12. z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  13. })
  14. }
  15. // 2. 针对每个数据对象,创建物体
  16. cubeIndolist.map(cubeObj => {
  17. const { color, w, h, d, x, y, z } = cubeObj
  18. const geometry = new THREE.BoxGeometry(w, h, d);
  19. const material = new THREE.MeshBasicMaterial({ color });
  20. cube = new THREE.Mesh(geometry, material)
  21. cube.position.set(x, y, z)
  22. scene.add(cube);
  23. })
  24. }

性能监视器的使用

后续考虑3d场景的性能优化

1.单独引入 Stats 附加组件

2.创建性能监视器

3.设置监视器面板类型(0, 1, 2)

4.设置监视器位置并添加 DOM

  1. // 1. 单独引入 stats 组件
  2. import Stats from 'three/examples/jsm/libs/stats.module.js'
  3. // 性能监视器
  4. let stats
  5. function renderLoop() {
  6. renderer.render(scene, camera)
  7. controls.update()
  8. // 性能监视器数值不断更新
  9. stats.update()
  10. requestAnimationFrame(renderLoop)
  11. }
  12. function createStats() {
  13. // 2. 创建性能监视器
  14. stats = new Stats()
  15. // 3. 设置监视器面板类型(0:fps-每秒传输帧数,1:ms-每帧刷新用时,2:mb-内存占用)
  16. stats.setMode(0)
  17. // 4. 设置监视器位置并添加 DOM
  18. stats.domElement.style.position = 'fixed'
  19. stats.domElement.style.left = '0'
  20. stats.domElement.style.top = '0'
  21. document.body.appendChild(stats.domElement)
  22. }
  23. /**准备内容--S */
  24. createStats() //创建性能监视器
  25. renderLoop() // 渲染循环
  26. /**准备内容--E */

删除物体

如何废置对象(How to dispose of objects)

内存泄漏:比如有一些对象或者物体在浏览器的计算机内存当中,但是我们的变量可能不在指向它了,但是它依旧占用这个内存,就会越叠越多,意外的全局变量、遗忘的定时器、 使用不当的闭包、遗漏的 DOM 元素、网络回调等都会造成内存泄漏。

  1. //在创建立方体函数中给每个立方体起个名字 cube.name = 'cu'
  2. function removeCube() {
  3. // 1. 给 window 双击绑定事件举例删除
  4. window.addEventListener('dblclick', () => {
  5. // 2. 调用 three.js 相关废置函数
  6. const arr = scene.children.filter(obj => obj.name === 'cu')
  7. const cube = arr[0]
  8. if (cube) {
  9. cube.geometry.dispose() // 移除图形数据
  10. cube.material.dispose() // 移除材质数据
  11. // 3. 再从场景中移除物体
  12. scene.remove(cube)
  13. }
  14. })
  15. }

three.js 物体分组管理

three.js物理分组详解它几乎和Object3D是相同的,其目的是使得组中对象在语法上的结构更加清晰。three.js物理分组详解

1.新建分组
2.分组中加入物体
3.把分组加入到场景中

  1. // 1. 新建分组
  2. let group = new THREE.Group()
  3. function createCube() {
  4. const cubeInfoArr = []
  5. for (let i = 0; i < 2500; i++) {
  6. cubeInfoArr.push({
  7. color: `rgb(${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)}, ${Math.floor(Math.random() * (255 - 0 + 1) + 0)})`,
  8. w: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  9. h: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  10. d: Math.floor(Math.random() * (3 - 1 + 1) + 1),
  11. x: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  12. y: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  13. z: Math.floor(Math.random() * (5 - -5 + 1) + -5),
  14. })
  15. }
  16. cubeInfoArr.map(cubeObj => {
  17. const { color, w, h, d, x, y, z } = cubeObj
  18. const geometry = new THREE.BoxGeometry(w, h, d)
  19. const material = new THREE.MeshBasicMaterial({ color })
  20. const cube = new THREE.Mesh(geometry, material)
  21. cube.position.set(x, y, z)
  22. cube.name = 'cu'
  23. // 2. 分组中加入物体
  24. group.add(cube)
  25. })
  26. // 3. 把分组加入到场景中
  27. scene.add(group)
  28. }
  29. function removeCube() {
  30. window.addEventListener('dblclick', () => {
  31. group.children.map(obj => {
  32. obj.geometry.dispose()
  33. obj.material.dispose()
  34. group.remove(obj)
  35. })
  36. // 把组对象移除掉
  37. scene.remove(group)
  38. })
  39. }

多种缓冲几何体

  1. function createGeometry() {
  2. // 1. 创建几何图形
  3. // 圆形平面:半径和三角形段数
  4. const circleGeo = new THREE.CircleGeometry(1, 32)
  5. // 平面
  6. const planeGeo = new THREE.PlaneGeometry(1, 1)
  7. // 球体 (半径,水平分三角形段数,垂直三角形段数)
  8. const sphereGeo = new THREE.SphereGeometry( 1, 32, 16)
  9. // 2. 创建网格材质 (side 设置哪一面渲染)
  10. const material = new THREE.MeshBasicMaterial({ color: 0xffff00, side: THREE.DoubleSide })
  11. // 3. 创建网格对象
  12. const circle = new THREE.Mesh(circleGeo, material)
  13. const plane = new THREE.Mesh(planeGeo, material)
  14. plane.position.set(-2, -2, -2)
  15. const sphere = new THREE.Mesh(sphereGeo, material)
  16. sphere.position.set(2, 2, 2)
  17. scene.add(circle)
  18. scene.add(plane)
  19. scene.add(sphere)
  20. }

创建点物体

注意:浏览three文档时,图形是图形,材质和物体要结合使用

  1. function createSphere() {
  2. // 1. 创建几何图形
  3. const geometry = new THREE.SphereGeometry(1, 32, 16)
  4. // 2. 创建点材质
  5. const material = new THREE.PointsMaterial({ color: 0x6600ff, size: 0.05 })
  6. // 3. 创建点物体
  7. const points = new THREE.Points(geometry, material)
  8. scene.add(points)
  9. }

线物体和材质

把球状的点物体用线连接起来

  1. function createLine() {
  2. // 1. 创建几何图形
  3. const geometry = new THREE.SphereGeometry(1, 32, 16)
  4. // 2. 创建线材质
  5. const material = new THREE.LineBasicMaterial({
  6. color: 0x6600ff
  7. })
  8. // 3. 创建线物体
  9. const line = new THREE.Line(geometry, material)
  10. scene.add(line)
  11. }

three.js线物体区别

查文档对比 three.js-line物体文档 docs

线(Line): 一条连续的线

环线(LineLoop): 一条从头链接到尾的闭合线

线段(LineSegments) :按顺序一对点链接一条线

 全景图贴图

  1. // 全景图贴图
  2. function createMap() {
  3. // 1. 创建球体几何图形
  4. const geometry = new THREE.SphereGeometry(1, 32, 16)
  5. // 2. 使用纹理加载器并创建网格材质对象
  6. const texture = new THREE.TextureLoader().load('/src/image/earth.png');
  7. // 立即使用纹理进行材质创建(map: 颜色贴图)
  8. const material = new THREE.MeshBasicMaterial({ map: texture });
  9. // 3. 创建网格物体
  10. const sphere = new THREE.Mesh(geometry, material)
  11. scene.add(sphere)
  12. }

立方体贴图

  1. function createCubeMap() {
  2. // 1. 创建立方缓冲几何体
  3. const geometry = new THREE.BoxGeometry(1, 1, 1)
  4. // 2. 加载不同纹理图片并创建材质对象 6
  5. const imgUrlArr = ['posx.jpg', 'negx.jpg', 'posy.jpg', 'negy.jpg', 'posz.jpg', 'negz.jpg']
  6. // 纹理加载器
  7. const textureLoader = new THREE.TextureLoader()
  8. // 设置当前纹理加载器公共的基础路径
  9. textureLoader.setPath('image/park/')
  10. // 遍历图片地址,映射成纹理材质对象
  11. const materialArr = imgUrlArr.map(imgUrl => {
  12. // 创建纹理图片对象
  13. const texture = textureLoader.load(imgUrl)
  14. // three.js 颜色通道为 rgb 颜色(为了防止图片太浅)
  15. texture.colorSpace = THREE.SRGBColorSpace
  16. return new THREE.MeshBasicMaterial({
  17. map: texture
  18. })
  19. })
  20. // 3. 创建网格物体并加入场景
  21. const cube = new THREE.Mesh(geometry, materialArr)
  22. scene.add(cube)
  23. }

全景公园

做法:

1.调整摄像机位置到立方体贴图的盒子中间

2.调整立方体沿着 z 轴做﹣1 缩小(镜面翻转)

3,所有的图形默认都是单面渲染的,所以需要给材质加上双面渲染的配置

  side: THREE.DoubleSide 

  1. // 1. 调整摄像机位置到盒子中间
  2. // 不能给 0 的原因:轨道控制器内部会取出摄像机初始位置坐变化
  3. camera.position.z = 0.1
  4. const cube = new THREE.Mesh(geometry, materialArr)
  5. // 2. 调整立方体沿着 z 轴做 -1 缩小(镜面翻转)
  6. cube.scale.set(1, 1, -1)
  7. scene.add(cube)
  8. // 3. 设置双面
  9. const materialArr = imgUrlArr.map(imgUrl => {
  10. // 创建纹理图片对象
  11. const texture = textureLoader.load(imgUrl)
  12. // three.js 颜色通道为 rgb 颜色(为了防止图片太浅)
  13. texture.colorSpace = THREE.SRGBColorSpace
  14. return new THREE.MeshBasicMaterial({
  15. map: texture,
  16. side: THREE.DoubleSide
  17. })
  18. })
  19. //

视频纹理

1.创建平面网格物体

2.物体材质使用视频纹理

  1. function createPlaneMap() {
  2. // 1. 创建平面几何物体
  3. // 2. 创建并设置视频纹理贴图
  4. const geometry = new THREE.PlaneGeometry(1, 0.5)
  5. // 视频纹理
  6. // 准备视频标签
  7. const video = document.createElement('video')
  8. video.src = 'video/mouse_cat.mp4'
  9. video.muted = true // 静音
  10. video.addEventListener('loadedmetadata', () => { // 加载视频完成
  11. video.play() // 开始播放视频
  12. })
  13. // 创建视频纹理对象
  14. const texture = new THREE.VideoTexture(video)
  15. // 把视频纹理->贴到材质上
  16. const material = new THREE.MeshBasicMaterial({ map: texture })
  17. // 创建物体
  18. const plane = new THREE.Mesh(geometry, material)
  19. scene.add(plane)
  20. // 点击按钮->播放声音
  21. const button = document.createElement('button')
  22. button.innerHTML = '播放'
  23. button.style.position = 'fixed'
  24. button.style.left = '0'
  25. button.style.bottom = '0'
  26. document.body.appendChild(button)
  27. button.addEventListener('click', () => {
  28. video.muted = false // 关闭静音
  29. })
  30. }

CSS3D 渲染器

1.准备原生 DOM 标签和内容样式

2.引入 CSS3DObject 和 CSS3DRenderer 进行渲染

注意:3D 渲染器是覆盖在 WebGLRenderer 之上的一层

基于 CSS3DRenderer 会设置单独的一个 div 容器用于加载显示旋转位移缩放标签物体

基于 CSS3DObject 把获取/创建的 DOM,转成 three.js 的 3D 物体

  1. // 目标:three.js 3D 渲染器,把原生 DOM 标签加入到 3D 场景中
  2. // 1. 准备原生 DOM 标签
  3. // 2. 引入 CSS3DObject, CSS3DRenderer 并把 DOM 转换成 3D 物体并加入到场景中
  4. // 重要:CSS3DRenderer 是一个新的渲染器,需要在渲染循环调用并适配
  5. import * as THREE from 'three'
  6. import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
  7. import { CSS3DObject, CSS3DRenderer } from 'three/addons/renderers/CSS3DRenderer.js'
  8. let scene, camera, renderer, labelRenderer // 标签渲染器
  9. let controls
  10. function init() {
  11. scene = new THREE.Scene()
  12. camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
  13. camera.position.z = 5
  14. renderer = new THREE.WebGLRenderer({ antialias: true })
  15. renderer.setSize(window.innerWidth, window.innerHeight)
  16. document.body.append(renderer.domElement)
  17. }
  18. function controlsCreate() {
  19. controls = new OrbitControls(camera, renderer.domElement)
  20. }
  21. function renderLoop() {
  22. renderer.render(scene, camera)
  23. // 也要让 DOM 渲染器不断更新不同角度的最新画面
  24. labelRenderer.render(scene, camera)
  25. requestAnimationFrame(renderLoop)
  26. }
  27. function createHelper() {
  28. const axesHelper = new THREE.AxesHelper(5)
  29. scene.add(axesHelper)
  30. }
  31. function domTo3D() {
  32. // 1. 准备原生 DOM 标签
  33. const tag = document.createElement('span')
  34. tag.innerHTML = '我是文字'
  35. tag.style.color = 'white'
  36. // 2. 引入 CSS3DObject, CSS3DRenderer 并把 DOM 转换成 3D 物体并加入到场景中
  37. // 原生标签的 px 的值会平移到 3d 空间中作为单位
  38. const tag3d = new CSS3DObject(tag)
  39. tag3d.scale.set(1 / 16, 1 / 16, 1 / 16)
  40. scene.add(tag3d)
  41. labelRenderer = new CSS3DRenderer()
  42. labelRenderer.setSize(window.innerWidth, window.innerHeight)
  43. labelRenderer.domElement.style.pointerEvents = 'none' // 在什么条件下让标签触发
  44. 鼠标交互事件(这里就是最上面的一层(3D渲的div)需要满足没有任何条件下的交互,此
  45. 时场景不让3D渲染的上层div跟鼠标有任何的交互,这里就类似于css所谓的禁止点击了,没有
  46. 任何交互就没有事件的产生,不让它阻拦鼠标穿透过去触摸canvas,因为轨道控制器监测的是
  47. canvas的鼠标事件)
  48. labelRenderer.domElement.style.position = 'fixed'
  49. labelRenderer.domElement.style.left = '0'
  50. labelRenderer.domElement.style.top = '0'
  51. document.body.appendChild(labelRenderer.domElement)
  52. // 重要:CSS3DRenderer 是一个新的渲染器,需要在渲染循环调用并适配
  53. }
  54. // 初始化
  55. init()
  56. // 轨道控制器
  57. controlsCreate()
  58. // 坐标轴
  59. createHelper()
  60. // DOM 转 3D
  61. domTo3D()
  62. // 渲染循环
  63. renderLoop()

鼠标事件

目标:与 3D 物体进行鼠标交互

类型1:空间中的原生 DOM交互 - 支持原生事件(设置 pointerEvents = ‘all’)

类型2:three.js 物体交互 - 使用光射投影 Raycaster

核心:鼠标位置归一化为设备坐标,配合摄像机计算收集鼠标移过哪些物体

公式:

x 点坐标:(浏览器 x 轴坐标点 / 画布宽度) * 2 - 1

y 点坐标:- (浏览器 y 轴坐标点 / 画布高度) * 2 + 1

类型1交互:

  1. function domTo3D() {
  2. const tag = document.createElement('span')
  3. tag.innerHTML = '立方体'
  4. tag.style.color = 'white'
  5. // 类型1:原生 DOM 使用原生的事件绑定(设置 pointerEvents='all')
  6. tag.style.pointerEvents = 'all'
  7. tag.addEventListener('click', e => {
  8. alert('dom 被点击了')
  9. e.stopPropagation()
  10. })
  11. const tag3d = new CSS3DObject(tag)
  12. tag3d.scale.set(1 / 32, 1 / 32, 1 / 32)
  13. tag3d.position.set(0, 1, 0)
  14. scene.add(tag3d)
  15. labelRenderer = new CSS3DRenderer()
  16. labelRenderer.setSize(window.innerWidth, window.innerHeight)
  17. labelRenderer.domElement.style.pointerEvents = 'none'
  18. labelRenderer.domElement.style.position = 'fixed'
  19. labelRenderer.domElement.style.left = '0'
  20. labelRenderer.domElement.style.top = '0'
  21. document.body.appendChild(labelRenderer.domElement)
  22. }

类型2交互:

  1. function bindClick() {
  2.   window.addEventListener('click', e => {
  3.     // 定义光线投射对象
  4.     const raycaster = new THREE.Raycaster()
  5.     // 定义二维向量对象(保存转换后的平面 x,y 坐标值)
  6.     const pointer = new THREE.Vector2()
  7.     // 把屏幕坐标 => WebGL设备坐标
  8.     // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  9.     pointer.x = (e.clientX / window.innerWidth) * 2 - 1
  10.     pointer.y = - (e.clientY / window.innerHeight) * 2 + 1
  11.     // 更新摄像机和鼠标之间的连线(位置)
  12.     raycaster.setFromCamera(pointer, camera)
  13.     // 获取这条线穿过了哪些物体,收集成一个数组
  14.     const list = raycaster.intersectObjects(scene.children)
  15.     console.log(list)
  16.   })
  17. }

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号