Sin descripción

RNSwiper.js 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import React, { Component, PureComponent } from 'react'
  2. import { View, Text, Animated, ScrollView, PanResponder, Dimensions, StyleSheet,} from 'react-native'
  3. const { width, height } = Dimensions.get("window")
  4. export default class RNSwiper extends Component {
  5. static defaultProps = {
  6. slideStyle: {},
  7. loop: true,
  8. autoPlay: true,
  9. autoPlayTimeOut: 3000,
  10. initIndex: 0,
  11. onChangeIndex: () => { },
  12. renderPagination: () => {},
  13. animation: (value, toValue) => {
  14. return Animated.spring(value, {
  15. toValue: toValue,
  16. friction: 10,
  17. tension: 50,
  18. useNativeDriver: true
  19. });
  20. },
  21. }
  22. scrollViewRef;
  23. dx = 0;
  24. autoPlayTimer;
  25. state = {
  26. activeIndex: 1,
  27. scrollValue: new Animated.Value(-styles.slideStyle.width),
  28. }
  29. componentWillMount() {
  30. this._panResponder = PanResponder.create({
  31. // 要求成为响应者:
  32. onStartShouldSetPanResponder: (evt, gestureState) => true,
  33. onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
  34. onMoveShouldSetPanResponder: (evt, gestureState) => true,
  35. onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
  36. onPanResponderGrant: (evt, gestureState) => { },
  37. onPanResponderMove: (evt, gestureState) => {
  38. this.getLoopStatus(gestureState) ? this.onPanResponderMove(gestureState.dx) : null
  39. this.stopAutoPlay();
  40. },
  41. onPanResponderTerminationRequest: (evt, gestureState) => true,
  42. onPanResponderRelease: (evt, gestureState) => {
  43. let nextStatu = this.computeNextOrPrev(gestureState)
  44. this.getLoopStatus(gestureState) ? nextStatu ? this.goPage(nextStatu) : null : null;
  45. this.props.autoPlay
  46. ? (this.stopAutoPlay(),
  47. this.startAutoPlay())
  48. : null;
  49. },
  50. onPanResponderTerminate: (evt, gestureState) => {
  51. // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
  52. },
  53. onShouldBlockNativeResponder: (evt, gestureState) => {
  54. return true;
  55. },
  56. });
  57. }
  58. componentWillReceiveProps (nextProps, nextState) {
  59. if (nextProps.autoplay) {
  60. this.startAutoPlay();
  61. } else {
  62. this.stopAutoPlay();
  63. }
  64. }
  65. componentDidMount () {
  66. const { autoPlay, initIndex } = this.props
  67. let index = (initIndex > this.getPageNumber() - 2 || initIndex < 0)
  68. ? (this.getPageNumber() - 2, this.console('warn', 'The props “initIndex” is out of range!'))
  69. : initIndex;
  70. console.log('index', index)
  71. if (autoPlay) {
  72. this.startAutoPlay()
  73. } else {
  74. this.stopAutoPlay()
  75. }
  76. this.goPage('Next', index, false)
  77. }
  78. startAutoPlay = () => {
  79. this.stopAutoPlay();
  80. this.autoPlayTimer = setInterval(() => {
  81. this.goPage('Next')
  82. }, this.props.autoPlayTimeOut)
  83. }
  84. stopAutoPlay = () => {
  85. if (this.autoPlayTimer) {
  86. clearInterval(this.autoPlayTimer)
  87. }
  88. }
  89. onPanResponderMove = (dx) => {
  90. let childrenWidth = styles.slideStyle.width;
  91. const { activeIndex } = this.state
  92. this.state.scrollValue.setValue(-activeIndex * childrenWidth + dx)
  93. }
  94. computeNextOrPrev = (gestureState) =>{
  95. let dx = gestureState.dx
  96. if (dx === 0) return // 单纯的点击事件
  97. return dx > 0 ? 'Prev' : 'Next';
  98. }
  99. goPage = (nextStatu = 'Next', index = undefined, animate = true) => {
  100. let { activeIndex } = this.state
  101. let childrenWidth = styles.slideStyle.width;
  102. let nextIndex = index !== undefined ? index : nextStatu === 'Prev' ? --activeIndex : ++activeIndex;
  103. this.getNowIndex(nextStatu, nextIndex)
  104. if (animate) {
  105. this.startGoPageAnimated(nextStatu, nextIndex)
  106. } else {
  107. let initIndex = nextIndex === 0 ? 1 : nextIndex === this.getPageNumber() - 2 ? 0 : ++nextIndex;
  108. this.setState({ activeIndex: initIndex }, () => { this.state.scrollValue.setValue(-initIndex * childrenWidth) })
  109. }
  110. }
  111. startGoPageAnimated = (nextStatu, nextIndex) => {
  112. let pageNum = this.getPageNumber();
  113. let childrenWidth = styles.slideStyle.width;
  114. this.setState({ activeIndex: nextIndex }, () => {
  115. this.props.animation(this.state.scrollValue, -nextIndex * childrenWidth).start(() => {
  116. // 上一页
  117. nextIndex <= 0 && nextStatu === 'Prev'
  118. ? this.setState({ activeIndex: pageNum - 2 }, () => { this.state.scrollValue.setValue(-childrenWidth * ( pageNum - 2 )) }) // 滑动动画
  119. : this.state.scrollValue.setValue(-nextIndex * childrenWidth); // 超出边界重置位置
  120. // 下一页
  121. nextIndex >= pageNum - 1 && nextStatu === 'Next'
  122. ? this.setState({ activeIndex: 1 }, () => { this.state.scrollValue.setValue(-childrenWidth); })
  123. : this.state.scrollValue.setValue(-nextIndex * childrenWidth)
  124. });
  125. })
  126. }
  127. getLoopStatus = (gestureState) => {
  128. let { activeIndex } = this.state
  129. let { loop } = this.props
  130. let nextStatu = this.computeNextOrPrev(gestureState)
  131. let nextIndex = nextStatu === 'Prev' ? --activeIndex : ++activeIndex;
  132. let pageNum = this.getPageNumber();
  133. let isLastPage = (nextIndex <= 0 || nextIndex >= pageNum - 1);
  134. return isLastPage ? isLastPage && loop : true;
  135. }
  136. getNowIndex = (status, index) => {
  137. const { onChangeIndex } = this.props
  138. const childLength = this.getPageNumber() - 2;
  139. status === 'Prev'
  140. ? onChangeIndex(index <= 0 ? childLength - 1 : index - 1)
  141. : onChangeIndex(index > childLength ? 0 : index - 1)
  142. }
  143. getPageArr = () => {
  144. const { children } = this.props
  145. let childArr = React.Children.toArray(children)
  146. childArr.unshift(childArr[childArr.length - 1]);
  147. childArr.push(childArr[1]);
  148. return childArr
  149. }
  150. getPageNumber = () => {
  151. return this.getPageArr().length
  152. }
  153. console = (type = 'log', msg = '') => {
  154. console[type](msg);
  155. }
  156. render (){
  157. const { slideStyle, renderPagination } = this.props
  158. const { activeIndex } = this.state
  159. let pageArr = this.getPageArr();
  160. let pageNum = this.getPageNumber();
  161. const transform = [{ translateX: this.state.scrollValue }]
  162. let pageIndex = activeIndex > this.getPageNumber() - 2 ? 0 : activeIndex <= 0 ? this.getPageNumber() - 3 : activeIndex - 1;
  163. const content = (
  164. <Animated.View
  165. style={[styles.flex_start,{ width: pageNum * width, transform}]}
  166. {...this._panResponder.panHandlers}
  167. >
  168. {
  169. pageArr.map((item,index) => {
  170. return (
  171. <View key={index} style={[styles.slideStyle, styles.flex_center, slideStyle]}>
  172. {item}
  173. </View>
  174. )
  175. })
  176. }
  177. </Animated.View>
  178. )
  179. return (
  180. <ScrollView
  181. ref={(ref) => {this.scrollViewRef = ref}}
  182. horizontal
  183. scrollEnabled={false}
  184. scrollEventThrottle = {200}
  185. >
  186. { content }
  187. <View style={[styles.slideStyle, { position: 'absolute',backgroundColor: 'transparents' }]}>
  188. {
  189. renderPagination(pageIndex)
  190. }
  191. </View>
  192. </ScrollView>
  193. )
  194. }
  195. }
  196. const styles = StyleSheet.create({
  197. flex_start: { justifyContent: 'flex-start', flexDirection: 'row', alignItems: 'center'},
  198. flex_center: { justifyContent: 'center', alignItems: 'center' },
  199. slideStyle: { height: 200, width, backgroundColor: 'red' },
  200. })