The effects move out of Board.svelte into src/fx-sprites/ — one component per flourish, markup and animation CSS together, so editing the fireball means opening Fireball.svelte and nothing else. A registry (index.ts) maps kind to sprite; Board renders through it. The event mapping stays in fx.ts. And a viewer: /?fx opens the Flourish Workshop — every sprite on a labeled parchment tile, replaying on a 2.2-second beat. Under the dev server, editing any sprite file hot-reloads the gallery mid-loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
35 lines
1.0 KiB
Svelte
35 lines
1.0 KiB
Svelte
<script lang="ts">
|
|
import type { FxOf } from "../fx";
|
|
import { center } from "./geom";
|
|
let { fx }: { fx: FxOf<"bolt"> } = $props();
|
|
/** A fresh jagged path every strike. */
|
|
const points = $derived.by(() => {
|
|
const a = center(fx.from), b = center(fx.to);
|
|
const segs = 6;
|
|
const pts: string[] = [`${a.x},${a.y}`];
|
|
for (let i = 1; i < segs; i++) {
|
|
const t = i / segs;
|
|
pts.push(`${(a.x + (b.x - a.x) * t + (Math.random() - 0.5) * 16).toFixed(1)},${(a.y + (b.y - a.y) * t + (Math.random() - 0.5) * 16).toFixed(1)}`);
|
|
}
|
|
pts.push(`${b.x},${b.y}`);
|
|
return pts.join(" ");
|
|
});
|
|
</script>
|
|
|
|
<polyline {points} class="bolt" />
|
|
|
|
<style>
|
|
.bolt {
|
|
fill: none;
|
|
stroke: #ffe94d;
|
|
stroke-width: 3;
|
|
stroke-linejoin: round;
|
|
filter: drop-shadow(0 0 7px rgba(255, 233, 77, 0.95));
|
|
animation: flicker 0.5s steps(2, jump-none) forwards;
|
|
}
|
|
@keyframes flicker {
|
|
0% { opacity: 0; } 15% { opacity: 1; } 40% { opacity: 0.3; }
|
|
60% { opacity: 1; } 100% { opacity: 0; }
|
|
}
|
|
</style>
|