編寫不易, 希望大家點贊
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) }} />
