Website Redesign 7
This commit is contained in:
101
src/lib/components/ParticleField.svelte
Normal file
101
src/lib/components/ParticleField.svelte
Normal file
@@ -0,0 +1,101 @@
|
||||
<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}
|
||||
Reference in New Issue
Block a user