The optional final polish: five refinements
The bolt deepens to saturated gold over a dark amber glow; the splash throws a wider crown of five droplets over a broader pool; the fireworks lose their protractor — uneven angles, lengths, sizes, and staggered timing, as a real burst has; the portal-cell's void gains turning swirl arms so the dark center reads as motion, not blur; the streak narrows and softens so its peak frame no longer resembles a drawn white line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e713d69582
commit
c6d73550df
@@ -45,10 +45,10 @@
|
||||
<style>
|
||||
.bolt .wide {
|
||||
fill: none;
|
||||
stroke: #ffe94d;
|
||||
stroke-width: 5;
|
||||
stroke: #e6a812;
|
||||
stroke-width: 5.5;
|
||||
stroke-linejoin: round;
|
||||
opacity: 0.55;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.bolt .fork {
|
||||
fill: none;
|
||||
@@ -59,9 +59,10 @@
|
||||
.bolt .core {
|
||||
fill: none;
|
||||
stroke: #fffdf0;
|
||||
stroke-width: 1.8;
|
||||
stroke-width: 2;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.bolt .fork { stroke: #f5d54a; }
|
||||
.bolt { animation: bolt-flicker 0.5s steps(2, jump-none) forwards; }
|
||||
@keyframes bolt-flicker {
|
||||
0% { opacity: 0; } 15% { opacity: 1; } 40% { opacity: 0.3; }
|
||||
|
||||
@@ -5,24 +5,34 @@
|
||||
let { fx }: { fx: FxOf<"fireworks"> } = $props();
|
||||
const c = $derived(center(fx.at));
|
||||
const COLORS = ["#e74c3c", "#f1c40f", "#3b8dd6", "#7ac47e"];
|
||||
const RAYS = [0, 45, 90, 135, 180, 225, 270, 315];
|
||||
// Deliberately uneven: a real burst has no protractor.
|
||||
const RAYS = [
|
||||
{ deg: 8, len: 22, r: 2.8, cls: "" },
|
||||
{ deg: 52, len: 17, r: 2.2, cls: "late" },
|
||||
{ deg: 88, len: 24, r: 3, cls: "" },
|
||||
{ deg: 141, len: 16, r: 2, cls: "later" },
|
||||
{ deg: 176, len: 21, r: 2.6, cls: "late" },
|
||||
{ deg: 224, len: 18, r: 2.3, cls: "" },
|
||||
{ deg: 267, len: 23, r: 2.8, cls: "later" },
|
||||
{ deg: 309, len: 15, r: 2, cls: "late" },
|
||||
];
|
||||
</script>
|
||||
|
||||
<g class="fireworks">
|
||||
{#each RAYS as deg, i (deg)}
|
||||
{@const dx = Math.cos((deg * Math.PI) / 180)}
|
||||
{@const dy = Math.sin((deg * Math.PI) / 180)}
|
||||
{#each RAYS as ray, i (ray.deg)}
|
||||
{@const dx = Math.cos((ray.deg * Math.PI) / 180)}
|
||||
{@const dy = Math.sin((ray.deg * Math.PI) / 180)}
|
||||
<line
|
||||
x1={c.x + 4 * dx} y1={c.y + 4 * dy}
|
||||
x2={c.x + 20 * dx} y2={c.y + 20 * dy}
|
||||
x2={c.x + ray.len * dx} y2={c.y + ray.len * dy}
|
||||
stroke={COLORS[i % 4]}
|
||||
class="trail"
|
||||
class={`trail ${ray.cls}`}
|
||||
style={`transform-origin: ${c.x}px ${c.y}px`}
|
||||
/>
|
||||
<circle
|
||||
cx={c.x + 21 * dx} cy={c.y + 21 * dy} r="2.6"
|
||||
cx={c.x + (ray.len + 1) * dx} cy={c.y + (ray.len + 1) * dy} r={ray.r}
|
||||
fill={COLORS[i % 4]}
|
||||
class="spark"
|
||||
class={`spark ${ray.cls}`}
|
||||
style={`transform-origin: ${c.x}px ${c.y}px`}
|
||||
/>
|
||||
{/each}
|
||||
@@ -36,6 +46,8 @@
|
||||
animation: trail-out 0.9s ease-out forwards;
|
||||
}
|
||||
.spark { animation: spark-out 1.05s ease-out forwards; }
|
||||
.trail.late, .spark.late { animation-delay: 0.07s; }
|
||||
.trail.later, .spark.later { animation-delay: 0.14s; }
|
||||
.heart { fill: #fffdf0; animation: heart-pop 0.4s ease-out forwards; }
|
||||
@keyframes trail-out {
|
||||
0% { opacity: 0; transform: scale(0.15); }
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
width={CELL - 10} height={CELL - 10} rx="8"
|
||||
fill={`url(#portal-void-${uid})`} class="void"
|
||||
style={`transform-origin: ${fx.at.x * CELL + CELL / 2}px ${fx.at.y * CELL + CELL / 2}px`} />
|
||||
{@const vx = fx.at.x * CELL + CELL / 2}
|
||||
{@const vy = fx.at.y * CELL + CELL / 2}
|
||||
<g class="swirl" style={`transform-origin: ${vx}px ${vy}px`}>
|
||||
<path d={`M ${vx + 12} ${vy} A 12 12 0 0 1 ${vx - 6} ${vy + 10}`} class="arm" />
|
||||
<path d={`M ${vx - 12} ${vy} A 12 12 0 0 1 ${vx + 6} ${vy - 10}`} class="arm" />
|
||||
<path d={`M ${vx} ${vy + 7} A 7 7 0 0 1 ${vx - 6} ${vy - 4}`} class="arm faint" />
|
||||
</g>
|
||||
<rect x={fx.at.x * CELL + 3} y={fx.at.y * CELL + 3}
|
||||
width={CELL - 6} height={CELL - 6} rx="6" class="veil outer"
|
||||
style={`transform-origin: ${fx.at.x * CELL + CELL / 2}px ${fx.at.y * CELL + CELL / 2}px`} />
|
||||
@@ -68,6 +75,19 @@
|
||||
filter: drop-shadow(0 0 4px rgba(155, 227, 224, 0.8));
|
||||
}
|
||||
.void { animation: void-open 0.9s ease-out forwards; }
|
||||
.arm {
|
||||
fill: none;
|
||||
stroke: rgba(155, 227, 224, 0.7);
|
||||
stroke-width: 1.8;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.arm.faint { stroke: rgba(180, 138, 224, 0.55); }
|
||||
.swirl { animation: swirl-turn 0.9s ease-in forwards; }
|
||||
@keyframes swirl-turn {
|
||||
0% { opacity: 0; transform: rotate(0deg) scale(1.1); }
|
||||
25% { opacity: 1; }
|
||||
100% { opacity: 0; transform: rotate(160deg) scale(0.4); }
|
||||
}
|
||||
.veil.outer { stroke-width: 2.5; animation: veil-in 0.9s ease-in forwards; }
|
||||
.veil.inner {
|
||||
stroke: rgba(180, 138, 224, 0.8);
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
</radialGradient>
|
||||
</defs>
|
||||
|
||||
<ellipse cx={c.x} cy={c.y + 4} rx="12" ry="5" fill={`url(#splash-pool-${uid})`} class="pool" />
|
||||
<circle cx={c.x} cy={c.y} r="5" class="ring" />
|
||||
<ellipse cx={c.x} cy={c.y + 5} rx="16" ry="6.5" fill={`url(#splash-pool-${uid})`} class="pool" />
|
||||
<circle cx={c.x} cy={c.y} r="7" class="ring" />
|
||||
<!-- The crown: droplets thrown upward, falling back -->
|
||||
<g class="crown">
|
||||
<circle cx={c.x - 10} cy={c.y - 6} r="2.5" class="drop" />
|
||||
<circle cx={c.x - 3} cy={c.y - 12} r="2" class="drop late" />
|
||||
<circle cx={c.x + 5} cy={c.y - 10} r="2.2" class="drop" />
|
||||
<circle cx={c.x + 11} cy={c.y - 5} r="1.7" class="drop later" />
|
||||
<circle cx={c.x - 14} cy={c.y - 6} r="2.8" class="drop" />
|
||||
<circle cx={c.x - 7} cy={c.y - 13} r="2.3" class="drop late" />
|
||||
<circle cx={c.x + 1} cy={c.y - 16} r="2.5" class="drop" />
|
||||
<circle cx={c.x + 8} cy={c.y - 12} r="2.2" class="drop later" />
|
||||
<circle cx={c.x + 14} cy={c.y - 5} r="1.9" class="drop late" />
|
||||
</g>
|
||||
|
||||
<style>
|
||||
@@ -36,7 +37,7 @@
|
||||
transform-origin: center;
|
||||
fill: none;
|
||||
stroke: #7cc0ee;
|
||||
stroke-width: 2.5;
|
||||
stroke-width: 3.2;
|
||||
animation: ring-out 0.55s ease-out forwards;
|
||||
}
|
||||
.drop { fill: #7cc0ee; animation: drop-arc 0.6s ease-in forwards; }
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<path {d} pathLength="100" class="trail wide" />
|
||||
<path {d} pathLength="100" class="trail bright" />
|
||||
<circle r="4.5" class="head" cx="0" cy="0">
|
||||
<circle r="4" class="head" cx="0" cy="0">
|
||||
<animateMotion dur="0.38s" fill="freeze" path={d} calcMode="spline"
|
||||
keySplines="0.3 0 0.4 1" keyTimes="0;1" keyPoints="0;1" />
|
||||
</circle>
|
||||
@@ -22,8 +22,8 @@
|
||||
stroke-dasharray: 100 100;
|
||||
animation: draw-collapse 0.68s ease-out forwards;
|
||||
}
|
||||
.trail.wide { stroke: rgba(233, 225, 203, 0.4); stroke-width: 7; }
|
||||
.trail.bright { stroke: rgba(255, 253, 240, 0.9); stroke-width: 3; }
|
||||
.trail.wide { stroke: rgba(233, 225, 203, 0.3); stroke-width: 5; }
|
||||
.trail.bright { stroke: rgba(255, 253, 240, 0.75); stroke-width: 2; }
|
||||
.head {
|
||||
fill: #fffdf0;
|
||||
animation: head-life 0.6s ease-out forwards;
|
||||
|
||||
Reference in New Issue
Block a user