React-native/React 公告滚动组件(原生代码)
编写不易, 希望大家点赞
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) }} />