THREE--demo15(拖放控制器)

效果

THREE--demo15(拖放控制器)_第1张图片

源码

import { useEffect, useRef } from 'react'
import * as T from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { DragControls } from 'three/examples/jsm/controls/DragControls'
import Stats from 'stats.js'
import { GUI } from 'dat.gui'

const Demo15 = () => {
    let stats, scene, camera, renderer, plane, planeGeometry, gui, controls, orbitControls

    useEffect(() => {
        init()
    }, [])

    const ThreeContainer = useRef()

    // 创建界面组件,修改代码中的变量
    const initGui = () => {
        // scene.add,scene.remove,scene.children
        controls = {
            addBox,
            removeBox,
            numberOfObjects: scene.children.length,
            rotationSpeed: 0.01,
        }
        gui = new GUI()
        gui.add(controls, 'addBox')
        gui.add(controls, 'removeBox')
        // listen方法监听属性变化,更新到面板
        gui.add(controls, 'numberOfObjects').listen()
        gui.add(controls, 'rotationSpeed', 0, 0.1)
    }

    // 检测动画运行的帧频
    const initStats = () => {
        stats = new Stats()
        stats.showPanel(1)
        ThreeContainer.current.append(stats.dom)
    }

    // 场景,作为容器,保存并跟踪所有渲染的物体
    const initScene = () => {
        scene = new T.Scene()
    }

    // 相机
    const initCamera = () => {
        // 透视相机,参数:视场,长宽比,近面,远面
        camera = new T.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
        // 正投影相机,参数:左边界,右边界,上边界,下边界,近面,远面
        // camera = new T.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16)
        camera.position.set(-30, 40, 30)
        camera.lookAt(scene.position)
    }

    // 渲染器,计算指定相机角度下,浏览器中scene的样子
    const initRenderer = () => {
        renderer = new T.WebGLRenderer()
        renderer.setClearColor(0xeeeeee)
        renderer.setSize(window.innerWidth, window.innerHeight)
        // 允许阴影映射
        renderer.shadowMap.enabled = true
        // 将render的输出挂载到HTML页面框架中的元素上
        ThreeContainer.current.append(renderer.domElement)
    }

    // 轨道控制器
    const initOrbitControls = () => {
        orbitControls = new OrbitControls(camera, renderer.domElement)
    }

    // 创建平面
    const createPlane = () => {
        planeGeometry = new T.PlaneGeometry(60, 20, 1, 1)
        const planeMaterial = new T.MeshLambertMaterial({
            color: 0xffffff
        })
        plane = new T.Mesh(planeGeometry, planeMaterial)
        plane.rotation.x = -0.5 * Math.PI
        plane.position.set(15, 0, 0)
        // 接受阴影
        plane.receiveShadow = true
        scene.add(plane)
    }

    // 添加光源
    const addLight = () => {
        const spotLight = new T.SpotLight(0xffffff)
        spotLight.position.set(-40, 60, -10)
        spotLight.castShadow = true
        scene.add(spotLight)
    }

    // 渲染场景
    const renderScene = () => {
        stats.update()

        // 可以将一个函数作为参数传给traverse,传递来的函数会在场景中的每一个子对象上调用一次
        scene.traverse(e => {
            // 除平面之外的Mesh对象
            if (e instanceof T.Mesh && e != plane) {

                e.rotation.y += controls.rotationSpeed
            }
        })

        // 以一定的时间间隔进行渲染
        requestAnimationFrame(renderScene)
        renderer.render(scene, camera)
    }

    // 自定义几何体
    const addBox = () => {
        // 缓冲对象
        const geometry = new T.BufferGeometry()
        // 点数组
        const vertices = new Float32Array([
            // 第一个面,一个面由两个三角形组成,一个三角形由三个点组成
            0, 0, 0,
            10, 0, 0,
            0, 10, 0,
            10, 0, 0,
            0, 10, 0,
            10, 10, 0,

            // 第二个面
            // ...
            // 第六个面
            // ...

            // 前六个面构成一个立方体,如果要在立方体上追加一个椎体,再增加四个面
            // 第七个面
            // 第八个面
            // 第九个面
            // 第十个面
        ])
        // BufferAttribute类存储与BufferGeometry关联的属性,第一个参数是保存存储在缓冲区中的数据的数组,第二个参数是保存在数组中的矢量的长度
        const attribute = new T.BufferAttribute(vertices, 3)
        geometry.attributes.position = attribute
        const material = new T.MeshBasicMaterial({
            color: Math.random() * 0xffffff,
            side: T.DoubleSide
        })
        const mesh = new T.Mesh(geometry, material)
        mesh.name = `box-${scene.children.length}`
        scene.add(mesh)
        controls.numberOfObjects = scene.children.length
    }

    // 移除几何体
    const removeBox = () => {
        const allChildren = scene.children
        const lastObject = allChildren[allChildren.length - 1]
        // 检查对象是否为Mesh对象,以防移除相机和光源
        if (lastObject instanceof T.Mesh) {
            scene.remove(lastObject)
            controls.numberOfObjects = scene.children.length
        }
    }

    // 拖放控制器
    const initDragControls = () => {
        const dragControls = new DragControls(scene.children, camera, renderer.domElement)
        dragControls.addEventListener( 'dragstart', function ( event ) {
            orbitControls.enabled = false;
        } );
        dragControls.addEventListener( 'dragend', function ( event ) {
            orbitControls.enabled = true;
        } );
    }

    const resize = () => {
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight
            camera.updateProjectionMatrix()
            renderer.setSize(window.innerWidth, window.innerHeight)
        }, false)
    }

    // 初始化
    const init = () => {
        initStats()
        initScene()
        initCamera()
        initRenderer()
        initOrbitControls()
        createPlane()
        initGui()
        addLight()
        initDragControls()
        renderScene()
        resize()
    }

    return (
        <div ref={ThreeContainer} />
    )
}

export default Demo15

你可能感兴趣的:(three,react,three)