需要剖析
因为业务外面须要可能渲染上传后的 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} />