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>
);
}
相信世界是平的
谨记四个字“修身养性”
大江东去浪淘尽英雄,再牛B的人物最后也是一掊土
向善不是目的,而是抚慰心灵,更多的感受幸福,感谢别人给你行善的机会
相信老子的话:万物生于有,有生于无,一切的道理都源于一个无法证明的假设
我是好是坏就自然而然的摆在那里,并不会因为别人的评价而改变什么,我也不需要别人用一张纸来说明我什么,世间最难得的是自由
谨记四个字“修身养性”
大江东去浪淘尽英雄,再牛B的人物最后也是一掊土
向善不是目的,而是抚慰心灵,更多的感受幸福,感谢别人给你行善的机会
相信老子的话:万物生于有,有生于无,一切的道理都源于一个无法证明的假设
我是好是坏就自然而然的摆在那里,并不会因为别人的评价而改变什么,我也不需要别人用一张纸来说明我什么,世间最难得的是自由
支持大额赞助: