UNPKG

rn-collie-swiper

Version:

A swiper for react native,indicator is in rn-collie

271 lines (246 loc) 7.89 kB
import React, {Component} from 'react' import { View, } from 'react-native' import ViewPager from "@react-native-community/viewpager"; import type { PageScrollEvent, PageSelectedEvent, PageScrollStateChangedEvent } from './Type'; type Props = { //是否无限循环 loop?: boolean, //自动滚动 autoPlay?: boolean, //自动滚动的毫秒数 interval?: number, //页面滚动的回调 onSwiperScroll?: Function<number>, //页面被选择的回调 onSelectedChange?: Function<number>, } type State = { loop: boolean } /** * 可切换的滚动组件 * 支持无限滚动 * 支持自动滚动 * 支持对外输出滚动的百分比 * 支持对外输出滚动的位置 */ export default class Swiper extends Component<Props, State> { static defaultProps = { loop: false, autoPlay: false, interval: 3000, }; //viewPage的ref viewPager: ViewPager = null; //页数 pageCount: number = 0; //viewPage中真实的页数,loop的话头部和尾部个多一个,即pageCount+2 realCount: number = 0; //viewPage中真实的位置 realPosition: number = 1; constructor(props) { super(props); this.state = { style: {}, ...props, }; this.initPops(props); this.startTimer(); } /** * 滚动到初始位置,因为loop的情况下,实际上第一个元素在第二个位置 */ componentDidMount(): void { setTimeout(() => this.scrollToIndex(0, false), 6); } componentWillUnmount() { this.stopTimer(); } componentWillReceiveProps(nextProps) { this.initPops(nextProps, false); this.startTimer(); } /** * 计算各字段的值 * @param props * @param init */ initPops(props, init = true) { let {children} = props; this.pageCount = children ? (children instanceof Array ? children.length : 1) : 0; this.realCount = (this.pageCount > 1 && props.loop) ? this.pageCount + 2 : this.pageCount; if (this.pageCount === 1) { if (init) { this.state = {...this.state, loop: false}; } else { this.setState({loop: false}); } } else if (!init) { this.setState({...props}); } } setPage(index: number, animal = false) { this.scrollToIndex(index, animal) } /** * loop的情况下,自动滚动的话,开启定时器进行自动滚动 */ startTimer() { this.stopTimer(); if (!this.props.autoPlay) { return; } this.timer = setTimeout(() => { this.timer = null; let toIndex = this.realPosition + 1; if (toIndex > this.realCount - 1) { toIndex = this.realCount - 1; } this.viewPager.setPage(toIndex); this.startTimer(); }, this.props.interval); } stopTimer() { if (this.timer) { clearTimeout(this.timer); this.timer = null; } } /** * 滚动对应的位置loop的页码和真实的页面是不同的,所以需要对真实位置的修正 * @param pageIndex 需要滚动的页码,从0开始 * @param animated 是否有动画 */ scrollToIndex(pageIndex, animated = true) { let {loop} = this.state; if (loop) { if (pageIndex <= 0) { pageIndex = 1; } else if (pageIndex >= this.pageCount - 1) { pageIndex = this.pageCount; } else { pageIndex += 1; } } if (this.viewPager) { if (animated) { this.viewPager.setPage(pageIndex); } else { this.viewPager.setPageWithoutAnimation(pageIndex); } } } /** * 当页面滚动时,对外传递滚动了多少 * 假设有三个页面,序号为0, 1 ,2 * 非loop情况下 0 0.1 0.2 1 1.1 1.2 2 * loop情况下 -0.8 -0.2 0 0.1 0.2 1 1.1 1.2 2 2.1 2.5 * @param e 滚动的事件 */ onPageScroll(e: PageScrollEvent) { let position = e.nativeEvent.position; let process; if (this.state.loop) { if (position === 0) { process = e.nativeEvent.offset - 1; } else if (position >= this.realCount - 1) { position = 0; process = position + e.nativeEvent.offset; } else { position -= 1; process = position + e.nativeEvent.offset; } } else { process = position + e.nativeEvent.offset; } this.props.onSwiperScroll && this.props.onSwiperScroll(process); } /** * 当页面选择情况下 * 非loop的情况下index不需要修正的 * loop的情况下首尾个多了一个页面,所以需要修正并传递给外界 * @param e 页面选择的事件 */ onPageSelected(e: PageSelectedEvent) { this.realPosition = e.nativeEvent.position; let index = this.realPosition; if (this.state.loop) { if (this.realPosition === 0) { index = this.pageCount - 1; } else if (this.realPosition >= this.realCount - 1) { index = 0; } else { index -= 1; } } this.props.onSelectedChange && this.props.onSelectedChange(index); } /** * 当页面完全静止不动时,如果在首尾两个页面的话, * 为了能够继续无限循环,则要偷偷的滚动到真实的位置上面去 * @param e */ onPageScrollStateChanged(e: PageScrollStateChangedEvent) { if (e.nativeEvent.pageScrollState === 'idle') { if (this.state.loop) { if (this.realPosition === 0) { this.scrollToIndex(this.pageCount - 1, false); } else if (this.realPosition === this.realCount - 1) { this.scrollToIndex(0, false); } } } } /** * 渲染所有的子页面 * 如果是loop的情况下, * 前面要增加一个最后的页面 * 后面要增加一个第一个页面 * 在滚动这两个添加的页面的时候, 悄悄的更换到实际的页面,以达到无限滚动的目的 * @returns {[]} */ renderChild() { let {loop} = this.state; let {children} = this.props; if (!(children instanceof Array)) { children = [children]; } let pages = []; let pageStyle = {flex: 1}; loop && pages.push( <View style={pageStyle} key={'page-left'}>{children[children.length - 1]}</View>, ); children.map((item, index) => pages.push( <View style={pageStyle} key={'page-' + index}>{item}</View>, )); loop && pages.push( <View style={pageStyle} key={'page-right'}>{children[0]}</View>, ); return pages; } render() { let {style, ...others} = this.state; return ( <ViewPager ref={(ref) => this.viewPager = ref} style={style} initialPage={0} scrollEnabled={true} orientation="horizontal" transitionStyle="scroll" onPageScroll={this.onPageScroll.bind(this)} onPageSelected={this.onPageSelected.bind(this)} onPageScrollStateChanged={this.onPageScrollStateChanged.bind(this)} {...others} showPageIndicator={false}> {this.renderChild()} </ViewPager> ); } }