diff --git a/package.json b/package.json index 4119d541..c1a84554 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,8 @@ "bugs": { "url": "https://github.com/zbtang/React-Native-ViewPager/issues" }, - "homepage": "https://github.com/zbtang/React-Native-ViewPager#readme" + "homepage": "https://github.com/zbtang/React-Native-ViewPager#readme", + "dependencies": { + "@react-native-community/viewpager": "^1.1.7" + } } diff --git a/viewpager/IndicatorViewPager.js b/viewpager/IndicatorViewPager.js index ad300fb4..44e7516f 100644 --- a/viewpager/IndicatorViewPager.js +++ b/viewpager/IndicatorViewPager.js @@ -2,129 +2,132 @@ * Created by tangzhibin on 16/3/23. */ -'use strict' +"use strict"; -import React, { Component } from 'react' -import PropTypes from 'prop-types' -import { StyleSheet, View, ViewPropTypes } from 'react-native' -import ViewPager from './ViewPager' +import React, { Component } from "react"; +import PropTypes from "prop-types"; +import { StyleSheet, View, ViewPropTypes } from "react-native"; +import ViewPager from "./ViewPager"; -const VIEWPAGER_REF = 'viewPager' -const INDICATOR_REF = 'indicator' +const VIEWPAGER_REF = "viewPager"; +const INDICATOR_REF = "indicator"; export default class IndicatorViewPager extends Component { - static propTypes = { - ...ViewPager.propTypes, - indicator: PropTypes.node, - pagerStyle: ViewPropTypes.style, - autoPlayEnable: PropTypes.bool, - autoPlayInterval: PropTypes.number, - horizontalScroll: PropTypes.bool + static propTypes = { + ...ViewPager.propTypes, + indicator: PropTypes.node, + pagerStyle: ViewPropTypes.style, + autoPlayEnable: PropTypes.bool, + autoPlayInterval: PropTypes.number, + horizontalScroll: PropTypes.bool + }; + + static defaultProps = { + indicator: null, + initialPage: 0, + autoPlayInterval: 3000, + autoPlayEnable: false, + horizontalScroll: true + }; + + constructor(props) { + super(props); + this._onPageScroll = this._onPageScroll.bind(this); + this._onPageSelected = this._onPageSelected.bind(this); + this._goToNextPage = this._goToNextPage.bind(this); + this._renderIndicator = this._renderIndicator.bind(this); + this.setPage = this.setPage.bind(this); + this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this); + this._startAutoPlay = this._startAutoPlay.bind(this); + this._stopAutoPlay = this._stopAutoPlay.bind(this); + this._currentIndex = props.initialPage; + this._childrenCount = React.Children.count(props.children); + } + + componentDidMount() { + if (this.props.autoPlayEnable) this._startAutoPlay(); + else this._stopAutoPlay(); + } + + UNSAFE_componentWillUpdate(nextProps, nextState) { + this._childrenCount = React.Children.count(nextProps.children); + if (this.props.autoPlayEnable !== nextProps.autoPlayEnable) { + nextProps.autoPlayEnable ? this._startAutoPlay() : this._stopAutoPlay(); } - - static defaultProps = { - indicator: null, - initialPage: 0, - autoPlayInterval: 3000, - autoPlayEnable: false, - horizontalScroll: true - } - - constructor (props) { - super(props) - this._onPageScroll = this._onPageScroll.bind(this) - this._onPageSelected = this._onPageSelected.bind(this) - this._goToNextPage = this._goToNextPage.bind(this) - this._renderIndicator = this._renderIndicator.bind(this) - this.setPage = this.setPage.bind(this) - this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this) - this._startAutoPlay = this._startAutoPlay.bind(this) - this._stopAutoPlay = this._stopAutoPlay.bind(this) - this._currentIndex = props.initialPage - this._childrenCount = React.Children.count(props.children) + } + + render() { + return ( + + + {this._renderIndicator()} + + ); + } + + componentWillUnmount() { + this._stopAutoPlay(); + } + + _onPageScroll(params) { + let indicator = this.refs[INDICATOR_REF]; + indicator && indicator.onPageScroll && indicator.onPageScroll(params); + this.props.onPageScroll && this.props.onPageScroll(params); + } + + _onPageSelected(params) { + let indicator = this.refs[INDICATOR_REF]; + indicator && indicator.onPageSelected && indicator.onPageSelected(params); + this.props.onPageSelected && this.props.onPageSelected(params); + this._currentIndex = params.position; + } + + _renderIndicator() { + let { indicator, initialPage } = this.props; + if (!indicator) return null; + return React.cloneElement(indicator, { + ref: INDICATOR_REF, + pager: this, + initialPage: initialPage + }); + } + + _goToNextPage() { + let nextIndex = (this._currentIndex + 1) % this._childrenCount; + this.setPage(nextIndex); + } + + _startAutoPlay() { + if (this._timerId) clearInterval(this._timerId); + this._timerId = setInterval( + this._goToNextPage, + this.props.autoPlayInterval + ); + } + + _stopAutoPlay() { + if (this._timerId) { + clearInterval(this._timerId); + this._timerId = null; } + } - componentDidMount () { - if (this.props.autoPlayEnable) this._startAutoPlay() - else this._stopAutoPlay() - } + setPage(selectedPage) { + this.refs[VIEWPAGER_REF].setPage(selectedPage); + } - componentWillUpdate (nextProps, nextState) { - this._childrenCount = React.Children.count(nextProps.children) - if (this.props.autoPlayEnable !== nextProps.autoPlayEnable) { - nextProps.autoPlayEnable ? this._startAutoPlay() : this._stopAutoPlay() - } - } - - render () { - return ( - - - {this._renderIndicator()} - - ) - } - - componentWillUnmount () { - this._stopAutoPlay() - } - - _onPageScroll (params) { - let indicator = this.refs[INDICATOR_REF] - indicator && indicator.onPageScroll && indicator.onPageScroll(params) - this.props.onPageScroll && this.props.onPageScroll(params) - } - - _onPageSelected (params) { - let indicator = this.refs[INDICATOR_REF] - indicator && indicator.onPageSelected && indicator.onPageSelected(params) - this.props.onPageSelected && this.props.onPageSelected(params) - this._currentIndex = params.position - } - - _renderIndicator () { - let {indicator, initialPage} = this.props - if (!indicator)return null - return React.cloneElement(indicator, { - ref: INDICATOR_REF, - pager: this, - initialPage: initialPage - }) - } - - _goToNextPage () { - let nextIndex = (this._currentIndex + 1) % this._childrenCount - this.setPage(nextIndex) - } - - _startAutoPlay () { - if (this._timerId) clearInterval(this._timerId) - this._timerId = setInterval(this._goToNextPage, this.props.autoPlayInterval) - } - - _stopAutoPlay () { - if (this._timerId) { - clearInterval(this._timerId) - this._timerId = null - } - } - - setPage (selectedPage) { - this.refs[VIEWPAGER_REF].setPage(selectedPage) - } - - setPageWithoutAnimation (selectedPage) { - this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage) - } + setPageWithoutAnimation(selectedPage) { + this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage); + } } const styles = StyleSheet.create({ - container: {}, - pager: {flex: 1} -}) + container: {}, + pager: { flex: 1 } +}); diff --git a/viewpager/ViewPager.js b/viewpager/ViewPager.js index 732db756..3930c987 100644 --- a/viewpager/ViewPager.js +++ b/viewpager/ViewPager.js @@ -2,183 +2,229 @@ * Created by tangzhibin on 16/2/28. */ -'use strict' +"use strict"; -import { PanResponder, Platform, ScrollView, StyleSheet, View, ViewPagerAndroid } from 'react-native' -import React, { Component } from 'react' +import { + PanResponder, + Platform, + ScrollView, + StyleSheet, + View +} from "react-native"; +import RNCViewPager from "@react-native-community/viewpager"; +import React, { Component } from "react"; -const SCROLLVIEW_REF = 'scrollView' -const VIEWPAGER_REF = 'viewPager' +const SCROLLVIEW_REF = "scrollView"; +const VIEWPAGER_REF = "viewPager"; const SCROLL_STATE = { - idle: 'idle', - settling: 'settling', - dragging: 'dragging' -} + idle: "idle", + settling: "settling", + dragging: "dragging" +}; export default class ViewPager extends Component { - static propTypes = {...ViewPagerAndroid.propTypes} - - static defaultProps = { - initialPage: 0, - keyboardDismissMode: 'on-drag', - onPageScroll: null, - onPageSelected: null, - onPageScrollStateChanged: null, - pageMargin: 0, - horizontalScroll: true + static propTypes = { ...RNCViewPager.propTypes }; + + static defaultProps = { + initialPage: 0, + keyboardDismissMode: "on-drag", + onPageScroll: null, + onPageSelected: null, + onPageScrollStateChanged: null, + pageMargin: 0, + horizontalScroll: true + }; + + _scrollState = SCROLL_STATE.idle; + + _preScrollX = null; + + _panResponder = PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onMoveShouldSetPanResponder: () => true, + onPanResponderGrant: () => this._setScrollState(SCROLL_STATE.dragging), + onPanResponderMove: () => null, + onPanResponderRelease: () => this._setScrollState(SCROLL_STATE.settling), + onPanResponderTerminate: () => null, + onPanResponderTerminationRequest: (evt, gestureState) => true + }); + + constructor(props) { + super(props); + this._onPageScrollOnAndroid = this._onPageScrollOnAndroid.bind(this); + this._onPageSelectedOnAndroid = this._onPageSelectedOnAndroid.bind(this); + this._renderOnIOS = this._renderOnIOS.bind(this); + this._onScrollOnIOS = this._onScrollOnIOS.bind(this); + this._onScrollViewLayout = this._onScrollViewLayout.bind(this); + this._childrenWithOverridenStyle = this._childrenWithOverridenStyle.bind( + this + ); + this._setScrollState = this._setScrollState.bind(this); + this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this); + this.setPage = this.setPage.bind(this); + this.state = { width: 0, height: 0, page: props.initialPage }; + } + + render() { + return this.props.forceScrollView || Platform.OS === "ios" ? ( + this._renderOnIOS() + ) : ( + + ); + } + + _onPageScrollOnAndroid(e) { + if (this.props.onPageScroll) this.props.onPageScroll(e.nativeEvent); + } + + _onPageSelectedOnAndroid(e) { + if (this.props.onPageSelected) this.props.onPageSelected(e.nativeEvent); + } + + _renderOnIOS() { + let childrenCount = this.props.children ? this.props.children.length : 0; + let initialPage = Math.min( + Math.max(0, this.props.initialPage), + childrenCount - 1 + ); + let needMonitorScroll = + !!this.props.onPageScroll || + !!this.props.onPageSelected || + !!this.props.onPageScrollStateChanged; + let needMonitorTouch = !!this.props.onPageScrollStateChanged; + let props = { + ...this.props, + ref: SCROLLVIEW_REF, + onLayout: this._onScrollViewLayout, + horizontal: true, + pagingEnabled: this.props.horizontalScroll ? true : false, + scrollEnabled: this.props.horizontalScroll ? true : false, + scrollsToTop: false, + showsHorizontalScrollIndicator: false, + showsVerticalScrollIndicator: false, + children: this._childrenWithOverridenStyle(), + contentOffset: { x: this.state.width * initialPage, y: 0 }, + decelerationRate: 0.9, + onScroll: needMonitorScroll ? this._onScrollOnIOS : null, + scrollEventThrottle: needMonitorScroll + ? this.props.onPageScroll + ? 8 + : 1 + : 0 + }; + if (needMonitorTouch) + props = Object.assign(props, this._panResponder.panHandlers); + const scrollViewStyle = { + overflow: "visible", + marginHorizontal: -this.props.pageMargin / 2 + }; + if (this.props.style && !this.props.style.height) + return ( + + ); + else + return ( + + + + ); + } + + _onScrollOnIOS(e) { + let { x } = e.nativeEvent.contentOffset, + offset, + position = Math.floor(x / this.state.width); + if (x === this._preScrollX) return; + this._preScrollX = x; + offset = x / this.state.width - position; + + if (this.props.onPageScroll) this.props.onPageScroll({ offset, position }); + + if (this.props.onPageSelected && offset === 0) { + this.props.onPageSelected({ position }); + this.props.onPageScrollStateChanged && + this._setScrollState(SCROLL_STATE.idle); + this.setState({ page: position }); } - - - _scrollState = SCROLL_STATE.idle - - _preScrollX = null - - _panResponder = PanResponder.create({ - onStartShouldSetPanResponder: () => true, - onMoveShouldSetPanResponder: () => true, - onPanResponderGrant: () => this._setScrollState(SCROLL_STATE.dragging), - onPanResponderMove: () => null, - onPanResponderRelease: () => this._setScrollState(SCROLL_STATE.settling), - onPanResponderTerminate: () => null, - onPanResponderTerminationRequest: (evt, gestureState) => true - }) - - constructor (props) { - super(props) - this._onPageScrollOnAndroid = this._onPageScrollOnAndroid.bind(this) - this._onPageSelectedOnAndroid = this._onPageSelectedOnAndroid.bind(this) - this._renderOnIOS = this._renderOnIOS.bind(this) - this._onScrollOnIOS = this._onScrollOnIOS.bind(this) - this._onScrollViewLayout = this._onScrollViewLayout.bind(this) - this._childrenWithOverridenStyle = this._childrenWithOverridenStyle.bind(this) - this._setScrollState = this._setScrollState.bind(this) - this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this) - this.setPage = this.setPage.bind(this) - this.state = {width: 0, height: 0, page: props.initialPage} + } + + _onScrollViewLayout(event) { + let { width, height } = event.nativeEvent.layout; + this.setState( + { width, height }, + () => + Platform.OS === "ios" && this.setPageWithoutAnimation(this.state.page) + ); + } + + _childrenWithOverridenStyle() { + if (this.state.width === 0 || this.state.height === 0) return null; + return React.Children.map(this.props.children, child => { + if (!child) return null; + let newProps = { + ...child.props, + style: [ + child.props.style, + { + width: this.state.width, + height: this.state.height, + position: null + } + ], + collapsable: false + }; + if ( + child.type && + child.type.displayName && + child.type.displayName !== "RCTView" && + child.type.displayName !== "View" + ) { + console.warn( + "Each ViewPager child must be a . Was " + child.type.displayName + ); + } + return React.createElement(child.type, newProps); + }); + } + + _setScrollState(scrollState) { + if (scrollState === this._scrollState) return; + this.props.onPageScrollStateChanged && + this.props.onPageScrollStateChanged(scrollState); + this._scrollState = scrollState; + } + + setPageWithoutAnimation(selectedPage) { + this.setState({ page: selectedPage }); + if (this.props.forceScrollView || Platform.OS === "ios") + this.refs[SCROLLVIEW_REF].scrollTo({ + x: this.state.width * selectedPage, + animated: false + }); + else { + this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage); + if (this.props.onPageSelected) + this.props.onPageSelected({ position: selectedPage }); } - - render () { - return (this.props.forceScrollView || Platform.OS === 'ios') ? this._renderOnIOS() : ( - - ) + } + + setPage(selectedPage) { + this.setState({ page: selectedPage }); + if (this.props.forceScrollView || Platform.OS === "ios") + this.refs[SCROLLVIEW_REF].scrollTo({ + x: this.state.width * selectedPage + }); + else { + this.refs[VIEWPAGER_REF].setPage(selectedPage); + if (this.props.onPageSelected) + this.props.onPageSelected({ position: selectedPage }); } - - _onPageScrollOnAndroid (e) { - if (this.props.onPageScroll) this.props.onPageScroll(e.nativeEvent) - } - - _onPageSelectedOnAndroid (e) { - if (this.props.onPageSelected) this.props.onPageSelected(e.nativeEvent) - } - - _renderOnIOS () { - let childrenCount = this.props.children ? this.props.children.length : 0 - let initialPage = Math.min(Math.max(0, this.props.initialPage), childrenCount - 1) - let needMonitorScroll = !!this.props.onPageScroll || !!this.props.onPageSelected || !!this.props.onPageScrollStateChanged - let needMonitorTouch = !!this.props.onPageScrollStateChanged - let props = { - ...this.props, - ref: SCROLLVIEW_REF, - onLayout: this._onScrollViewLayout, - horizontal: true, - pagingEnabled: this.props.horizontalScroll ? true : false, - scrollEnabled: this.props.horizontalScroll ? true : false, - scrollsToTop: false, - showsHorizontalScrollIndicator: false, - showsVerticalScrollIndicator: false, - children: this._childrenWithOverridenStyle(), - contentOffset: {x: this.state.width * initialPage, y: 0}, - decelerationRate: 0.9, - onScroll: needMonitorScroll ? this._onScrollOnIOS : null, - scrollEventThrottle: needMonitorScroll ? ( this.props.onPageScroll ? 8 : 1) : 0 - } - if (needMonitorTouch) props = Object.assign(props, this._panResponder.panHandlers) - const scrollViewStyle = { - overflow: 'visible', - marginHorizontal: -this.props.pageMargin / 2 - } - if (this.props.style && !this.props.style.height) - return - else return ( - - - - ) - } - - _onScrollOnIOS (e) { - let {x} = e.nativeEvent.contentOffset, offset, position = Math.floor(x / this.state.width) - if (x === this._preScrollX) return - this._preScrollX = x - offset = x / this.state.width - position - - if (this.props.onPageScroll) this.props.onPageScroll({offset, position}) - - if (this.props.onPageSelected && offset === 0) { - this.props.onPageSelected({position}) - this.props.onPageScrollStateChanged && this._setScrollState(SCROLL_STATE.idle) - this.setState({page: position}) - } - } - - _onScrollViewLayout (event) { - let {width, height} = event.nativeEvent.layout - this.setState({width, height}, () => Platform.OS === 'ios' && this.setPageWithoutAnimation(this.state.page)) - } - - _childrenWithOverridenStyle () { - if (this.state.width === 0 || this.state.height === 0) return null - return React.Children.map(this.props.children, (child) => { - if (!child)return null - let newProps = { - ...child.props, - style: [child.props.style, { - width: this.state.width, - height: this.state.height, - position: null - }], - collapsable: false - } - if (child.type && - child.type.displayName && - (child.type.displayName !== 'RCTView') && - (child.type.displayName !== 'View')) { - console.warn('Each ViewPager child must be a . Was ' + child.type.displayName) - } - return React.createElement(child.type, newProps) - }) - } - - _setScrollState (scrollState) { - if (scrollState === this._scrollState) return - this.props.onPageScrollStateChanged && this.props.onPageScrollStateChanged(scrollState) - this._scrollState = scrollState - } - - setPageWithoutAnimation (selectedPage) { - this.setState({page: selectedPage}) - if (this.props.forceScrollView || Platform.OS === 'ios') - this.refs[SCROLLVIEW_REF].scrollTo({x: this.state.width * selectedPage, animated: false}) - else { - this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage) - if (this.props.onPageSelected) this.props.onPageSelected({position: selectedPage}) - } - } - - setPage (selectedPage) { - this.setState({page: selectedPage}) - if (this.props.forceScrollView || Platform.OS === 'ios') this.refs[SCROLLVIEW_REF].scrollTo({x: this.state.width * selectedPage}) - else { - this.refs[VIEWPAGER_REF].setPage(selectedPage) - if (this.props.onPageSelected) this.props.onPageSelected({position: selectedPage}) - } - } - + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..a3066296 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@react-native-community/viewpager@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@react-native-community/viewpager/-/viewpager-1.1.7.tgz#7d3b1631f1ec91145db92a8e25c80d53027e96ba" + integrity sha512-k9v2KJtAprNPq7IZmedD2VLMePvPW+ohX3uDnkpoKritBji+/RtRmTKrdtPi3Uvp0toq/KtPttAds1dr7AZNpw==