需要剖析
因为业务外面须要可能渲染上传后的3D模型数据,本意是须要依据不同的数据格式,反对不同的渲染的,因而想到了应用 three.js
来实现这个性能,原本想间接在网上借鉴一下大佬的内容,因为咱们的技术选型是 React
,所以一开始还尝试应用 github
上的一些他人配置好的 react-three
这样的,然而如同没有我须要的那种,只好本人手撸。
实现形式
其实整体的还是依据 three官网文档 下面的参数进行设置,不多废话间接上代码:
引入组件
import React, { Component, Fragment } from 'react'import * as THREE from 'three'// import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader'import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'// import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader'import FileService from '@/services/FileService'import './styles.less'
内容渲染
class Online3DView extends Component { constructor(props) { super(props) this.state = { isModel: false, currentName: '暂无名字', clientX: 0, clientY: 0, } this.threeRef = React.createRef() } componentDidMount() { const { height, width, fileId } = this.props let that = this // 加载要渲染的文件的数据流(Blob) FileService.downloadForPreview(fileId) .then((res) => { const url = window.URL.createObjectURL(res) // todo 初始化场景 const scene = new THREE.Scene() // todo 加载相机 const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 80) camera.position.set(1, 25, 25) camera.lookAt(new THREE.Vector3(0, 0, 0)) //todo 加载光线 const ambLight = new THREE.AmbientLight(0x404040, 1) const pointLight = new THREE.PointLight(0x404040, 0.8) const directionalLight = new THREE.DirectionalLight(0xffffff, 1) pointLight.position.set(100, 10, 0) pointLight.receiveShadow = true scene.add(ambLight) scene.add(pointLight) scene.add(directionalLight) //todo renderer const renderer = new THREE.WebGLRenderer({ antialias: true, }) renderer.setSize(width, height - 10) renderer.setClearColor(0xb9d3ff, 1) // 这里应用哪一种loader就要构建相应的loader,我这里应用了glb文件类型,所以加载了这个 let glbLoader = new GLTFLoader() glbLoader.load(url, function (glTF) { glTF.scene.traverse(function (child) { if (glTF.isMesh) { glTF.frustumCulled = false //模型暗影 glTF.castShadow = true //模型自发光 glTF.material.emissive = glTF.material.color glTF.material.emissiveMap = glTF.material.map } }) scene.add(glTF.scene) // todo 场景控制器初始化 const controls = new OrbitControls(camera, renderer.domElement) controls.enabled = true // 鼠标管制是否可用 // 是否主动旋转 controls.autoRotate = true controls.autoRotateSpeed = 0.05 //是否可旋转,旋转速度(鼠标左键) controls.enableRotate = true controls.rotateSpeed = 0.3 //controls.target = new THREE.Vector();//摄像机聚焦到某一个点 //最大最小相机挪动间隔(景深相机) controls.minDistance = 10 controls.maxDistance = 100 //最大仰视角和仰视角 controls.minPolarAngle = Math.PI / 4 // 45度视角 controls.maxPolarAngle = Math.PI / 1 // 75度视角 //惯性滑动,滑动大小默认0.25 controls.enableDamping = true controls.dampingFactor = 0.25 //是否可平移,默认挪动速度为7px controls.enablePan = true controls.panSpeed = 0.5 //controls.screenSpacePanning = true; //滚轮缩放管制 controls.enableZoom = true controls.zoomSpeed = 1.5 //程度方向视角限度 //controls.minAzimuthAngle = -Math.PI/4; //controls.maxAzimuthAngle = Math.PI/4; //todo 绑定到类上 that.scene = scene that.camera = camera that.renderer = renderer that.controls = controls //鼠标移入和移出事件高亮显示选中的模型 that.currentObjectColor = null //移入模型的色彩 that.currentObject = null //鼠标移入的模型 // 初始化场景 // 加载到dom元素上 that.threeRef.current.appendChild(that.renderer.domElement) that.start() that.resizeFunc1() that.resizeFunc2() }) }) .catch((err) => {}) window.addEventListener('resize', this.resizeFunc1, false) window.addEventListener('resize', this.resizeFunc2, false) } componentWillUnmount() { this.stop() this.renderer && this.threeRef.current.removeChild(this.renderer.domElement) window.removeEventListener('resize', this.resizeFunc1, false) window.removeEventListener('resize', this.resizeFunc2, false) } // 初始化 start = () => { if (!this.frameId) { this.frameId = requestAnimationFrame(this.animate) } } // 卸载组件的时候去除 stop = () => { cancelAnimationFrame(this.frameId) } // 更新状态 animate = () => { this.controls.update() this.renderScene() this.frameId = requestAnimationFrame(this.animate) } renderScene = () => { this.renderer.render(this.scene, this.camera) } closeModel = (e) => { e.stopPropagation() if (this.controls && !this.controls.autoRotate) { this.controls.autoRotate = true } this.setState({ isModel: false, }) } resizeFunc1 = () => { this.controls.update() } resizeFunc2 = (e) => { const dom = document.getElementById('test_three') const { offsetWidth, offsetHeight } = dom this.camera.aspect = offsetWidth / offsetHeight this.camera.updateProjectionMatrix() this.renderer.setSize(offsetWidth, offsetHeight) } render() { return ( <Fragment> <div className={this.props.className || 'three-component'} id="test_three" ref={this.threeRef} /> </Fragment> ) }}
应用他
<Online3DView height={600} width={1150} fileId={record.mediaFileId} />