import { useCallback, useEffect, useMemo, useState, type CSSProperties } from 'react'; import { View, Text } from '@tarojs/components'; import Taro, { useReady } from '@tarojs/taro'; type StoreRedeemMarqueeProps = { lines: string[]; }; /** 估算单行像素宽(12px 字号,DOM 测量失败时兜底) */ function estimateTextWidth(text: string): number { let w = 0; for (const ch of text) { w += /[^\x00-\xff]/.test(ch) ? 12 : 7; } return Math.max(Math.ceil(w), 80); } /** * 门店核销动态走马灯(H5 + 微信小程序) * * - 用 Text 测自然内容宽(weapp 的 View 默认撑满父级,不能用来测宽) * - 两段显式同宽 + 轨道宽 = 2×段宽,CSS translateX(-50%) 无缝循环 * - 测量失败时用字宽估算,仍能滚动 */ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) { const segment = useMemo(() => { const joined = lines .map((s) => String(s || '').trim()) .filter(Boolean) .join('  '); return joined ? `${joined}  ` : ''; }, [lines]); const measureId = useMemo( () => `sm${Math.random().toString(36).slice(2, 10)}`, [segment], ); const [segWidth, setSegWidth] = useState(0); const measure = useCallback(() => { if (!segment) { setSegWidth(0); return; } const fallback = estimateTextWidth(segment); Taro.nextTick(() => { try { Taro.createSelectorQuery() .select(`#${measureId}`) .boundingClientRect() .exec((res) => { const w = Number(res?.[0]?.width || 0); // weapp 若误测成容器宽,会接近视口;用估算兜底纠正 if (w > 8 && w < fallback * 2.5) { setSegWidth(Math.ceil(w)); } else { setSegWidth(fallback); } }); } catch { setSegWidth(fallback); } }); }, [segment, measureId]); useReady(() => { measure(); }); useEffect(() => { if (!segment) { setSegWidth(0); return; } setSegWidth(0); measure(); const t1 = setTimeout(measure, 100); const t2 = setTimeout(measure, 350); const t3 = setTimeout(() => { setSegWidth((prev) => (prev > 0 ? prev : estimateTextWidth(segment))); }, 600); return () => { clearTimeout(t1); clearTimeout(t2); clearTimeout(t3); }; }, [segment, measure]); if (!segment) return null; const running = segWidth > 0; const durationSec = Math.max(10, Math.min(90, segWidth / 42)); const trackStyle: CSSProperties | undefined = running ? { width: `${segWidth * 2}px`, animationDuration: `${durationSec}s`, // 微信 / Safari WebkitAnimationDuration: `${durationSec}s`, } : undefined; const segStyle: CSSProperties | undefined = running ? { width: `${segWidth}px` } : undefined; return ( {segment} {segment} ); }