Files
web-app-launcher/src/lib/components/background/MeshGradient.svelte
T
alexei.dolgolyov e6b50fb4f1 feat(mvp): phase 8 - integration, testing & deployment
Fix all build/type/lint errors (zod 3.25 compat wrapper, Svelte 5 fixes),
write 115 unit tests across 10 test files, expand seed script with demo
data, update Docker config with migration on startup.
2026-03-24 22:09:17 +03:00

72 lines
1.4 KiB
Svelte

<script lang="ts">
import { theme } from '$lib/stores/theme.svelte.js';
interface Blob {
x: number;
y: number;
vx: number;
vy: number;
hueOffset: number;
size: number;
}
const blobCount = 4;
let blobs = $state<Blob[]>([]);
let animFrame: number;
function initBlobs(): Blob[] {
return Array.from({ length: blobCount }, (_, i) => ({
x: 20 + Math.random() * 60,
y: 20 + Math.random() * 60,
vx: (Math.random() - 0.5) * 0.02,
vy: (Math.random() - 0.5) * 0.02,
hueOffset: i * 40,
size: 35 + Math.random() * 20
}));
}
function animate() {
blobs = blobs.map((blob) => {
let { x, y, vx, vy } = blob;
x += vx;
y += vy;
if (x < 5 || x > 95) vx = -vx;
if (y < 5 || y > 95) vy = -vy;
return { ...blob, x, y, vx, vy };
});
animFrame = requestAnimationFrame(animate);
}
$effect(() => {
blobs = initBlobs();
animFrame = requestAnimationFrame(animate);
return () => {
cancelAnimationFrame(animFrame);
};
});
</script>
<div class="absolute inset-0">
<svg class="h-full w-full" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="mesh-blur">
<feGaussianBlur stdDeviation="60" />
</filter>
</defs>
{#each blobs as blob (blob.hueOffset)}
<circle
cx="{blob.x}%"
cy="{blob.y}%"
r="{blob.size}%"
fill="hsla({theme.primaryHue + blob.hueOffset}, {theme.primarySaturation}%, {theme.isDark ? 40 : 60}%, 0.12)"
filter="url(#mesh-blur)"
/>
{/each}
</svg>
</div>