编写不易, 希望大家点赞
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) }} />