102 lines
2.9 KiB
Svelte
102 lines
2.9 KiB
Svelte
<script lang="ts">
|
|
import { T, useTask } from '@threlte/core';
|
|
import { onMount } from 'svelte';
|
|
import * as THREE from 'three';
|
|
import { themeColors, mode } from '$lib/stores/theme';
|
|
|
|
interface Props {
|
|
particleCount?: number;
|
|
}
|
|
|
|
let { particleCount = 500 }: Props = $props();
|
|
|
|
let particlesGeometry: THREE.BufferGeometry;
|
|
let particlesMaterial: THREE.PointsMaterial;
|
|
let particles = $state<THREE.Points | null>(null);
|
|
let positions: Float32Array;
|
|
let velocities: Float32Array;
|
|
let time = 0;
|
|
|
|
// Create particles
|
|
function createParticles() {
|
|
positions = new Float32Array(particleCount * 3);
|
|
velocities = new Float32Array(particleCount * 3);
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const i3 = i * 3;
|
|
// Spread particles in a larger area
|
|
positions[i3] = (Math.random() - 0.5) * 20;
|
|
positions[i3 + 1] = (Math.random() - 0.5) * 20;
|
|
positions[i3 + 2] = (Math.random() - 0.5) * 20;
|
|
|
|
// Random velocities
|
|
velocities[i3] = (Math.random() - 0.5) * 0.01;
|
|
velocities[i3 + 1] = (Math.random() - 0.5) * 0.01;
|
|
velocities[i3 + 2] = (Math.random() - 0.5) * 0.01;
|
|
}
|
|
|
|
particlesGeometry = new THREE.BufferGeometry();
|
|
particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
|
|
|
particlesMaterial = new THREE.PointsMaterial({
|
|
size: 0.05,
|
|
sizeAttenuation: true,
|
|
transparent: true,
|
|
opacity: 0.6,
|
|
blending: THREE.AdditiveBlending,
|
|
depthWrite: false
|
|
});
|
|
|
|
particles = new THREE.Points(particlesGeometry, particlesMaterial);
|
|
}
|
|
|
|
onMount(() => {
|
|
createParticles();
|
|
});
|
|
|
|
// Update particle color based on theme
|
|
$effect(() => {
|
|
if (particlesMaterial) {
|
|
const color = new THREE.Color($themeColors.primary);
|
|
particlesMaterial.color = color;
|
|
particlesMaterial.opacity = $mode === 'dark' ? 0.6 : 0.4;
|
|
}
|
|
});
|
|
|
|
// Animation loop
|
|
useTask((delta) => {
|
|
if (!particles || !particlesGeometry) return;
|
|
|
|
time += delta;
|
|
const positionAttribute = particlesGeometry.getAttribute('position') as THREE.BufferAttribute;
|
|
const positionArray = positionAttribute.array as Float32Array;
|
|
|
|
for (let i = 0; i < particleCount; i++) {
|
|
const i3 = i * 3;
|
|
|
|
// Add some wave motion
|
|
positionArray[i3] += velocities[i3] + Math.sin(time + i * 0.1) * 0.001;
|
|
positionArray[i3 + 1] += velocities[i3 + 1] + Math.cos(time + i * 0.1) * 0.001;
|
|
positionArray[i3 + 2] += velocities[i3 + 2];
|
|
|
|
// Wrap around boundaries
|
|
if (positionArray[i3] > 10) positionArray[i3] = -10;
|
|
if (positionArray[i3] < -10) positionArray[i3] = 10;
|
|
if (positionArray[i3 + 1] > 10) positionArray[i3 + 1] = -10;
|
|
if (positionArray[i3 + 1] < -10) positionArray[i3 + 1] = 10;
|
|
if (positionArray[i3 + 2] > 10) positionArray[i3 + 2] = -10;
|
|
if (positionArray[i3 + 2] < -10) positionArray[i3 + 2] = 10;
|
|
}
|
|
|
|
positionAttribute.needsUpdate = true;
|
|
|
|
// Rotate the entire particle system slowly
|
|
particles.rotation.y += delta * 0.05;
|
|
particles.rotation.x += delta * 0.02;
|
|
});
|
|
</script>
|
|
|
|
{#if particles}
|
|
<T is={particles} />
|
|
{/if}
|