mirror of
https://github.com/tiennm99/tiennm99bot.git
synced 2026-10-11 03:13:46 +00:00
fix(gacha): blend the mirror glint additively and sweep the whole card
The glint band was a card-sized box sliding diagonally, so the top-right and bottom-left corners never fell inside it. Draw the band on a box three times the card's size, centred on it, so every corner is lit. Blend the glint with plus-lighter and tint it with the card's accent so it brightens the card's own tone instead of painting flat white over it.
This commit is contained in:
1 parent
21b9e596bc
commit
c4bc131372
3 files changed
+74
-18
No files matched your search
+33
@@ -0,0 +1,33 @@
|
||||
---
|
||||
title: "Gacha glint: additive blend and full-card coverage"
|
||||
date: 2026-10-02
|
||||
summary: "Mirror glint now blends additively in the card's accent tone and sweeps every corner of the card"
|
||||
---
|
||||
|
||||
# Gacha glint: additive blend and full-card coverage
|
||||
|
||||
## What happened
|
||||
- The user asked for an additive blend on the gacha card's mirror glint. If that cost too much GPU, they wanted lower alpha and a colour that matches the card's tone instead.
|
||||
- They also noticed the glint did not cover the whole card.
|
||||
|
||||
## Root cause (coverage)
|
||||
- `.wish-glint` was a card-sized box carrying a 135deg gradient, translated from (-100%,-100%) to (100%,100%).
|
||||
- A band only paints inside its own box, so as the box slid diagonally the top-right and bottom-left corners were never inside it at the moment the band passed. With a 63:88 card, the top-right corner sits about 5px outside the box when the band reaches it.
|
||||
|
||||
## Changes
|
||||
- `src/gacha/page/page.css`:
|
||||
- The glint clip uses `mix-blend-mode: plus-lighter`.
|
||||
- Band and flare colours are tinted with `--glint`, the card accent, with peak alpha around 0.75.
|
||||
- The band box is now 300% of the card, centred on it, with gradient stops rescaled by 1/3. The flare width goes from 46% to 15.3%.
|
||||
- `src/gacha/page/page.js`:
|
||||
- `--glint` is set on the clip itself, because the card flies in an overlay outside #stage.
|
||||
- The sweep now runs from translate(-26.7%) to translate(26.7%), which is 0.8 card sizes each way.
|
||||
|
||||
## Evidence
|
||||
- Docker render timings showed additive and normal blending within noise: 14.2s vs 14.4s and 11.6s vs 11.5s per clip. Headless Chrome composites in software here, and blending costs nothing measurable.
|
||||
- Frames at 3.8s (3-star) and 3.75s (5-star) show the band reaching the card edges, tinted blue and gold.
|
||||
|
||||
## Next steps
|
||||
- The user reviews the fixtures before commit.
|
||||
|
||||
> Historical work record — not durable authority. Prefer docs/specs/ADRs for current decisions.
|
||||
@@ -133,10 +133,17 @@ body {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* The mirror glint that sweeps across the card once it lands (page.js). */
|
||||
/*
|
||||
* The mirror glint that sweeps across the card once it lands (page.js). It is
|
||||
* blended additively (plus-lighter), so it brightens the card's own colours
|
||||
* instead of painting flat white over them; the light is tinted with the
|
||||
* card's accent (--glint) so it matches the card's tone.
|
||||
*/
|
||||
.wish-glint-clip {
|
||||
--glint-core: color-mix(in srgb, var(--glint, #fff) 35%, #fff);
|
||||
border-radius: inherit;
|
||||
inset: 0;
|
||||
mix-blend-mode: plus-lighter;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
@@ -146,32 +153,43 @@ body {
|
||||
.wish-glint {
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
transparent 36%,
|
||||
rgb(255 255 255 / 0.18) 43%,
|
||||
rgb(255 255 255 / 0.85) 49%,
|
||||
#ffffff 50%,
|
||||
rgb(255 255 255 / 0.85) 51%,
|
||||
rgb(255 255 255 / 0.18) 57%,
|
||||
transparent 64%
|
||||
transparent 45.3%,
|
||||
color-mix(in srgb, var(--glint, #fff) 14%, transparent) 47.7%,
|
||||
color-mix(in srgb, var(--glint-core) 60%, transparent) 49.7%,
|
||||
color-mix(in srgb, var(--glint-core) 75%, transparent) 50%,
|
||||
color-mix(in srgb, var(--glint-core) 60%, transparent) 50.3%,
|
||||
color-mix(in srgb, var(--glint, #fff) 14%, transparent) 52.3%,
|
||||
transparent 54.7%
|
||||
);
|
||||
inset: 0;
|
||||
height: 300%;
|
||||
left: -100%;
|
||||
position: absolute;
|
||||
transform: translate(-100%, -100%);
|
||||
top: -100%;
|
||||
transform: translate(-26.7%, -26.7%);
|
||||
width: 300%;
|
||||
}
|
||||
|
||||
/* A four-ray flare riding the band's centre, which travels corner to corner. */
|
||||
/*
|
||||
* A four-ray flare riding the band's centre, which travels corner to corner.
|
||||
* Sized against the band's 3x box, so it spans 46% of the card.
|
||||
*/
|
||||
.wish-glint-flare {
|
||||
background: radial-gradient(circle, #ffffff 0 14%, rgb(255 255 255 / 0.6) 30%, transparent 64%);
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
color-mix(in srgb, var(--glint-core) 85%, transparent) 0 14%,
|
||||
color-mix(in srgb, var(--glint) 45%, transparent) 30%,
|
||||
transparent 64%
|
||||
);
|
||||
aspect-ratio: 1;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 46%;
|
||||
width: 15.3%;
|
||||
}
|
||||
|
||||
.wish-glint-flare::before,
|
||||
.wish-glint-flare::after {
|
||||
background: linear-gradient(90deg, transparent, #ffffff 46% 54%, transparent);
|
||||
background: linear-gradient(90deg, transparent, var(--glint-core) 46% 54%, transparent);
|
||||
content: '';
|
||||
height: 5%;
|
||||
left: -40%;
|
||||
|
||||
@@ -41,6 +41,8 @@ const createFace = () => {
|
||||
content.append(el('span', 'wish-rank', wish.rank), title, el('p', 'wish-stars', '★'.repeat(wish.stars)));
|
||||
const clip = el('span', 'wish-glint-clip');
|
||||
clip.setAttribute('aria-hidden', 'true');
|
||||
// Set on the clip itself: the card flies in an overlay outside #stage.
|
||||
clip.style.setProperty('--glint', wish.artwork.accent);
|
||||
const band = el('span', 'wish-glint');
|
||||
band.append(el('span', 'wish-glint-flare'));
|
||||
clip.append(band);
|
||||
@@ -162,9 +164,12 @@ const glint = Object.freeze({duration: 650, easing: 'cubic-bezier(.45, 0, .55, 1
|
||||
* Keeps the card's text hidden while it spins and switches it on as the card
|
||||
* turns edge-on for its last swing face up, so the text rides in on the final
|
||||
* turn without visibly popping on. Then it sweeps a mirror glint from the
|
||||
* top-left corner to the bottom-right one as the card comes to rest. The glint band is a gradient
|
||||
* across its own card-sized box, so moving that box from one corner to the
|
||||
* other carries the band diagonally over the whole face.
|
||||
* top-left corner to the bottom-right one as the card comes to rest. The glint
|
||||
* band is a gradient across a box three times the card's size and centred on
|
||||
* it, so the card stays inside that box for the whole sweep and every corner is
|
||||
* lit; a card-sized box would leave the top-right and bottom-left corners
|
||||
* outside it as it slid diagonally. Moving the box 0.8 card-widths and heights
|
||||
* each way starts and ends the band clear of the card.
|
||||
*
|
||||
* @param {Element} turn
|
||||
* @param {number} delay Milliseconds until the spin starts.
|
||||
@@ -176,7 +181,7 @@ const revealFace = (turn, delay, duration) => {
|
||||
{delay, duration, easing: 'linear', fill: 'both'},
|
||||
);
|
||||
const band = turn.querySelector('.wish-glint');
|
||||
band?.animate([{transform: 'translate(-100%, -100%)'}, {transform: 'translate(100%, 100%)'}], {
|
||||
band?.animate([{transform: 'translate(-26.7%, -26.7%)'}, {transform: 'translate(26.7%, 26.7%)'}], {
|
||||
delay: delay + duration,
|
||||
duration: glint.duration,
|
||||
easing: glint.easing,
|
||||
|
||||
Reference in new issue
Block a user