threejs 实现3d柱状图

import { useEffect, useRef } from 'react';
import { BoxGeometry, Mesh, AmbientLight, MeshPhongMaterial, MeshLambertMaterial, PerspectiveCamera, Scene, WebGLRenderer, MeshBasicMaterial, Color, BufferAttribute, OrthographicCamera, WireframeGeometry, Group, LineSegments, LineBasicMaterial, Side, DoubleSide, FrontSide, BackSide, PointLight, DirectionalLight, Vector2, Raycaster, Event, Object3D, Vector3, ShaderMaterial, MeshToonMaterial } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';

export default function HomePage() {
    const container = useRef < HTMLDivElement > (null)
    useEffect(() => {
        let INTERSECTED: Mesh | null = null
        const pointer = new Vector2()
        const raycaster = new Raycaster();
        const WIDTH = container.current ? container.current.clientWidth : 100
        const HEIGHT = container.current ? container.current.clientHeight : 100
        const data = [
            [20 + 10, 10],
            [50 + 10, 10],
            [60 + 10, 10],
            [40 + 10, 60],
            [40 + 10, 60],
            [40 + 10, 60],
        ]

        let maxY = 0n
        data.forEach(d => {
            if (d[0] + d[1] > maxY) {
                maxY = d[0] + d[1]
            }
        })

        const scene = new Scene()
        const light = new DirectionalLight(0xffffff, 0.6)
        light.lookAt(0, 0, 0)
        light.position.setX(WIDTH + 300)
        light.position.setY(0)
        light.position.setZ(380)
        scene.add(light);

        const light2 = new DirectionalLight(0xffffff, 0.6)
        light2.lookAt(0, 0, 0)
        light2.position.setX(0)
        light2.position.setY(HEIGHT + 400)
        light2.position.setZ(400)
        scene.add(light2);

        const light3 = new AmbientLight(0x404040); // soft white light
        scene.add(light3);

        const barWidth = Math.floor(WIDTH / data.length * 0.691 * 0.70721)
        data.forEach((d, i) => {
            const group = new Group()
            const box1 = new BoxGeometry(barWidth, HEIGHT / maxY * d[0], barWidth, 1, 1, 1)
            const box2 = new BoxGeometry(barWidth, HEIGHT / maxY * d[1], barWidth, 1, 1, 1)true, shadowSide: BackSide, side: DoubleSide, wireframe: false
        })
        const material1 = new MeshLambertMaterial({
            transparent: true,
            opacity: 0.91
        })
        box1.computeBoundingBox();
        box2.computeBoundingBox();
        const count = box1.attributes.position.count
        const color1 = new Color()
        const color2 = new Color()
        box1.setAttribute('color', new BufferAttribute(new Float32Array(count * 3), 3));
        box2.setAttribute('color', new BufferAttribute(new Float32Array(count * 3), 3));
        const colors1 = box1.attributes.color
        const colors2 = box2.attributes.color
        for (let i = 0; i < count; i++) {
            const x = box1.attributes.position.getX(i)
            const y = box1.attributes.position.getY(i)
            const z = box1.attributes.position.getZ(i)
            console.log(i, x, y, z)
            if (y > 0) {
                color1.setHex(0x1C8CFF)
                color2.setHex(0x00FFC2)
            } else {
                color1.setHex(0x093783)
                color2.setHex(0x117B5C)
            }
            colors1.setXYZ(i, color1.r, color1.g, color1.b)
            colors2.setXYZ(i, color2.r, color2.g, color2.b)
        }
        const boxMesh1 = new Mesh(box1, material1)
        const boxMesh2 = new Mesh(box2, material1.clone())
        boxMesh1.morphTargetInfluences
        boxMesh2.position.y = HEIGHT / maxY * d[0] / 2 + HEIGHT / maxY * d[1] / 2
        group.add(boxMesh1)
        group.add(boxMesh2)
        group.position.x = i * WIDTH / data.length + (WIDTH / data.length / 2)
        group.position.y = HEIGHT / maxY * d[0] / 2
        group.position.z = -barWidth / 2
        group.rotateY(-Math.PI / 4)
        scene.add(group)
    })
    const camera = new OrthographicCamera(0, WIDTH, HEIGHT, 0)
    camera.position.z = 1000
    camera.position.y = 412
    camera.rotateX(-Math.PI / 8)
    const renderer = new WebGLRenderer({ antialias: true, alpha: true })
    renderer.setSize(WIDTH, HEIGHT)
    if (container.current) {
        container.current.childNodes.forEach(child => {
            child.remove()
        })
        container.current.appendChild(renderer.domElement)
    }
    const controls = new OrbitControls(camera, renderer.domElement)
    const rayCastOrigin = new Vector3(WIDTH / 2, HEIGHT, 800)
    function animate() {
        requestAnimationFrame(animate)
        rayCastOrigin.transformDirection
        raycaster.setFromCamera(pointer, camera)
        const intersects = raycaster.intersectObjects(scene.children, true);

        if (intersects.length > 0) {
            if (INTERSECTED != intersects[0].object) {
                if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);
                INTERSECTED = intersects[0].object as Mesh;
                INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
                INTERSECTED.material.emissive.setHex(0x444444);

            }

        } else {

            if (INTERSECTED) INTERSECTED.material.emissive.setHex(INTERSECTED.currentHex);

            INTERSECTED = null;

        }
        controls.update()
        renderer.render(scene, camera)
    }
    animate()
    if (container.current) {
        container.current.addEventListener('pointermove', e => {
            pointer.x = (e.offsetX - WIDTH / 2) / (WIDTH / 2)
            pointer.y = (e.offsetY - HEIGHT / 2) / (HEIGHT / 2) * -1
        })
    }
}, [])
return (
    <div ref={container} className={styles.container}>

    </div>
);
}

 

posted @ 2022-08-28 15:27  SKY_VIEW  阅读(804)  评论(0编辑  收藏  举报