| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- import { Image } from 'expo-image';
- import React, { useEffect, useRef, useState } from 'react';
- import { Animated, Easing, ImageBackground, StyleSheet, Text, View } from 'react-native';
- import { Images } from '@/constants/images';
- interface BarrageItemType {
- id?: string;
- poolId?: string;
- nickname?: string;
- poolName?: string;
- text?: string; // amount or feedback
- type?: string; // '奖池'
- avatar?: string;
- }
- interface BarrageProps {
- data: BarrageItemType[];
- speed?: number; // ms per pixel? Or just a factor.
- style?: any;
- }
- export const Barrage: React.FC<BarrageProps> = ({ data, speed = 30, style }) => {
- const [contentWidth, setContentWidth] = useState(0);
- const translateX = useRef(new Animated.Value(0)).current;
- useEffect(() => {
- if (contentWidth > 0 && data.length > 0) {
- startAnimation();
- }
- }, [contentWidth, data]);
- const startAnimation = () => {
- // Reset to 0
- translateX.setValue(0);
-
- // Duration: width * factor.
- // If width is 1000, speed 30 -> 30000ms (30s).
- const duration = contentWidth * speed;
- Animated.loop(
- Animated.timing(translateX, {
- toValue: -contentWidth,
- duration: duration,
- easing: Easing.linear,
- useNativeDriver: true,
- })
- ).start();
- };
- const renderItem = (item: BarrageItemType, index: number) => {
- const isPool = item.poolId && Number(item.text || '0') > 0;
- // item.type == '奖池' logic from Vue
-
- return (
- <ImageBackground
- key={`${item.id || index}-main`}
- source={{ uri: Images.box.barrageItem }}
- style={styles.itemBg}
- resizeMode="stretch"
- >
- <View style={styles.contentRow}>
- {/* Avatar */}
- <View style={styles.avatarBox}>
- <Image source={{ uri: item.avatar }} style={styles.avatar} contentFit="cover" />
- </View>
-
- {/* Text Content */}
- <View style={styles.textContainer}>
- {isPool ? (
- <Text style={styles.text} numberOfLines={1}>
- <Text style={styles.nickname}>{item.nickname?.slice(0,1) + '***' + item.nickname?.slice(-1)}</Text>
- <Text> 在 </Text>
- <Text style={styles.poolName}>{item.poolName}</Text>
- <Text> {item.type === '奖池' ? '消费了' : '获得'} </Text>
- <Text style={styles.amount}>{item.text}</Text>
- <Text> {item.type === '奖池' ? '元' : ''} </Text>
- </Text>
- ) : (
- <Text style={styles.text} numberOfLines={1}>
- <Text style={styles.nickname}>{item.nickname?.slice(0,1) + '***' + item.nickname?.slice(-1)}</Text>
- <Text>: {item.text}</Text>
- </Text>
- )}
- </View>
- </View>
- </ImageBackground>
- );
- };
- if (!data || data.length === 0) return null;
- return (
- <View style={[styles.container, style]}>
- <Animated.View
- style={[
- styles.scrollContainer,
- { transform: [{ translateX }] },
- ]}
- >
- {/* Measure the width of the first set */}
- <View
- style={styles.row}
- onLayout={(e) => setContentWidth(e.nativeEvent.layout.width)}
- >
- {data.map((item, index) => renderItem(item, index))}
- </View>
- {/* Duplicate set for seamless loop */}
- <View style={styles.row}>
- {data.map((item, index) => renderItem(item, index))}
- </View>
- </Animated.View>
- </View>
- );
- };
- const styles = StyleSheet.create({
- container: {
- width: '100%',
- overflow: 'hidden',
- height: 40, // Adjust based on item height
- },
- scrollContainer: {
- flexDirection: 'row',
- },
- row: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- itemBg: {
- paddingHorizontal: 12, // Reduced padding
- paddingVertical: 4,
- marginRight: 10,
- justifyContent: 'center',
- height: 32, // Fixed height
- minWidth: 150,
- },
- contentRow: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- avatarBox: {
- marginRight: 6,
- width: 24,
- height: 24,
- borderRadius: 12,
- borderWidth: 1,
- borderColor: '#000',
- overflow: 'hidden',
- backgroundColor: '#fff',
- },
- avatar: {
- width: '100%',
- height: '100%',
- },
- textContainer: {
- justifyContent: 'center',
- },
- text: {
- color: '#fff',
- fontSize: 10,
- },
- nickname: {
- fontWeight: 'bold',
- },
- poolName: {
- color: '#0084FF',
- fontSize: 10,
- },
- amount: {
- color: '#FF0000',
- fontSize: 11,
- fontWeight: 'bold',
- }
- });
|