Improve snappiness and add speed setting.

This commit is contained in:
Greyson Parrelli
2026-07-26 14:21:56 -04:00
parent e3234b6f83
commit 4ed04c967b
6 changed files with 250 additions and 66 deletions
+134 -32
View File
@@ -1710,7 +1710,7 @@ h3 {
its former neighbors glide into the gap. */
.card-ghost {
z-index: 3;
animation: card-ghost-out 280ms cubic-bezier(0.4, 0, 0.7, 0.4) forwards;
animation: card-ghost-out 240ms cubic-bezier(0.4, 0, 0.7, 0.4) forwards;
}
@keyframes card-ghost-out {
@@ -1886,11 +1886,43 @@ h3 {
/* ---------- battle ---------- */
.battle {
/* Playback-speed multiplier, set inline from the speed picker. Every battle
animation divides its base duration by this, so the visuals quicken in
lockstep with the JS step timers. Fallback keeps calc() valid if unset. */
--battle-speed: 1;
display: flex;
flex-direction: column;
gap: 18px;
}
/* Speed picker in the playback controls. */
.battle-speed {
display: flex;
align-items: center;
gap: 2px;
margin-left: 4px;
padding-left: 6px;
border-left: 1px solid rgba(253, 243, 220, 0.18);
}
.battle-speed-label {
font-size: 0.9rem;
opacity: 0.7;
margin-right: 2px;
}
.battle-speed-btn {
min-width: 2.6em;
padding: 3px 5px;
font-size: 0.78rem;
}
.battle-speed-btn.is-active {
background: var(--gold);
color: #2a1d0c;
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.25);
}
.battle-header {
display: flex;
justify-content: center;
@@ -2073,7 +2105,7 @@ h3 {
left: 50%;
margin-left: -42px;
z-index: 6;
animation: summon-pop 1000ms ease forwards;
animation: summon-pop calc(720ms / var(--battle-speed)) ease forwards;
pointer-events: none;
}
@@ -2151,7 +2183,7 @@ h3 {
z-index: 60;
font-size: 2rem;
pointer-events: none;
animation: spawn-fly 1000ms ease-out forwards;
animation: spawn-fly 760ms ease-out forwards;
filter: drop-shadow(0 3px 4px rgba(0, 0, 0, 0.5));
}
@@ -2179,7 +2211,7 @@ h3 {
position: fixed;
z-index: 60;
transform-origin: top left;
animation: buy-fly 480ms cubic-bezier(0.5, 0.02, 0.3, 1) forwards;
animation: buy-fly 380ms cubic-bezier(0.5, 0.02, 0.3, 1) forwards;
pointer-events: none;
filter: drop-shadow(0 12px 18px rgba(0, 0, 0, 0.5));
}
@@ -2244,7 +2276,7 @@ h3 {
.setaside-card.is-leaving,
.food-fan-card.is-leaving {
animation: card-leave 400ms cubic-bezier(0.4, 0, 0.7, 0.4) forwards;
animation: card-leave calc(300ms / var(--battle-speed)) cubic-bezier(0.4, 0, 0.7, 0.4) forwards;
pointer-events: none;
}
@@ -2274,11 +2306,11 @@ h3 {
}
.food-fan-card.food-in-bottom {
animation: food-in-bottom 440ms cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
animation: food-in-bottom calc(320ms / var(--battle-speed)) cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
}
.food-fan-card.food-in-top {
animation: food-in-top 440ms cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
animation: food-in-top calc(320ms / var(--battle-speed)) cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
}
/* A pet that faints with a pending effect slides out of the arena into the
@@ -2298,7 +2330,7 @@ h3 {
}
.setaside-card.setaside-in {
animation: setaside-in 600ms cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
animation: setaside-in calc(440ms / var(--battle-speed)) cubic-bezier(0.2, 0.8, 0.3, 1) backwards;
}
.battle-unit {
@@ -2354,12 +2386,12 @@ h3 {
.battle-unit.unit-reveal-bottom {
transform-origin: center bottom;
animation: unit-reveal-bottom 520ms cubic-bezier(0.2, 0.8, 0.3, 1);
animation: unit-reveal-bottom calc(380ms / var(--battle-speed)) cubic-bezier(0.2, 0.8, 0.3, 1);
}
.battle-unit.unit-reveal-top {
transform-origin: center top;
animation: unit-reveal-top 520ms cubic-bezier(0.2, 0.8, 0.3, 1);
animation: unit-reveal-top calc(380ms / var(--battle-speed)) cubic-bezier(0.2, 0.8, 0.3, 1);
}
/* --- rock dice --- */
@@ -2402,13 +2434,13 @@ h3 {
}
.droll.is-rolling {
animation: droll-shake 170ms linear infinite;
animation-delay: calc(var(--i) * 45ms);
animation: droll-shake calc(150ms / var(--battle-speed)) linear infinite;
animation-delay: calc(var(--i) * 45ms / var(--battle-speed));
}
.droll.is-settled {
animation: droll-settle 280ms cubic-bezier(0.2, 0.85, 0.3, 1) both;
animation-delay: calc(var(--i) * 55ms);
animation: droll-settle calc(240ms / var(--battle-speed)) cubic-bezier(0.2, 0.85, 0.3, 1) both;
animation-delay: calc(var(--i) * 55ms / var(--battle-speed));
}
@keyframes droll-shake {
@@ -2494,7 +2526,7 @@ h3 {
font-size: 1rem;
color: #7be495;
text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5);
animation: fx-pop 1000ms ease forwards;
animation: fx-pop calc(760ms / var(--battle-speed)) ease forwards;
pointer-events: none;
z-index: 5;
white-space: nowrap;
@@ -2504,43 +2536,113 @@ h3 {
filter: grayscale(1);
}
/* You (bottom) lunge up into the seam; the opponent (top) lunges down. */
/* You (bottom) lunge up into the seam; the opponent (top) lunges down. A quick
crouch of anticipation, an explosive lunge, then an impact recoil with a
scale punch and a little shake before settling — reads as a real collision
rather than a gentle bump. */
@keyframes clash-bottom {
0% {
transform: translateY(0);
transform: translateY(0) scale(1) rotate(0);
}
35% {
transform: translateY(-26px) rotate(2deg);
12% {
transform: translateY(10px) scale(0.95);
}
60% {
transform: translateY(6px);
40% {
transform: translateY(-34px) scale(1.09) rotate(2.5deg);
}
52% {
transform: translateY(-21px) scale(0.98) rotate(-2.5deg);
}
67% {
transform: translateY(8px) rotate(1.5deg);
}
83% {
transform: translateY(-3px) rotate(-0.5deg);
}
100% {
transform: translateY(0);
transform: translateY(0) scale(1) rotate(0);
}
}
@keyframes clash-top {
0% {
transform: translateY(0);
transform: translateY(0) scale(1) rotate(0);
}
35% {
transform: translateY(26px) rotate(-2deg);
12% {
transform: translateY(-10px) scale(0.95);
}
60% {
transform: translateY(-6px);
40% {
transform: translateY(34px) scale(1.09) rotate(-2.5deg);
}
52% {
transform: translateY(21px) scale(0.98) rotate(2.5deg);
}
67% {
transform: translateY(-8px) rotate(-1.5deg);
}
83% {
transform: translateY(3px) rotate(0.5deg);
}
100% {
transform: translateY(0);
transform: translateY(0) scale(1) rotate(0);
}
}
.battle-unit.clash-bottom {
animation: clash-bottom 500ms ease;
animation: clash-bottom calc(460ms / var(--battle-speed)) cubic-bezier(0.36, 0.7, 0.3, 1);
}
.battle-unit.clash-top {
animation: clash-top 500ms ease;
animation: clash-top calc(460ms / var(--battle-speed)) cubic-bezier(0.36, 0.7, 0.3, 1);
}
/* When the two pets meet, the centre seam cracks: the bolt flares and a bright
shockwave ring bursts outward from the collision point. */
@keyframes clash-bolt {
0% {
transform: scale(0.5) rotate(-12deg);
opacity: 0.5;
filter: drop-shadow(0 0 6px rgba(246, 201, 78, 0.5));
}
30% {
transform: scale(1.7) rotate(6deg);
opacity: 1;
filter: drop-shadow(0 0 18px rgba(255, 236, 150, 0.95));
}
100% {
transform: scale(1) rotate(0);
opacity: 0.5;
filter: drop-shadow(0 0 8px rgba(246, 201, 78, 0.5));
}
}
@keyframes clash-shock {
0% {
transform: translate(-50%, -50%) scale(0.2);
opacity: 0.85;
}
100% {
transform: translate(-50%, -50%) scale(2.6);
opacity: 0;
}
}
.battle-center-clash .battle-center-bolt {
animation: clash-bolt calc(420ms / var(--battle-speed)) cubic-bezier(0.3, 0.8, 0.3, 1);
}
.battle-center-clash::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 46px;
height: 46px;
border-radius: 50%;
border: 3px solid rgba(255, 232, 150, 0.9);
box-shadow: 0 0 14px rgba(255, 224, 130, 0.7);
animation: clash-shock calc(420ms / var(--battle-speed)) ease-out forwards;
pointer-events: none;
}
@keyframes dying {
@@ -2551,7 +2653,7 @@ h3 {
}
.battle-unit.unit-dying {
animation: dying 700ms ease 500ms forwards;
animation: dying calc(480ms / var(--battle-speed)) ease calc(360ms / var(--battle-speed)) forwards;
}
@keyframes damage-pop {
@@ -2577,7 +2679,7 @@ h3 {
font-size: 1.4rem;
color: #ff6b52;
text-shadow: 0 2px 0 rgba(0, 0, 0, 0.5);
animation: damage-pop 1100ms ease 250ms forwards;
animation: damage-pop calc(700ms / var(--battle-speed)) ease calc(160ms / var(--battle-speed)) forwards;
opacity: 0;
pointer-events: none;
z-index: 5;