编写不易, 希望大家点赞
import React, {PureComponent} from 'react'; import {Animated, Easing, View} from 'react-native'; export default class NoticeScroll extends PureComponent { constructor(props) { super(props); this.state = { newChildren: this.props.children, }; this.animation = new Animated.Value(0); this.direction = this.props.direction === 'vertical' ? 'height' : 'width'; this.transationValue = this.props.styles[this.direction]; this.key = 0; this.arr = []; } startAnimation() { const meter = this.props.meter || 0; Animated.timing(this.animation, { toValue: -this.transationValue + meter, duration: this.props.scrolltime || 5000, easing: Easing.linear, useNativeDriver: true, }).start(() => { this.animation = new Animated.Value(0); this.initPosition(); this.startAnimation(); }); } initPosition() { this.key++; if (this.key < 2) { // React.Children.forEach(this.props.children, (child, index) => { // let props = { // key: `${this.key}${index}`, // ...child.props // }; // this.arr.push(React.cloneElement(child, props)); // });
React.Children.forEach(this.props.children, (child, index) => { let newProps = { key: `${this.key}${index}flag`, ...child.props, }; this.arr.push(React.cloneElement(child, newProps)); }); } this.setState({ newChildren: [...this.arr], }); } componentDidMount() { this.initPosition(); this.startAnimation(); } componentWillUnmount() { this.startAnimation = () => {}; } render() { const {styles, direction} = this.props; const {newChildren} = this.state; return ( <View style={{overflow: 'hidden', height: 35, justifyContent: 'center'}}>
<Animated.View style={{ transform: [ direction !== 'vertical'
? {translateX: this.animation} : {translateY: this.animation}, ], flexDirection: 'row', }}> {newChildren} </Animated.View>
</View> ); } }
组件可以在React中直接使用,把Animated.View 改成Animated.div即可
此代码有一个不好的地方,就是只能读取本地的,否则会有延迟
优化代码!!!

import React, { Component } from 'react'; import { View, Animated, Easing, Text, TouchableOpacity, InteractionManager } from 'react-native'; const styles = { bgContainerStyle : { flexDirection : 'row', alignItems : 'center', justifyContent : 'flex-start', backgroundColor : '#FFFFFF', overflow : 'hidden' }, textMeasuringViewStyle: { flexDirection : 'row', opacity : 0, }, textMeasuringTextStyle: { fontSize : 16, }, textStyle : { fontSize : 16, color : '#000000', } }; export default class ScrollAnnounce extends Component { constructor(props) { super(props); this.state = { animation : null, textList : [], textWidth : 0, viewWidth : 0, start:false } } static defaultProps = { duration : 10000, speed : 0, textList : [], width : 375, height : 50, direction : 'left', reverse : false, separator : 20, onTextClick : () => {}, } componentWillMount(){ this.setState({ textList : this.props.textList || [], }) this.animatedTransformX = new Animated.Value(0); } componentDidUpdate(){ let { textWidth, viewWidth } = this.state; let { duration, speed, width, direction } = this.props; let mDuration = duration; if(speed && speed > 0){ mDuration = (width + textWidth) / speed * 1000; } if(!this.state.animation && textWidth && viewWidth){ this.animatedTransformX.setValue(direction == 'left' ? width : (direction == 'right' ? -textWidth : width)); this.setState({ animation : Animated.timing(this.animatedTransformX, { toValue: direction == 'left' ? -textWidth : (direction == 'right' ? width : -textWidth), duration: mDuration, useNativeDriver: true, easing: Easing.linear, }), }, () => { this.state.animation && this.state.animation.start(() => { this.setState({ animation: null, }); }); }) } } componentWillReceiveProps(nextProps){ let newText = nextProps.textList || []; let oldText = this.props.textList || []; if (newText !== oldText) { this.state.animation && this.state.animation.stop(); this.setState({ textList : newText, animation: null, start:true }); } } componentWillUnmount(){ this.state.animation && this.state.animation.stop(); } textOnLayout = (e) => { let width = e.nativeEvent.layout.width; let { textList, separator } = this.props; this.setState({ textWidth : width + ((textList.length - 1) * separator), }) } viewOnLayout = (e) => { let width = e.nativeEvent.layout.width; this.setState({ viewWidth : width, }) } textView(list){ let { textStyle, onTextClick, reverse, separator } = this.props; let itemView = []; for(let i = 0;i<list.length;i++){ let item = list[i]; if(reverse){ item.value = item.value.split("").reverse().join(""); } itemView.push( <TouchableOpacity key = {''+i} activeOpacity = {0.9} onPress = {() => { onTextClick(item) }}> <View style = {{flexDirection : 'row',marginRight : i < list.length - 1 ? separator : 0}}> <Text style = {{ ...styles.textStyle, ...textStyle }} numberOfLines = {1} >{item.value}</Text> </View> </TouchableOpacity> ); } return( <Animated.View style = {{flexDirection : 'row',width : this.state.textWidth,transform: [{ translateX: this.animatedTransformX }]}} onLayout={(event) => this.viewOnLayout(event)} > {itemView} </Animated.View> ) } textLengthView(list){ let { textStyle } = this.props; let text = ''; for(let i = 0;i<list.length;i++){ text += list[i].value; } return( <View style = {{ ...styles.textMeasuringViewStyle, width : list.length * 1024 }}> <Text style = {{ ...styles.textMeasuringTextStyle, ...textStyle }} onLayout={(event) => this.textOnLayout(event)} numberOfLines = {1} >{text}</Text> </View> ) } render(){ let { width, height, bgContainerStyle } = this.props; let { textList,start } = this.state; return( <View style = {{ ...styles.bgContainerStyle, width : width, height : height, ...bgContainerStyle, }} opacity = {this.state.animation ? 1 : 0}> {start&&this.textView(textList) } {start&&this.textLengthView(textList) } </View> ) } }
<ScrollAnnounce textList = {noticeList} speed = {60} width = {Platform.OS==='ios'?autoWidth(283):autoWidth(295)} height = {30} direction = {'left'} reverse = {false} bgContainerStyle = {{backgroundColor : '#f8f8f8'}} textStyle = {{fontSize : 12,color : '#D1B793'}} onTextClick = {(item) => { this._toDetail(item) }} />