import React, { Component, PureComponent } from 'react'
import { View, Text, Animated, ScrollView, PanResponder, Dimensions, StyleSheet,} from 'react-native'
const { width, height } = Dimensions.get("window")
export default class RNSwiper extends Component {
static defaultProps = {
slideStyle: {},
loop: true,
autoPlay: true,
autoPlayTimeOut: 3000,
initIndex: 0,
onChangeIndex: () => { },
renderPagination: () => {},
animation: (value, toValue) => {
return Animated.spring(value, {
toValue: toValue,
friction: 10,
tension: 50,
useNativeDriver: true
});
},
}
scrollViewRef;
dx = 0;
autoPlayTimer;
state = {
activeIndex: 1,
scrollValue: new Animated.Value(-styles.slideStyle.width),
}
componentWillMount() {
this._panResponder = PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => { },
onPanResponderMove: (evt, gestureState) => {
this.getLoopStatus(gestureState) ? this.onPanResponderMove(gestureState.dx) : null
this.stopAutoPlay();
},
onPanResponderTerminationRequest: (evt, gestureState) => true,
onPanResponderRelease: (evt, gestureState) => {
let nextStatu = this.computeNextOrPrev(gestureState)
this.getLoopStatus(gestureState) ? nextStatu ? this.goPage(nextStatu) : null : null;
this.props.autoPlay
? (this.stopAutoPlay(),
this.startAutoPlay())
: null;
},
onPanResponderTerminate: (evt, gestureState) => {
// 另一个组件已经成为了新的响应者,所以当前手势将被取消。
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return true;
},
});
}
componentWillReceiveProps (nextProps, nextState) {
if (nextProps.autoplay) {
this.startAutoPlay();
} else {
this.stopAutoPlay();
}
}
componentDidMount () {
const { autoPlay, initIndex } = this.props
let index = (initIndex > this.getPageNumber() - 2 || initIndex < 0)
? (this.getPageNumber() - 2, this.console('warn', 'The props “initIndex” is out of range!'))
: initIndex;
console.log('index', index)
if (autoPlay) {
this.startAutoPlay()
} else {
this.stopAutoPlay()
}
this.goPage('Next', index, false)
}
startAutoPlay = () => {
this.stopAutoPlay();
this.autoPlayTimer = setInterval(() => {
this.goPage('Next')
}, this.props.autoPlayTimeOut)
}
stopAutoPlay = () => {
if (this.autoPlayTimer) {
clearInterval(this.autoPlayTimer)
}
}
onPanResponderMove = (dx) => {
let childrenWidth = styles.slideStyle.width;
const { activeIndex } = this.state
this.state.scrollValue.setValue(-activeIndex * childrenWidth + dx)
}
computeNextOrPrev = (gestureState) =>{
let dx = gestureState.dx
if (dx === 0) return // 单纯的点击事件
return dx > 0 ? 'Prev' : 'Next';
}
goPage = (nextStatu = 'Next', index = undefined, animate = true) => {
let { activeIndex } = this.state
let childrenWidth = styles.slideStyle.width;
let nextIndex = index !== undefined ? index : nextStatu === 'Prev' ? --activeIndex : ++activeIndex;
this.getNowIndex(nextStatu, nextIndex)
if (animate) {
this.startGoPageAnimated(nextStatu, nextIndex)
} else {
let initIndex = nextIndex === 0 ? 1 : nextIndex === this.getPageNumber() - 2 ? 0 : ++nextIndex;
this.setState({ activeIndex: initIndex }, () => { this.state.scrollValue.setValue(-initIndex * childrenWidth) })
}
}
startGoPageAnimated = (nextStatu, nextIndex) => {
let pageNum = this.getPageNumber();
let childrenWidth = styles.slideStyle.width;
this.setState({ activeIndex: nextIndex }, () => {
this.props.animation(this.state.scrollValue, -nextIndex * childrenWidth).start(() => {
// 上一页
nextIndex <= 0 && nextStatu === 'Prev'
? this.setState({ activeIndex: pageNum - 2 }, () => { this.state.scrollValue.setValue(-childrenWidth * ( pageNum - 2 )) }) // 滑动动画
: this.state.scrollValue.setValue(-nextIndex * childrenWidth); // 超出边界重置位置
// 下一页
nextIndex >= pageNum - 1 && nextStatu === 'Next'
? this.setState({ activeIndex: 1 }, () => { this.state.scrollValue.setValue(-childrenWidth); })
: this.state.scrollValue.setValue(-nextIndex * childrenWidth)
});
})
}
getLoopStatus = (gestureState) => {
let { activeIndex } = this.state
let { loop } = this.props
let nextStatu = this.computeNextOrPrev(gestureState)
let nextIndex = nextStatu === 'Prev' ? --activeIndex : ++activeIndex;
let pageNum = this.getPageNumber();
let isLastPage = (nextIndex <= 0 || nextIndex >= pageNum - 1);
return isLastPage ? isLastPage && loop : true;
}
getNowIndex = (status, index) => {
const { onChangeIndex } = this.props
const childLength = this.getPageNumber() - 2;
status === 'Prev'
? onChangeIndex(index <= 0 ? childLength - 1 : index - 1)
: onChangeIndex(index > childLength ? 0 : index - 1)
}
getPageArr = () => {
const { children } = this.props
let childArr = React.Children.toArray(children)
childArr.unshift(childArr[childArr.length - 1]);
childArr.push(childArr[1]);
return childArr
}
getPageNumber = () => {
return this.getPageArr().length
}
console = (type = 'log', msg = '') => {
console[type](msg);
}
render (){
const { slideStyle, renderPagination } = this.props
const { activeIndex } = this.state
let pageArr = this.getPageArr();
let pageNum = this.getPageNumber();
const transform = [{ translateX: this.state.scrollValue }]
let pageIndex = activeIndex > this.getPageNumber() - 2 ? 0 : activeIndex <= 0 ? this.getPageNumber() - 3 : activeIndex - 1;
const content = (
{
pageArr.map((item,index) => {
return (
{item}
)
})
}
)
return (
{this.scrollViewRef = ref}}
horizontal
scrollEnabled={false}
scrollEventThrottle = {200}
>
{ content }
{
renderPagination(pageIndex)
}
)
}
}
const styles = StyleSheet.create({
flex_start: { justifyContent: 'flex-start', flexDirection: 'row', alignItems: 'center'},
flex_center: { justifyContent: 'center', alignItems: 'center' },
slideStyle: { height: 200, width, backgroundColor: 'red' },
})