添加了一个修改
This commit is contained in:
124
src/views/Color.vue
Normal file
124
src/views/Color.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user