Improve snappiness and add speed setting.
This commit is contained in:
+134
-32
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user