three初体验
three我的理解就是:先创建一个 鱼缸(scene),需要什么东西,就往鱼缸里加
scene场景
场景里面有背景,就加 this.scene.background
要添加模型,this.scene.add(模型)
要有标记点,就创建一个精灵标记点 this.scene.add(精灵标记)
等等
重点是 initThreeJS() 函数和 animate()函数
<div ref="canvasContainer" id="canvas-container"></div>
引入three和tween
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import TWEEN from '@tweenjs/tween.js'
// data:
camera: null, // 相机
scene: null, // 场景
renderer: null, // 渲染器
gltf: null, // GLTF模型
controls: null, // 轨道控制器
mounted() {// 绑定 animate 方法到当前实例,防止 this 丢失this.animate = this.animate.bind(this)// 初始化Three.js场景this.initThreeJS()
},
// 在页面销毁前,删除所有的三维实例和注册的..
beforeDestroy() {// 清理Three.js资源, 包括渲染器、场景和模型, 标记, 轨迹等if (this.renderer) {if (this.renderer.forceContextLoss) {this.renderer.forceContextLoss()}this.renderer.domElement = nullthis.renderer = null}if (this.scene) {this.scene.clear()this.scene = null}if (this.gltf) {if (this.gltf.scene && this.scene) {this.scene.remove(this.gltf.scene)}this.gltf = null}this.stopAnimate = true // 停止动画window.removeEventListener('resize', this.onWindowResize)
},
// methods:
initThreeJS() {// 初始化场景、相机、渲染器等this.scene = new THREE.Scene()this.camera = new THREE.PerspectiveCamera(50,window.innerWidth / window.innerHeight,0.1,100000000)this.renderer = new THREE.WebGLRenderer({ antialias: true })this.renderer.setSize(window.innerWidth, window.innerHeight)this.$el.querySelector('#canvas-container').appendChild(this.renderer.domElement)// 添加光源和环境const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)this.scene.add(ambientLight)const directionalLight1 = new THREE.DirectionalLight(0xffffff, 10)directionalLight1.position.set(10, 20, 5)this.scene.add(directionalLight1)const directionalLight2 = new THREE.DirectionalLight(0xffffff, 10)directionalLight2.position.set(-10, -20, -5)this.scene.add(directionalLight2)// 添加天空背景图const textureLoader = new THREE.TextureLoader()const texture = textureLoader.load('本地天空图片')// 设置纹理this.scene.background = texture// 初始化轨道控制器this.controls = new OrbitControls(this.camera, this.renderer.domElement)this.controls.minPolarAngle = 0this.controls.maxPolarAngle = Math.PI / 2this.controls.maxDistance = 1000// 加载模型const loader = new GLTFLoader()// let model = nullloader.load('本地模型 xxx.glb',(gltf) => {this.gltf = gltfthis.scene.add(gltf.scene)this.animate()this.loading = false},undefined,(error) => {console.error(`Error loading model: ${error}`)})// 设置初始相机位置this.camera.position.set(0, 40, 100)this.controls.target.set(20, 10, 5)this.controls.update()this.stopAnimate = false // 启动动画// 监听窗口大小变化window.addEventListener('resize', this.onWindowResize, false)
},
// 添加人员用户标记(做沿轨迹动画)
addUserMarker() {const spriteMap = new THREE.TextureLoader().load('http://172.16.9.115:5304/images/user.svg') // 人物标记图片const spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap })this.userMarker = new THREE.Sprite(spriteMaterial)this.scene.add(this.userMarker)const start = this.pathPoints[0]// y 轴偏移 高度 + 1this.userMarker.position.set(start.x, start.y + 6, start.z)this.userMarker.scale.set(9, 12, 1)
},
onWindowResize() {this.camera.aspect = window.innerWidth / window.innerHeightthis.camera.updateProjectionMatrix()this.renderer.setSize(window.innerWidth, window.innerHeight)
},
// 要不停的调用
animate() {if (this.stopAnimate) return // 如果停止动画,则直接返回requestAnimationFrame(this.animate)TWEEN.update()this.controls.update()// 以下逻辑(在场景中创建一个平铺在地面的轨迹路径,创建一个人员精灵标记,// 然后让精灵标记沿路径做循环动画)。不需要可以不用管// 轨迹函数在我另一篇博客中// === 新增:让 userMarker 沿路径循环运动 ===// if (this.userMarker && this.curve) {// this.userT +=// (this.userSpeed * this.clock.getDelta()) / this.curve.getLength()// if (this.userT > 1) this.userT = 0// const pos = this.curve.getPointAt(this.userT)// this.userMarker.position.set(pos.x, pos.y + 6, pos.z)// // 让精灵朝向运动方向// const tangent = this.curve.getTangentAt(this.userT)// // const axis = new THREE.Vector3(0, 1, 0)// const angle = Math.atan2(tangent.x, tangent.z)// this.userMarker.rotation.set(0, angle, 0)// }this.renderer.render(this.scene, this.camera)
},