关于react.js:threejs-实践3D渲染

4次阅读

共计 4208 个字符,预计需要花费 11 分钟才能阅读完成。

需要剖析

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