diff --git a/packages/taro-components-rn/jest.config.js b/packages/taro-components-rn/jest.config.js index 4afdc4b45124..38e7beacce42 100644 --- a/packages/taro-components-rn/jest.config.js +++ b/packages/taro-components-rn/jest.config.js @@ -6,7 +6,8 @@ module.exports = { '^.+\\.tsx?$': 'ts-jest' }, transformIgnorePatterns: [ - 'node_modules/(?!(react-native|react-native-swiper|react-native-vertical-view-pager|react-native-animatable|react-native-collapsible|@bang88/react-native-ultimate-listview|react-native-modal-popover|react-native-modal-popover|react-native-safe-area-view)/)' + 'node_modules/(?!(react-native|react-native-swiper|react-native-vertical-view-pager|react-native-animatable|react-native-collapsible|@bang88/react-native-ultimate-listview|react-native-modal-popover|react-native-modal-popover|react-native-safe-area-view)/)', + 'node_modules/(?!(@manjiz/react-native-swiper)/)' ], testMatch: [ '**/__tests__/**/*.ts?(x)', diff --git a/packages/taro-components-rn/package.json b/packages/taro-components-rn/package.json index ccbabe47d1cb..97fb941cc27e 100644 --- a/packages/taro-components-rn/package.json +++ b/packages/taro-components-rn/package.json @@ -29,6 +29,7 @@ "license": "MIT", "dependencies": { "@ant-design/react-native": "^3.1.9", + "@manjiz/react-native-swiper": "^0.0.3", "prop-types": "^15.6.2", "react-dom": "^16.4.0", "react-mixin": "^5.0.0", diff --git a/packages/taro-components-rn/src/components/Swiper/index.tsx b/packages/taro-components-rn/src/components/Swiper/index.tsx index 6733ff29c441..951acec06ae3 100644 --- a/packages/taro-components-rn/src/components/Swiper/index.tsx +++ b/packages/taro-components-rn/src/components/Swiper/index.tsx @@ -53,7 +53,7 @@ import { ViewStyle } from 'react-native' // import Swiper from 'react-native-swiper' -import Swiper from '../../lib/react-native-swiper' +import Swiper from '@manjiz/react-native-swiper' import { noop } from '../../utils' import { SwiperProps } from './PropsType' diff --git a/packages/taro-components-rn/src/lib/react-native-swiper/PropsType.tsx b/packages/taro-components-rn/src/lib/react-native-swiper/PropsType.tsx deleted file mode 100644 index 7a2c370a87df..000000000000 --- a/packages/taro-components-rn/src/lib/react-native-swiper/PropsType.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { - StyleProp, - ViewStyle, - ScrollViewProps, -} from 'react-native'; - -export interface ReactNativeSwiperProps extends ScrollViewProps { - children: any; - - containerStyle?: StyleProp; - scrollViewStyle?: StyleProp; - // -> ScrollView.contentContainerStyle - style?: StyleProp; - // Custom styles will merge with the default styles. - paginationStyle?: ViewStyle; - // Allow custom the active-dot element. - dotStyle?: StyleProp; - // Allow custom the active-dot element. - activeDotStyle?: StyleProp; - - // Basic - // If true, the scroll view's children are arranged horizontally in a row instead of vertically in a column. - horizontal?: boolean; - // If no specify default enable fullscreen mode by flex: 1. - loop?: boolean; - // Set to false to disable continuous loop mode. - autoplay?: boolean; - // Index number of initial slide. - index?: number; - // Called with the new index when the user swiped - onIndexChanged?: (index: number) => void; - - // Custom basic style & content - width?: number; - // If no specify default fullscreen mode by flex: 1. - height?: number; - // Only load current index slide , loadMinimalSize slides before and after. - loadMinimal?: boolean; - // see loadMinimal - loadMinimalSize?: number; - // Custom loader to display when slides aren't loaded - loadMinimalLoader?: React.ReactNode; - - // Pagination - // Set to true make pagination visible. - showsPagination?: boolean; - // Complete control how to render pagination with three params (index, total, context) ref to this.state.index / this.state.total / this, For example: show numbers instead of dots. - renderPagination?: (index: number, total: number, swiper: any) => JSX.Element; - // Allow custom the dot element. - dot?: any; - // Allow custom the active-dot element. - activeDot?: any; - // Allow custom the active-dot element. - dotColor?: string; - // Allow custom the active-dot element. - activeDotColor?: string; - - // Autoplay - // Delay between auto play transitions (in second). - autoplayTimeout?: number; - // Cycle direction control. - autoplayDirection?: boolean; - - // Supported ScrollResponder - // When animation begins after letting up - onScrollBeginDrag?: any; - // Makes no sense why this occurs first during bounce - onMomentumScrollEnd?: any; - // Immediately after onMomentumScrollEnd - onTouchStartCapture?: any; - // Same, but bubble phase - onTouchStart?: any; - // You could hold the touch start for a long time - onTouchEnd?: any; - // When lifting up - you could pause forever before * lifting - onResponderRelease?: any; - - // ...ScrollViewProps - // pagingEnabled?: boolean; - // showsHorizontalScrollIndicator?: boolean; - // showsVerticalScrollIndicator?: boolean; - // bounces?: boolean; - // scrollsToTop?: boolean; - // removeClippedSubviews?: boolean; - // automaticallyAdjustContentInsets?: boolean; - // scrollEnabled?: boolean; -} - -export interface ReactNativeSwiperState { - width: number; - height: number; - offset: Offset, - autoplayEnd?: boolean; - loopJump?: boolean; - total: number; - index: number; - dir: 'x' | 'y'; - pIndex: number; -} - -export type Offset = { - x: number; - y: number; -} - -export type ScrollEventSim = { - nativeEvent: { - contentOffset: { - x: number; - y: number; - } - } -} diff --git a/packages/taro-components-rn/src/lib/react-native-swiper/index.bak20190619tsx b/packages/taro-components-rn/src/lib/react-native-swiper/index.bak20190619tsx deleted file mode 100644 index e31e9b24b3c5..000000000000 --- a/packages/taro-components-rn/src/lib/react-native-swiper/index.bak20190619tsx +++ /dev/null @@ -1,529 +0,0 @@ -/** - * react-native-swiper - * - * react-native >= 0.55.4 - * react >= 16.3.1 - * - * 组件由 `react-native-swiper` 修改而来 - * - * @author leecade - * @author Manjiz - */ - -import React, { Component } from 'react' -import { - View, - ScrollView, - Dimensions, - ActivityIndicator, - LayoutChangeEvent, - NativeSyntheticEvent, - NativeScrollEvent, - Platform, - InteractionManager, -} from 'react-native' -import { ReactNativeSwiperProps, ReactNativeSwiperState, Offset } from './PropsType' -import { noop } from '../../utils' -import styles from './styles' - -const isAndroid = Platform.OS === 'android' - -const getOffset = (dir: 'x'|'y', index: number, width: number, height: number): Offset => { - const tmp: Offset = { x: 0, y: 0 } - tmp[dir] = (dir === 'x' ? width : height) * index - return tmp -} - -export default class extends Component { - /** - * @see http://facebook.github.io/react-native/docs/scrollview.html - */ - static defaultProps = { - horizontal: true, - pagingEnabled: true, - removeClippedSubviews: true, - showsPagination: true, - loop: true, - loadMinimalSize: 1, - autoplayTimeout: 2.5, - autoplayDirection: true, - index: 0, - - showsHorizontalScrollIndicator: true, - showsVerticalScrollIndicator: false, - bounces: false, - scrollsToTop: false, - automaticallyAdjustContentInsets: false, - } - - static getDerivedStateFromProps (props: ReactNativeSwiperProps, state: ReactNativeSwiperState) { - const initState: any = { - autoplayEnd: false, - loopJump: false, - offset: {} - } - - initState.total = props.children ? props.children.length || 1 : 0 - - const updateIndex = state.pIndex !== props.index - - if (state.total === initState.total && !updateIndex) { - // retain the index - initState.index = state.index - } else { - initState.index = initState.total > 1 ? Math.min(props.index as number, initState.total - 1) : 0 - initState.pIndex = props.index as number - } - - const { width, height } = Dimensions.get('window') - - initState.dir = props.horizontal ? 'x' : 'y' - initState.width = props.width || state.width || width - initState.height = props.height || state.height || height - initState.offset[initState.dir] = (initState.dir === 'x' ? initState.width : initState.height) * (props.index as number + (props.loop ? 1 : 0)) - - return initState - } - - state: ReactNativeSwiperState = { - width: 0, - height: 0, - offset: { x: 0, y: 0 }, - total: 0, - pIndex: 0, - index: 0, - dir: 'x' - } - initialRender: boolean = true - autoplayTimer: any - loopJumpTimer: any - scrolling: boolean = false - realtimeOffset: Partial = { x: 0, y: 0 } - $scrollView: any = React.createRef() - - onContentSizeChange = (contentWidth: number, contentHeight: number): void => { - console.log('1123 onContentSizeChange=----') - const { loop } = this.props - const { index, width, height, dir } = this.state - const offset: Offset = getOffset(dir, index + (loop ? 1 : 0), width, height) - InteractionManager.runAfterInteractions(() => { - const node = this.$scrollView.current - node && node.scrollTo({ ...offset, animated: isAndroid }, false) - }) - } - - getSnapshotBeforeUpdate (prevProps: ReactNativeSwiperProps, prevState: ReactNativeSwiperState) { - this.scrolling = false - return { - propsChanged: prevProps !== this.props, - shouldClearAutoPlay: !this.props.autoplay && this.autoplayTimer, - ifIndexChange: prevState.index !== this.state.index, - } - } - - componentDidUpdate (prevProps: ReactNativeSwiperProps, prevState: ReactNativeSwiperState, snapshot: any) { - if (snapshot.shouldClearAutoPlay) { - clearTimeout(this.autoplayTimer as number) - } - // If the index has changed, we notify the parent via the onIndexChanged callback - if (snapshot.ifIndexChange) { - const { onIndexChanged = noop } = this.props - onIndexChanged(this.state.index) - } - if (!snapshot.propsChanged && prevState.offset !== this.state.offset) { - const node = this.$scrollView.current - // workaround-1: android scrollTo not work after offset changed. (In real device) - node && node.scrollTo({ - ...this.state.offset, - animated: isAndroid || this.isAutoScroll, - duration: this.isAutoScroll ? 500 : 1 - }) - this.isAutoScroll = false - } - } - - componentDidMount () { - this.autoplay() - } - - componentWillUnmount () { - this.autoplayTimer && clearTimeout(this.autoplayTimer) - this.loopJumpTimer && clearTimeout(this.loopJumpTimer) - } - - fullState () { - return Object.assign({}, this.state, { offset: this.realtimeOffset }) - } - - onLayout = (event: LayoutChangeEvent) => { - const { loop } = this.props - const { - index, - total, - dir, - width, - height, - } = this.state - // const { width: layoutWidth, height: layoutHeight } = event.nativeEvent.layout - const layoutWidth = Math.round(event.nativeEvent.layout.width) - const layoutHeight = Math.round(event.nativeEvent.layout.height) - let offsetWouldBeSet: Partial = this.realtimeOffset = {} - const stateWouldBeSet: any = { width: layoutWidth, height: layoutHeight } - - if (total > 1) { - let actualIndex = index - loop && actualIndex++ - offsetWouldBeSet = getOffset(dir, actualIndex, layoutWidth, layoutHeight) - } - - // only update the offset in state if needed, updating offset while swiping - // causes some bad jumping / stuttering - if (layoutWidth !== width || layoutHeight !== height) { - stateWouldBeSet.offset = offsetWouldBeSet - } - - this.setState(stateWouldBeSet) - } - - autoScrollOffset?: Offset - - /** - * Scroll by index - */ - isAutoScroll: boolean = false - scrollBy = (step: number, animated: boolean = true) => { - const { loop } = this.props - const { dir, width, height, index, total } = this.state - console.log('scrollBy', this.scrolling, total) - if (this.scrolling || total < 2) return - const actualIndex = (loop ? 1 : 0) + index - const newActualIndex = actualIndex + step - let x = 0 - let y = 0 - dir === 'x' ? (x = newActualIndex * width) : (y = newActualIndex * height) - - // const node = this.$scrollView.current - // node && node.scrollTo({ x, y, animated, duration: 500 }) - this.isAutoScroll = true - this.setState({ offset: { x, y } }) - this.autoScrollOffset = { x, y } - - // update scroll state - this.scrolling = true - - this.setState({ autoplayEnd: false }) - } - - /** - * Automatic rolling - */ - autoplay = () => { - const { - children, - autoplay, - autoplayTimeout, - loop, - autoplayDirection - } = this.props - const { index, total, autoplayEnd } = this.state - if (!Array.isArray(children) || !autoplay || this.scrolling || autoplayEnd) return - - this.autoplayTimer && clearTimeout(this.autoplayTimer) - this.autoplayTimer = setTimeout(() => { - if (!loop && (autoplayDirection ? index === total - 1 : index === 0)) { - return this.setState({ autoplayEnd: true }) - } else { - this.scrollBy(autoplayDirection ? 1 : -1) - } - }, (autoplayTimeout as number) * 1000) - } - - onScroll = (e: NativeSyntheticEvent) => { - if (isAndroid && this.autoScrollOffset) { - const contentOffset = e.nativeEvent.contentOffset - const { dir, width, height } = this.state - if (Math.abs(contentOffset[dir] - this.autoScrollOffset[dir]) <= 1) { - this.onScrollEnd(e) - } - } - } - - - /** - * Scroll begin handle - */ - onScrollBegin = (e?: NativeSyntheticEvent) => { - const { onScrollBeginDrag = noop } = this.props - // update scroll state - this.scrolling = true - onScrollBeginDrag(e, this.fullState(), this) - } - - /** - * Scroll end handle - */ - onScrollEnd = (e: NativeSyntheticEvent) => { - console.log('onScrollEnd---') - const { onMomentumScrollEnd = noop } = this.props - - // update scroll state - this.scrolling = false - - // making our events coming from android compatible to updateIndex logic - const contentOffset = e.nativeEvent.contentOffset - - this.updateIndexByOffset(contentOffset, () => { - this.autoplay() - - // if `onMomentumScrollEnd` registered will be called here - onMomentumScrollEnd(e, this.fullState(), this) - }) - } - - /* - * Drag end handle - */ - onScrollEndDrag = (e: NativeSyntheticEvent) => { - const { horizontal, children } = this.props - const { index } = this.state - const { contentOffset } = e.nativeEvent - const previousOffset = horizontal ? this.realtimeOffset.x : this.realtimeOffset.y - const newOffset = horizontal ? contentOffset.x : contentOffset.y - - if (previousOffset === newOffset && (index === 0 || index === children.length - 1)) { - this.scrolling = false - } - } - - /** - * Update index after scroll - */ - updateIndexByOffset = (contentOffset: Offset, cb: any) => { - const { loop } = this.props - let { index, dir, width, height, total } = this.state - - // Android not setting this onLayout first? https://github.com/leecade/react-native-swiper/issues/582 - !this.realtimeOffset && (this.realtimeOffset = {}) - - const diff = (contentOffset[dir] || 0) - (this.realtimeOffset[dir] || 0) - const step = dir === 'x' ? width : height - let loopJump = false - - // Do nothing if offset no change. - if (!this.initialRender && !diff) return - - // Note: if touch very very quickly and continuous, - // the variation of `index` more than 1. - // parseInt() ensures it's always an integer - // index = parseInt(index + Math.round(diff / step) + '') - let actualIndex = parseInt(Math.round((contentOffset[dir] || 0) / step) + '') - - if (loop) { - if (actualIndex === 0) { - index = total - 1 - contentOffset[dir] = step * total - loopJump = true - } else if (actualIndex === total + 1) { - index = 0 - contentOffset[dir] = step - loopJump = true - } else { - index = actualIndex - 1 - } - } - - const stateWouldBeSet: any = { index, loopJump } - - this.realtimeOffset = contentOffset - - // only update offset in state if loopJump is true - if (loopJump) { - // when swiping to the beginning of a looping set for the third time, - // the new offset will be the same as the last one set in state. - // Setting the offset to the same thing will not do anything, - // so we increment it by 1 then immediately set it to what it should be, - // after render. - if (contentOffset[dir] === this.realtimeOffset[dir]) { - stateWouldBeSet.offset = { x: 0, y: 0 } - stateWouldBeSet.offset[dir] = (contentOffset[dir] || 0) + 1 - this.setState(stateWouldBeSet, () => { - this.setState({ offset: contentOffset }, cb) - }) - } else { - stateWouldBeSet.offset = contentOffset - this.setState(stateWouldBeSet, cb) - } - } else { - this.setState(stateWouldBeSet, cb) - } - } - - scrollViewPropOverrides = () => { - const overrides: any = {} - - for (let prop in this.props) { - // if(~scrollResponders.indexOf(prop) - if ( - typeof this.props[prop as keyof ReactNativeSwiperProps] === 'function' - && prop !== 'onMomentumScrollEnd' - && prop !== 'renderPagination' - && prop !== 'onScrollBeginDrag' - ) { - const originResponder = this.props[prop as keyof ReactNativeSwiperProps] || noop - overrides[prop] = (e: any) => originResponder(e, this.fullState(), this) - } - } - - return overrides - } - - /** - * Render pagination - * By default, dots only show when `total` >= 2 - */ - renderPagination = (): React.ReactNode => { - if (this.state.total <= 1) return null - - const dots = [] - const ActiveDot = this.props.activeDot || ( - - ) - - const Dot = this.props.dot || ( - - ) - - for (let i = 0; i < this.state.total; i++) { - dots.push(i === this.state.index - ? React.cloneElement(ActiveDot, { key: i }) - : React.cloneElement(Dot, { key: i }) - ) - } - - return ( - - {dots} - - ) - } - - renderScrollView = (pages: any) => { - return ( - - {pages} - - ) - } - - /** - * Default render - */ - render () { - const { - index, - total, - width, - height - } = this.state - const { - children, - containerStyle, - loop, - loadMinimal, - loadMinimalSize, - loadMinimalLoader, - renderPagination, - showsPagination, - } = this.props - // let dir = state.dir - // let key = 0 - const loopVal = loop ? 1 : 0 - let pages: Element[] | Element = [] - - const pageStyle = [{ width, height }, styles.slide] - const pageStyleLoading: any = { - width, - height, - flex: 1, - justifyContent: 'center', - alignItems: 'center' - } - - // For make infinite at least total > 1 - if (total > 1) { - // Re-design a loop model for avoid img flickering - const pagesKeys = Object.keys(children) - if (loop) { - pagesKeys.unshift(total - 1 + '') - pagesKeys.push('0') - } - - pages = pagesKeys.map((pageIdx: string, i: number) => { - if (loadMinimal) { - if (i >= (index + loopVal - (loadMinimalSize as number)) && - i <= (index + loopVal + (loadMinimalSize as number))) { - return {children[parseInt(pageIdx)]} - } else { - return ( - - {loadMinimalLoader ? loadMinimalLoader : } - - ) - } - } else { - return {children[parseInt(pageIdx)]} - } - }) - } else { - pages = {children} - } - - return ( - - {this.renderScrollView(pages)} - {showsPagination && (renderPagination ? renderPagination(index, total, this) : this.renderPagination())} - - ) - } -} diff --git a/packages/taro-components-rn/src/lib/react-native-swiper/index.tsx b/packages/taro-components-rn/src/lib/react-native-swiper/index.tsx deleted file mode 100644 index f9ddb1326a37..000000000000 --- a/packages/taro-components-rn/src/lib/react-native-swiper/index.tsx +++ /dev/null @@ -1,534 +0,0 @@ -/** - * react-native-swiper - * - * react-native >= 0.55.4 - * react >= 16.3.1 - * - * 组件由 `react-native-swiper` 修改而来 - * - * @author leecade - * @author Manjiz - */ - -import React, { Component } from 'react' -import { - View, - ScrollView, - Dimensions, - ActivityIndicator, - LayoutChangeEvent, - NativeSyntheticEvent, - NativeScrollEvent, - Platform, - InteractionManager, -} from 'react-native' -import { ReactNativeSwiperProps, ReactNativeSwiperState, Offset, ScrollEventSim } from './PropsType' -import { noop } from '../../utils' -import styles from './styles' - -const isAndroid = Platform.OS === 'android' - -const getOffset = (dir: 'x'|'y', index: number, width: number, height: number): Offset => { - const tmp: Offset = { x: 0, y: 0 } - tmp[dir] = (dir === 'x' ? width : height) * index - return tmp -} - -export default class extends Component { - /** - * @see http://facebook.github.io/react-native/docs/scrollview.html - */ - static defaultProps = { - horizontal: true, - pagingEnabled: true, - removeClippedSubviews: true, - showsPagination: true, - loop: true, - loadMinimalSize: 1, - autoplayTimeout: 2.5, - autoplayDirection: true, - index: 0, - - showsHorizontalScrollIndicator: false, - showsVerticalScrollIndicator: false, - bounces: false, - scrollsToTop: false, - automaticallyAdjustContentInsets: false, - } - - static getDerivedStateFromProps (props: ReactNativeSwiperProps, state: ReactNativeSwiperState) { - const initState: any = { - autoplayEnd: false, - loopJump: false, - offset: {} - } - - initState.total = props.children ? props.children.length || 1 : 0 - - const updateIndex = state.pIndex !== props.index - - if (state.total === initState.total && !updateIndex) { - // retain the index - initState.index = state.index - } else { - initState.index = initState.total > 1 ? Math.min(props.index as number, initState.total - 1) : 0 - initState.pIndex = props.index as number - } - - const { width, height } = Dimensions.get('window') - - initState.dir = props.horizontal ? 'x' : 'y' - initState.width = props.width || state.width || width - initState.height = props.height || state.height || height - initState.offset[initState.dir] = (initState.dir === 'x' ? initState.width : initState.height) * (props.index as number + (props.loop ? 1 : 0)) - - return initState - } - - state: ReactNativeSwiperState = { - width: 0, - height: 0, - offset: { x: 0, y: 0 }, - total: 0, - pIndex: 0, - index: 0, - dir: 'x', - } - - autoplayTimer: any - loopJumpTimer: any - autoScrolling: boolean = false - scrolling: boolean = false - scrollAnimated: boolean = false - realtimeOffset: Partial = { x: 0, y: 0 } - onAndroidScrollEndTimer: any - - $scrollView: any = React.createRef() - - componentDidMount () { - this.autoplay() - } - - componentDidUpdate (prevProps: ReactNativeSwiperProps, prevState: ReactNativeSwiperState) { - const shouldClearAutoPlay = !this.props.autoplay && this.autoplayTimer - const indexChanged = prevState.index !== this.state.index - this.scrolling = false - shouldClearAutoPlay && clearTimeout(this.autoplayTimer as number) - // If the index has changed, we notify the parent via the onIndexChanged callback - if (indexChanged) { - const { onIndexChanged = noop } = this.props - onIndexChanged(this.state.index) - } - const { dir } = this.state - if (prevState.offset !== this.state.offset && prevState.offset[dir] !== this.state.offset[dir]) { - const node = this.$scrollView.current - // workaround-1: android scrollTo not work after offset changed. (In real device) - // Android scrollTo didn't trigger onMomentumScrollEnd. - node && node.scrollTo({ - ...this.state.offset, - animated: isAndroid || this.scrollAnimated, - duration: this.scrollAnimated ? 500 : 1 - }) - } - } - - componentWillUnmount () { - this.autoplayTimer && clearTimeout(this.autoplayTimer) - this.loopJumpTimer && clearTimeout(this.loopJumpTimer) - this.onAndroidScrollEndTimer && clearTimeout(this.onAndroidScrollEndTimer) - } - - /** - * Reset index and autoplay if contentSizeChange. - */ - onContentSizeChange = (contentWidth: number, contentHeight: number): void => { - const { loop } = this.props - const { width, total, height, dir } = this.state - const offset: Offset = getOffset(dir, loop && total > 1 ? 1 : 0, width, height) - this.realtimeOffset = offset - this.setState({ index: 0, offset }, () => { - this.autoplay() - // workaround-2: In android, - if (isAndroid) { - const node = this.$scrollView.current - node && node.scrollTo(offset) - } - }) - } - - fullState () { - return Object.assign({}, this.state, { offset: this.realtimeOffset }) - } - - onLayout = (event: LayoutChangeEvent) => { - const { loop } = this.props - const { - index, - total, - dir, - width, - height, - } = this.state - const { width: layoutWidth, height: layoutHeight } = event.nativeEvent.layout - // const layoutWidth = Math.round(event.nativeEvent.layout.width) - // const layoutHeight = Math.round(event.nativeEvent.layout.height) - let offsetWouldBeSet: Partial = this.realtimeOffset = {} - const stateWouldBeSet: any = { width: layoutWidth, height: layoutHeight } - - if (total > 1) { - let actualIndex = index - loop && actualIndex++ - offsetWouldBeSet = getOffset(dir, actualIndex, layoutWidth, layoutHeight) - } - - // only update the offset in state if needed, updating offset while swiping - // causes some bad jumping / stuttering - if (layoutWidth !== width || layoutHeight !== height) { - stateWouldBeSet.offset = offsetWouldBeSet - } - - this.setState(stateWouldBeSet) - } - - /** - * Scroll by index - */ - scrollBy = (step: number, animated: boolean = true) => { - const { loop } = this.props - const { dir, width, height, index, total } = this.state - if (this.scrolling || total < 2) return - const actualIndex = (loop ? 1 : 0) + index - const newActualIndex = actualIndex + step - let x = 0 - let y = 0 - dir === 'x' ? (x = newActualIndex * width) : (y = newActualIndex * height) - - this.scrolling = true - this.autoScrolling = true - this.scrollAnimated = animated - this.setState({ - offset: { x, y }, - autoplayEnd: false - }, () => { - this.scrollAnimated = false - }) - } - - /** - * Automatic rolling - */ - autoplay = () => { - const { - children, - autoplay, - autoplayTimeout, - loop, - autoplayDirection - } = this.props - const { index, total, autoplayEnd } = this.state - if (!Array.isArray(children) || !autoplay || this.scrolling || autoplayEnd) return - - this.autoplayTimer && clearTimeout(this.autoplayTimer) - this.autoplayTimer = setTimeout(() => { - if (!loop && (autoplayDirection ? index === total - 1 : index === 0)) { - return this.setState({ autoplayEnd: true }) - } else { - this.scrollBy(autoplayDirection ? 1 : -1) - } - }, (autoplayTimeout as number) * 1000) - } - - /** - * Scroll begin handle - */ - onScrollBegin = (e?: ScrollEventSim) => { - const { onScrollBeginDrag = noop } = this.props - this.scrolling = true - onScrollBeginDrag(e, this.fullState(), this) - } - - /** - * workaround-3: Android didn't trigger onMomentumScrollEnd after scrollTo(). - */ - onScroll = (e: ScrollEventSim) => { - if (isAndroid && this.autoScrolling) { - const contentOffset = e.nativeEvent.contentOffset - const { dir, offset } = this.state - if (Math.abs(Math.round(contentOffset[dir]) - Math.round(offset[dir])) < 2) { - this.onAndroidScrollEndTimer && clearTimeout(this.onAndroidScrollEndTimer) - // Protect nativeEvent object. - const tmpEvent: ScrollEventSim = { nativeEvent: { contentOffset: { x: contentOffset.x, y: contentOffset.y } } } - this.onAndroidScrollEndTimer = setTimeout(() => { - this.onScrollEnd(tmpEvent) - }, 50) - } - } - } - - /** - * Scroll end handle - */ - onScrollEnd = (e: ScrollEventSim) => { - const { onMomentumScrollEnd = noop } = this.props - - this.scrolling = false - this.autoScrolling = false - - // making our events coming from android compatible to updateIndex logic - const contentOffset = e.nativeEvent.contentOffset - - this.updateIndexByOffset(contentOffset, () => { - this.autoplay() - - // if `onMomentumScrollEnd` registered will be called here - onMomentumScrollEnd(e, this.fullState(), this) - }) - } - - /* - * Drag end handle - */ - onScrollEndDrag = (e: ScrollEventSim) => { - const { horizontal, children } = this.props - const { index } = this.state - const { contentOffset } = e.nativeEvent - const previousOffset = horizontal ? this.realtimeOffset.x : this.realtimeOffset.y - const newOffset = horizontal ? contentOffset.x : contentOffset.y - - if (previousOffset === newOffset && (index === 0 || index === children.length - 1)) { - this.scrolling = false - } - } - - /** - * Update index after scroll - */ - updateIndexByOffset = (contentOffset: Offset, cb: any) => { - const { loop } = this.props - let { index, dir, width, height, total } = this.state - - // Android not setting this onLayout first? https://github.com/leecade/react-native-swiper/issues/582 - !this.realtimeOffset && (this.realtimeOffset = {}) - - const diff = (contentOffset[dir] || 0) - (this.realtimeOffset[dir] || 0) - const step = dir === 'x' ? width : height - let loopJump = false - - // Do nothing if offset no change. - if (!diff) return - - // Note: if touch very very quickly and continuous, - // the variation of `index` more than 1. - // parseInt() ensures it's always an integer - // index = parseInt(index + Math.round(diff / step) + '') - let actualIndex = parseInt(Math.round((contentOffset[dir] || 0) / step) + '') - - if (loop) { - if (actualIndex === 0) { - index = total - 1 - contentOffset[dir] = step * total - loopJump = true - } else if (actualIndex === total + 1) { - index = 0 - contentOffset[dir] = step - loopJump = true - } else { - index = actualIndex - 1 - } - } - - const stateWouldBeSet: any = { index, loopJump } - - this.realtimeOffset = contentOffset - - // only update offset in state if loopJump is true - if (loopJump) { - // when swiping to the beginning of a looping set for the third time, - // the new offset will be the same as the last one set in state. - // Setting the offset to the same thing will not do anything, - // so we increment it by 1 then immediately set it to what it should be, - // after render. - if (contentOffset[dir] === this.realtimeOffset[dir]) { - stateWouldBeSet.offset = { x: 0, y: 0 } - stateWouldBeSet.offset[dir] = (contentOffset[dir] || 0) + 1 - this.setState(stateWouldBeSet, () => { - this.setState({ offset: contentOffset }, cb) - }) - } else { - stateWouldBeSet.offset = contentOffset - this.setState(stateWouldBeSet, cb) - } - } else { - this.setState(stateWouldBeSet, cb) - } - } - - scrollViewPropOverrides = () => { - const overrides: any = {} - - for (let prop in this.props) { - // if(~scrollResponders.indexOf(prop) - if ( - typeof this.props[prop as keyof ReactNativeSwiperProps] === 'function' - && prop !== 'onMomentumScrollEnd' - && prop !== 'renderPagination' - && prop !== 'onScrollBeginDrag' - ) { - const originResponder = this.props[prop as keyof ReactNativeSwiperProps] || noop - overrides[prop] = (e: any) => originResponder(e, this.fullState(), this) - } - } - - return overrides - } - - /** - * Render pagination - * By default, dots only show when `total` >= 2 - */ - renderPagination = (): React.ReactNode => { - if (this.state.total <= 1) return null - - const dots = [] - const ActiveDot = this.props.activeDot || ( - - ) - - const Dot = this.props.dot || ( - - ) - - for (let i = 0; i < this.state.total; i++) { - dots.push(i === this.state.index - ? React.cloneElement(ActiveDot, { key: i }) - : React.cloneElement(Dot, { key: i }) - ) - } - - return ( - - {dots} - - ) - } - - renderScrollView = (pages: any) => { - return ( - - {pages} - - ) - } - - /** - * Default render - */ - render () { - const { - index, - total, - width, - height - } = this.state - const { - children, - containerStyle, - loop, - loadMinimal, - loadMinimalSize, - loadMinimalLoader, - renderPagination, - showsPagination, - } = this.props - // let dir = state.dir - // let key = 0 - const loopVal = loop ? 1 : 0 - let pages: Element[] | Element = [] - - const pageStyle = [{ width, height }, styles.slide] - const pageStyleLoading: any = { - width, - height, - flex: 1, - justifyContent: 'center', - alignItems: 'center' - } - - // For make infinite at least total > 1 - if (total > 1) { - // Re-design a loop model for avoid img flickering - const pagesKeys = Object.keys(children) - if (loop) { - pagesKeys.unshift(total - 1 + '') - pagesKeys.push('0') - } - - pages = pagesKeys.map((pageIdx: string, i: number) => { - if (loadMinimal) { - if (i >= (index + loopVal - (loadMinimalSize as number)) && - i <= (index + loopVal + (loadMinimalSize as number))) { - return {children[parseInt(pageIdx)]} - } else { - return ( - - {loadMinimalLoader ? loadMinimalLoader : } - - ) - } - } else { - return {children[parseInt(pageIdx)]} - } - }) - } else { - pages = {children} - } - - return ( - - {this.renderScrollView(pages)} - {showsPagination && (renderPagination ? renderPagination(index, total, this) : this.renderPagination())} - - ) - } -} diff --git a/packages/taro-components-rn/src/lib/react-native-swiper/styles.tsx b/packages/taro-components-rn/src/lib/react-native-swiper/styles.tsx deleted file mode 100644 index 98f064f1a769..000000000000 --- a/packages/taro-components-rn/src/lib/react-native-swiper/styles.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Default styles - * @type {StyleSheetPropType} - */ - -import { - StyleSheet -} from 'react-native' - -export default StyleSheet.create({ - container: { - backgroundColor: 'transparent', - position: 'relative', - flex: 1 - }, - - wrapper: { - backgroundColor: 'transparent', - }, - - slide: { - backgroundColor: 'transparent', - overflow: 'hidden' - }, - - pagination_x: { - position: 'absolute', - bottom: 25, - left: 0, - right: 0, - flexDirection: 'row', - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'transparent' - }, - - pagination_y: { - position: 'absolute', - right: 15, - top: 0, - bottom: 0, - flexDirection: 'column', - flex: 1, - justifyContent: 'center', - alignItems: 'center', - backgroundColor: 'transparent' - } -}) diff --git a/packages/taro-components-rn/yarn.lock b/packages/taro-components-rn/yarn.lock index a6e16ffd3a36..11b7da85f2dd 100644 --- a/packages/taro-components-rn/yarn.lock +++ b/packages/taro-components-rn/yarn.lock @@ -519,6 +519,11 @@ resolved "https://registry.npm.taobao.org/@bang88/react-native-ultimate-listview/download/@bang88/react-native-ultimate-listview-3.3.0.tgz#b09199105b942aee4ae05729e4bd229353217ab6" integrity sha1-sJGZEFuUKu5K4Fcp5L0ik1MherY= +"@manjiz/react-native-swiper@^0.0.3": + version "0.0.3" + resolved "https://registry.npm.taobao.org/@manjiz/react-native-swiper/download/@manjiz/react-native-swiper-0.0.3.tgz#bcc03e573121751d50579050ef38461e21f33233" + integrity sha1-vMA+VzEhdR1QV5BQ7zhGHiHzMjM= + "@sinonjs/commons@^1", "@sinonjs/commons@^1.0.2": version "1.4.0" resolved "http://registry.npm.taobao.org/@sinonjs/commons/download/@sinonjs/commons-1.4.0.tgz#7b3ec2d96af481d7a0321252e7b1c94724ec5a78"