fix: reliably restart hero slide indicator fill animation

The dot indicator relied on a React key remount to restart the CSS
heroIndicatorFill keyframe on each slide change, which WebKitGTK
sometimes fails to pick up as a fresh animation start, leaving the
active dot frozen at 0% while the underlying slide timer kept
advancing normally. Drive the restart imperatively via a ref with a
forced reflow instead.
This commit is contained in:
KhooLy 2026-07-30 19:58:32 +03:00
parent 674921e250
commit da8ef1dfb5

View file

@ -56,6 +56,7 @@ export const HeroSection = React.memo(function HeroSection({
const pendingRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const activeIndexRef = useRef(activeIndex);
activeIndexRef.current = activeIndex;
const indicatorFillRef = useRef<HTMLSpanElement | null>(null);
const activeMeta = items[activeIndex] ?? meta;
const canSlide = items.length > 1;
@ -140,6 +141,20 @@ export const HeroSection = React.memo(function HeroSection({
return () => window.clearInterval(id);
}, [canSlide, items.length, isActive, trailerPending, slideIntervalMs]);
useEffect(() => {
const el = indicatorFillRef.current;
if (!el) return;
el.style.animation = 'none';
void el.offsetWidth;
el.style.animation = `heroIndicatorFill ${slideIntervalMs}ms linear forwards`;
el.style.animationPlayState = trailerPending || !isActive ? 'paused' : 'running';
}, [activeIndex, slideIntervalMs]);
useEffect(() => {
const el = indicatorFillRef.current;
if (el) el.style.animationPlayState = trailerPending || !isActive ? 'paused' : 'running';
}, [trailerPending, isActive]);
useEffect(() => {
if (!canSlide) return;
const next = items[(activeIndex + 1) % items.length];
@ -346,16 +361,10 @@ export const HeroSection = React.memo(function HeroSection({
onClick={() => goTo(i)}
>
<span
key={i === activeIndex ? `${activeIndex}` : `${i}-static`}
ref={i === activeIndex ? indicatorFillRef : undefined}
style={{
...styles.indicatorFill,
...(i < activeIndex ? styles.indicatorFillDone : null),
...(i === activeIndex
? {
animation: `heroIndicatorFill ${slideIntervalMs}ms linear forwards`,
animationPlayState: trailerPending || !isActive ? 'paused' : 'running',
}
: null),
}}
/>
</button>