Files
newToy/src/views/Color.vue
2025-11-23 23:55:10 +08:00

125 lines
3.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="color-page">
<div class="color-header">
<h1>三维空间色彩粒子</h1>
<p>这是从旧项目抽离出来的第一个实验场景后续可以继续补充更多交互与玩法</p>
</div>
<div class="color-canvas" ref="canvasRoot"></div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref } from 'vue'
import * as THREE from 'three'
const canvasRoot = ref<HTMLDivElement | null>(null)
let renderer: THREE.WebGLRenderer | null = null
let scene: THREE.Scene | null = null
let camera: THREE.PerspectiveCamera | null = null
let animationId = 0
onMounted(() => {
if (!canvasRoot.value) return
const container = canvasRoot.value
const width = container.clientWidth
const height = container.clientHeight
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true })
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
container.appendChild(renderer.domElement)
scene = new THREE.Scene()
camera = new THREE.PerspectiveCamera(55, width / height, 0.1, 2000)
camera.position.set(0, 0, 380)
const geometry = new THREE.IcosahedronGeometry(1, 3)
const group = new THREE.Group()
const gridSize = 64
const spacing = 8
for (let x = -gridSize; x <= gridSize; x += spacing) {
for (let y = -gridSize; y <= gridSize; y += spacing) {
const zLayerCount = 3
for (let zIndex = 0; zIndex < zLayerCount; zIndex++) {
if (Math.random() > 0.55) continue
const color = new THREE.Color()
color.setHSL(Math.random(), 0.7, Math.random() * 0.2 + 0.2)
const material = new THREE.MeshBasicMaterial({ color })
const mesh = new THREE.Mesh(geometry, material)
mesh.position.set(
x,
y + (Math.random() - 0.5) * 10,
zIndex * 18 + (Math.random() - 0.5) * 20,
)
const s = Math.random() * 3 + 1.2
mesh.scale.setScalar(s)
group.add(mesh)
}
}
}
scene.add(group)
const clock = new THREE.Clock()
const animate = () => {
animationId = requestAnimationFrame(animate)
const t = clock.getElapsedTime()
group.rotation.y = t * 0.16
group.rotation.x = Math.sin(t * 0.3) * 0.35
if (camera) {
camera.position.z = 360 + Math.sin(t * 0.5) * 40
camera.lookAt(0, 0, 0)
}
renderer?.render(scene as THREE.Scene, camera as THREE.PerspectiveCamera)
}
animate()
const onResize = () => {
if (!renderer || !camera) return
const w = container.clientWidth
const h = container.clientHeight
renderer.setSize(w, h)
camera.aspect = w / h
camera.updateProjectionMatrix()
}
window.addEventListener('resize', onResize)
onBeforeUnmount(() => {
cancelAnimationFrame(animationId)
window.removeEventListener('resize', onResize)
if (renderer) {
renderer.dispose()
}
if (scene) {
scene.traverse((obj) => {
if ((obj as THREE.Mesh).geometry) {
;(obj as THREE.Mesh).geometry.dispose()
}
if ((obj as THREE.Mesh).material) {
const mat = (obj as THREE.Mesh).material
if (Array.isArray(mat)) {
mat.forEach((m) => m.dispose())
} else {
mat.dispose()
}
}
})
}
if (canvasRoot.value && renderer) {
canvasRoot.value.removeChild(renderer.domElement)
}
})
})
</script>