Files
dukang/apps/mini-user/src/components/JiuzuSplash.tsx
T
jacy 9c8d5f2cad feat(assoc): v4.0.1 合伙人关联码、分佣账单与 H5 用户管理
订单佣金只认关联用户;合伙人备注写入独立表;H5 增加用户管理与首页统计。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-30 14:35:39 +08:00

141 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useCallback, useEffect, useRef, useState } from 'react';
import { Image, Text, View } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { JIUZU_SPLASH_GIF_URL, JIUZU_SPLASH_MARK_URL } from '@dukang/shared-types';
import { markJiuzuSplashPlayed } from '../lib/jiuzu-splash';
const CHARS = ['酒', '祖', '杜', '康'] as const;
const SPLASH_NAV_BG = '#140808';
const HOME_NAV_BG = '#FAF9F7';
/** GIF 21 帧 × 80ms,略提前淡出避免循环 */
const GIF_MS = 1650;
const GIF_FADE_MS = 350;
/** 四字显现并升到偏上位置 */
const TITLE_MS = 2400;
/** 副标题在四字到位后再升起 */
const SUB_DELAY_MS = 2300;
const SUB_MS = 1400;
const HOLD_MS = 900;
const FADE_MS = 800;
const FADE_AT_MS = GIF_MS + SUB_DELAY_MS + SUB_MS + HOLD_MS;
type JiuzuSplashProps = {
onDone: () => void;
};
function applySplashChrome() {
try {
void Taro.hideTabBar({ animation: false });
} catch {
/* H5 无原生 TabBar */
}
void Taro.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: SPLASH_NAV_BG,
animation: { duration: 200, timingFunc: 'easeIn' },
}).catch(() => {});
}
function restoreChrome() {
try {
void Taro.showTabBar({ animation: false });
} catch {
/* H5 无原生 TabBar */
}
void Taro.setNavigationBarColor({
frontColor: '#000000',
backgroundColor: HOME_NAV_BG,
animation: { duration: 200, timingFunc: 'easeOut' },
}).catch(() => {});
}
export default function JiuzuSplash({ onDone }: JiuzuSplashProps) {
const finishedRef = useRef(false);
const fadingRef = useRef(false);
const onDoneRef = useRef(onDone);
onDoneRef.current = onDone;
const [leaving, setLeaving] = useState(false);
const [gifDone, setGifDone] = useState(false);
const [gifGone, setGifGone] = useState(false);
const finish = useCallback(() => {
if (finishedRef.current) return;
finishedRef.current = true;
restoreChrome();
onDoneRef.current();
}, []);
const beginExit = useCallback(() => {
if (fadingRef.current || finishedRef.current) return;
fadingRef.current = true;
setLeaving(true);
setTimeout(finish, FADE_MS);
}, [finish]);
useEffect(() => {
markJiuzuSplashPlayed();
applySplashChrome();
const gifTimer = setTimeout(() => setGifDone(true), GIF_MS);
const gifGoneTimer = setTimeout(() => setGifGone(true), GIF_MS + GIF_FADE_MS);
const exitTimer = setTimeout(beginExit, FADE_AT_MS);
return () => {
clearTimeout(gifTimer);
clearTimeout(gifGoneTimer);
clearTimeout(exitTimer);
if (!finishedRef.current) restoreChrome();
};
}, [beginExit]);
return (
<View
className={`jiuzu-splash${gifDone ? ' jiuzu-splash--after-gif' : ''}${leaving ? ' jiuzu-splash--out' : ''}`}
catchMove
onTouchMove={(e) => {
e.stopPropagation?.();
}}
>
<View className="jiuzu-splash-mist jiuzu-splash-mist--a" />
<View className="jiuzu-splash-mist jiuzu-splash-mist--b" />
{gifGone ? null : (
<Image
className={`jiuzu-splash-gif${gifDone ? ' jiuzu-splash-gif--out' : ''}`}
src={JIUZU_SPLASH_GIF_URL}
mode="aspectFill"
style={{ width: '100%', height: '100%' }}
/>
)}
<View className="jiuzu-splash-copy">
<View className="jiuzu-splash-lockup">
<Image
className="jiuzu-splash-mark"
src={JIUZU_SPLASH_MARK_URL}
mode="aspectFit"
style={{ width: '320px', height: '180px' }}
/>
<View className="jiuzu-splash-title">
<View className="jiuzu-splash-chars">
<View className="jiuzu-splash-shimmer" />
{CHARS.map((ch) => (
<View key={ch} className="jiuzu-splash-char">
<Text className="jiuzu-splash-char-text">{ch}</Text>
</View>
))}
</View>
</View>
</View>
<View className="jiuzu-splash-sub">
<Text className="jiuzu-splash-sub-text">千年酒祖 · 杜康好客</Text>
</View>
</View>
<View className="jiuzu-splash-skip" onClick={finish}>
<Text className="jiuzu-splash-skip-text">跳过</Text>
</View>
</View>
);
}