62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { useEffect, useState, type AnimationEvent } from 'react';
|
|
import styles from './Splash.module.css';
|
|
|
|
/** 单线手写 WebDrop,逐笔画出。每个字母一条路径,按序号错开 */
|
|
const STROKES = [
|
|
'M4 14 L14 58 L24 30 L34 58 L44 14',
|
|
'M52 42 H76 C76 28 66 26 60 31 C50 38 53 58 63 58 C69 59 74 56 77 52',
|
|
'M88 8 L88 58 M88 46 C92 34 106 36 106 47 C106 59 92 62 88 52',
|
|
'M120 14 L120 58 M120 14 C156 12 160 58 120 58',
|
|
'M172 30 L172 58 M172 42 C176 31 186 29 192 33',
|
|
'M204 46 C204 33 222 33 222 46 C222 59 204 59 204 46',
|
|
'M236 32 L236 74 M236 46 C240 34 256 36 256 48 C256 60 242 64 236 54',
|
|
];
|
|
|
|
const reduced = () => matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
interface Props {
|
|
ready: boolean;
|
|
onDone: () => void;
|
|
}
|
|
|
|
export function Splash({ ready, onDone }: Props) {
|
|
const [animDone, setAnimDone] = useState(reduced);
|
|
const [skipped, setSkipped] = useState(false);
|
|
const leaving = skipped || (animDone && ready);
|
|
|
|
useEffect(() => {
|
|
const skip = () => setSkipped(true);
|
|
window.addEventListener('keydown', skip);
|
|
return () => window.removeEventListener('keydown', skip);
|
|
}, []);
|
|
|
|
function onEnd(e: AnimationEvent) {
|
|
if (e.animationName.includes('draw') && e.target instanceof SVGElement && e.target.dataset.last) setAnimDone(true);
|
|
if (e.animationName.includes('leave') && e.target === e.currentTarget) onDone();
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className={`${styles.splash} ${leaving ? styles.leaving : ''}`}
|
|
onClick={() => setSkipped(true)}
|
|
onAnimationEnd={onEnd}
|
|
role="status"
|
|
aria-label="加载中"
|
|
>
|
|
<svg viewBox="0 0 262 82" className={styles.word} aria-hidden="true">
|
|
{STROKES.map((d, i) => (
|
|
<path
|
|
key={d}
|
|
d={d}
|
|
pathLength={1}
|
|
className={styles.stroke}
|
|
style={{ '--i': i } as React.CSSProperties}
|
|
data-last={i === STROKES.length - 1 ? '1' : undefined}
|
|
/>
|
|
))}
|
|
</svg>
|
|
<p className={styles.hint}>加载中</p>
|
|
</div>
|
|
);
|
|
}
|