The review lands: twelve sprites get their motion stories
The outside review's diagnosis was right — the weak sprites had one element doing one motion. Now each tells its story: the whiff is three air streaks sweeping past a dodger with a wobble; the hit is a contact flash, a compressed ring punched outward, and flying debris; the streak's head visibly travels, drawing a tapered trail that collapses into the destination; the sparkle snaps white-hot between counter-rotating glints and sheds motes; the tacks are drawn silhouettes now, points up as a tack on the floor menaces, hopping in sequence; the claw slashes reveal one by one along their length with a bright leading edge while the whole strike rakes sideways. The sector ghosts keep their dashed frames but earn motion arcs, directional chevrons, corner dust, and a grinding shudder as they settle. The portal opens wide, ripples along its length, and pinches shut; cell mouths collapse rings of energy inward. The thornbush springs actual branches with thorn spurs. The slime is an asymmetric blob that grips, with elastic strands snapping toward the caught. Dust gains contrast, varied grain, and blows perpendicular to the wall that shed it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
a88cae3a0e
commit
bcaa06251a
@@ -3,42 +3,41 @@
|
||||
import { center } from "./geom";
|
||||
|
||||
let { fx }: { fx: FxOf<"streak"> } = $props();
|
||||
const uid = $props.id();
|
||||
const a = $derived(center(fx.from));
|
||||
const b = $derived(center(fx.to));
|
||||
const d = $derived(`M ${a.x} ${a.y} L ${b.x} ${b.y}`);
|
||||
</script>
|
||||
|
||||
<defs>
|
||||
<linearGradient id={`streak-trail-${uid}`}
|
||||
x1={a.x} y1={a.y} x2={b.x} y2={b.y} gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#e9e1cb" stop-opacity="0" />
|
||||
<stop offset="0.7" stop-color="#e9e1cb" stop-opacity="0.6" />
|
||||
<stop offset="1" stop-color="#fffdf0" stop-opacity="0.95" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<line x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke={`url(#streak-trail-${uid})`} class="trail" />
|
||||
<circle cx={b.x} cy={b.y} r="5" class="head" />
|
||||
<path {d} pathLength="100" class="trail wide" />
|
||||
<path {d} pathLength="100" class="trail bright" />
|
||||
<circle r="4.5" class="head" cx="0" cy="0">
|
||||
<animateMotion dur="0.26s" fill="freeze" path={d} calcMode="spline"
|
||||
keySplines="0.3 0 0.4 1" keyTimes="0;1" keyPoints="0;1" />
|
||||
</circle>
|
||||
|
||||
<style>
|
||||
.trail {
|
||||
stroke-width: 6;
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
animation: trail-fade 0.55s ease-out forwards;
|
||||
stroke-dasharray: 100 100;
|
||||
animation: draw-collapse 0.55s 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; }
|
||||
.head {
|
||||
transform-box: fill-box;
|
||||
transform-origin: center;
|
||||
fill: rgba(255, 253, 240, 0.9);
|
||||
animation: head-land 0.45s ease-out forwards;
|
||||
fill: #fffdf0;
|
||||
animation: head-life 0.5s ease-out forwards;
|
||||
}
|
||||
@keyframes trail-fade {
|
||||
0% { opacity: 0.95; }
|
||||
/* Draw from source to head, then the tail chases it into the destination. */
|
||||
@keyframes draw-collapse {
|
||||
0% { stroke-dashoffset: 100; opacity: 0.95; }
|
||||
47% { stroke-dashoffset: 0; opacity: 0.95; }
|
||||
100% { stroke-dashoffset: -100; opacity: 0; }
|
||||
}
|
||||
@keyframes head-life {
|
||||
0% { opacity: 0; }
|
||||
12% { opacity: 1; }
|
||||
60% { opacity: 1; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
@keyframes head-land {
|
||||
0% { opacity: 0; transform: scale(0.4); }
|
||||
35% { opacity: 1; transform: scale(1.3); }
|
||||
100% { opacity: 0; transform: scale(0.9); }
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user