fix(mini-user): 门店核销走马灯改用 JS 位移
微信端 CSS max-content/百分比 transform 不可靠;改为测量宽度后 translateX 滚动,并上移到店名下方。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro from '@tarojs/taro';
|
||||
|
||||
type StoreRedeemMarqueeProps = {
|
||||
lines: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* 横向走马灯(JS 位移)。
|
||||
* 微信小程序对 CSS max-content / translateX(-50%) 宽度计算不稳定,故不用纯 CSS 动画。
|
||||
*/
|
||||
export default function StoreRedeemMarquee({ lines }: StoreRedeemMarqueeProps) {
|
||||
const text = useMemo(
|
||||
() =>
|
||||
lines
|
||||
.map((s) => String(s || '').trim())
|
||||
.filter(Boolean)
|
||||
.join(' '),
|
||||
[lines],
|
||||
);
|
||||
const [offset, setOffset] = useState(0);
|
||||
const halfWidthRef = useRef(0);
|
||||
const measureIdRef = useRef(`m${Date.now().toString(36)}`);
|
||||
|
||||
useEffect(() => {
|
||||
setOffset(0);
|
||||
halfWidthRef.current = 0;
|
||||
if (!text) return;
|
||||
|
||||
const measure = () => {
|
||||
Taro.createSelectorQuery()
|
||||
.select(`#${measureIdRef.current}`)
|
||||
.boundingClientRect()
|
||||
.exec((res) => {
|
||||
const width = Number(res?.[0]?.width || 0);
|
||||
if (width > 0) halfWidthRef.current = width;
|
||||
});
|
||||
};
|
||||
|
||||
measure();
|
||||
const t1 = setTimeout(measure, 80);
|
||||
const t2 = setTimeout(measure, 320);
|
||||
return () => {
|
||||
clearTimeout(t1);
|
||||
clearTimeout(t2);
|
||||
};
|
||||
}, [text]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!text) return;
|
||||
const speedPxPerSec = 48;
|
||||
const intervalMs = 32;
|
||||
const step = (speedPxPerSec * intervalMs) / 1000;
|
||||
const timer = setInterval(() => {
|
||||
setOffset((prev) => {
|
||||
const half = halfWidthRef.current || Math.max(text.length * 7, 120);
|
||||
const next = prev - step;
|
||||
return -next >= half ? 0 : next;
|
||||
});
|
||||
}, intervalMs);
|
||||
return () => clearInterval(timer);
|
||||
}, [text]);
|
||||
|
||||
if (!text) return null;
|
||||
|
||||
const segment = `${text} `;
|
||||
|
||||
return (
|
||||
<View className="store-detail-marquee">
|
||||
<View
|
||||
className="store-detail-marquee-track"
|
||||
style={{ transform: `translateX(${offset}px)` }}
|
||||
>
|
||||
<View id={measureIdRef.current} className="store-detail-marquee-item">
|
||||
<Text className="store-detail-marquee-item-text">{segment}</Text>
|
||||
</View>
|
||||
<View className="store-detail-marquee-item">
|
||||
<Text className="store-detail-marquee-item-text">{segment}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user