diff --git a/apps/mini-user/src/components/StoreRedeemMarquee.tsx b/apps/mini-user/src/components/StoreRedeemMarquee.tsx index f8f1de7..c6d6915 100644 --- a/apps/mini-user/src/components/StoreRedeemMarquee.tsx +++ b/apps/mini-user/src/components/StoreRedeemMarquee.tsx @@ -11,8 +11,16 @@ const MIN_FLY_MS = 2400; const PAUSE_MIN_MS = 1000; const PAUSE_MAX_MS = 5000; const TICK_MS = 16; -/** 飘动终点:left 停在此处(略向左溢出 10px) */ -const STOP_LEFT_PX = -10; +/** 全文滚出视口后,再向左多走 10px */ +const EXTRA_AFTER_EXIT_PX = 10; + +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); +} function randomPauseMs() { return PAUSE_MIN_MS + Math.floor(Math.random() * (PAUSE_MAX_MS - PAUSE_MIN_MS + 1)); @@ -50,6 +58,28 @@ function measureBoxWidth(selector: string, fallback: number): Promise { }); } +function measureTextWidth(selector: string, text: string): Promise { + const fallback = estimateTextWidth(text); + return new Promise((resolve) => { + Taro.nextTick(() => { + try { + const page = Taro.getCurrentInstance().page; + const query = page ? Taro.createSelectorQuery().in(page) : Taro.createSelectorQuery(); + query + .select(selector) + .boundingClientRect() + .exec((res) => { + const w = Number(res?.[0]?.width || 0); + if (w > 8 && w < fallback * 3) resolve(Math.ceil(w)); + else resolve(fallback); + }); + } catch { + resolve(fallback); + } + }); + }); +} + /** * 核销走马灯:单条从右向左位移飞出,间隔 1~5 秒随机再播下一条。 * @@ -68,6 +98,7 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) { ); const rootIdRef = useRef(`smr${Math.random().toString(36).slice(2, 10)}`); + const textIdRef = useRef(`smt${Math.random().toString(36).slice(2, 10)}`); const indexRef = useRef(0); const boxWidthRef = useRef(getBoxWidthFallback()); const itemsKey = items.join('\n'); @@ -128,16 +159,22 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) { while (!cancelled && items.length) { const idx = indexRef.current % items.length; + const text = items[idx]; const box = boxWidthRef.current; setDisplayIndex(idx); const from = box; - const to = STOP_LEFT_PX; + setLeftPx(from); + await sleep(48); + if (cancelled) break; + + const textW = await measureTextWidth(`#${textIdRef.current}`, text); + // 全文 left 边缘移出容器左边界后再走 10px + const to = -(textW + EXTRA_AFTER_EXIT_PX); const distance = from - to; const durationMs = Math.max(MIN_FLY_MS, Math.round((distance / FLY_SPEED) * 1000)); - setLeftPx(from); await sleep(32); if (cancelled) break; @@ -169,7 +206,9 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) { return ( - {current} + + {current} + );