需要剖析

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