fix(mini-user): 走马灯全文滚出视口后再多走 10px

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 17:52:10 +08:00
parent ff462bca9a
commit 7e3dc131ad
@@ -11,8 +11,16 @@ const MIN_FLY_MS = 2400;
const PAUSE_MIN_MS = 1000; const PAUSE_MIN_MS = 1000;
const PAUSE_MAX_MS = 5000; const PAUSE_MAX_MS = 5000;
const TICK_MS = 16; const TICK_MS = 16;
/** 飘动终点:left 停在此处(略向左溢出 10px */ /** 全文滚出视口后,再向左多走 10px */
const STOP_LEFT_PX = -10; 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() { function randomPauseMs() {
return PAUSE_MIN_MS + Math.floor(Math.random() * (PAUSE_MAX_MS - PAUSE_MIN_MS + 1)); 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<number> {
}); });
} }
function measureTextWidth(selector: string, text: string): Promise<number> {
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 秒随机再播下一条。 * 核销走马灯:单条从右向左位移飞出,间隔 1~5 秒随机再播下一条。
* *
@@ -68,6 +98,7 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) {
); );
const rootIdRef = useRef(`smr${Math.random().toString(36).slice(2, 10)}`); 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 indexRef = useRef(0);
const boxWidthRef = useRef(getBoxWidthFallback()); const boxWidthRef = useRef(getBoxWidthFallback());
const itemsKey = items.join('\n'); const itemsKey = items.join('\n');
@@ -128,16 +159,22 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) {
while (!cancelled && items.length) { while (!cancelled && items.length) {
const idx = indexRef.current % items.length; const idx = indexRef.current % items.length;
const text = items[idx];
const box = boxWidthRef.current; const box = boxWidthRef.current;
setDisplayIndex(idx); setDisplayIndex(idx);
const from = box; 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 distance = from - to;
const durationMs = Math.max(MIN_FLY_MS, Math.round((distance / FLY_SPEED) * 1000)); const durationMs = Math.max(MIN_FLY_MS, Math.round((distance / FLY_SPEED) * 1000));
setLeftPx(from);
await sleep(32); await sleep(32);
if (cancelled) break; if (cancelled) break;
@@ -169,7 +206,9 @@ export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) {
return ( return (
<View id={rootIdRef.current} className="store-detail-marquee"> <View id={rootIdRef.current} className="store-detail-marquee">
<View className="store-detail-marquee-inner" style={innerStyle}> <View className="store-detail-marquee-inner" style={innerStyle}>
<Text className="store-detail-marquee-text">{current}</Text> <Text id={textIdRef.current} className="store-detail-marquee-text">
{current}
</Text>
</View> </View>
</View> </View>
); );