diff --git a/package.json b/package.json index 01b232b..b6c13f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-image-gallery", - "version": "2.1.5", + "version": "2.1.8", "description": "Pure JavaScript image gallery component for iOS and Android", "main": "src/Gallery.js", "scripts": { diff --git a/src/Gallery.js b/src/Gallery.js index 5cc4835..6313840 100644 --- a/src/Gallery.js +++ b/src/Gallery.js @@ -1,291 +1,307 @@ -import React, { PureComponent } from 'react'; -import { View, ViewPropTypes } from 'react-native'; -import PropTypes from 'prop-types'; -import { createResponder } from './libraries/GestureResponder'; -import TransformableImage from './libraries/TransformableImage'; -import ViewPager from './libraries/ViewPager'; +import React, { PureComponent } from "react" +import { View, ViewPropTypes } from "react-native" +import PropTypes from "prop-types" + +import { createResponder } from "./libraries/GestureResponder" +import TransformableImage from "./libraries/TransformableImage" +import ViewPager from "./libraries/ViewPager" const DEFAULT_FLAT_LIST_PROPS = { - windowSize: 3 -}; + windowSize: 3, +} export default class Gallery extends PureComponent { - static propTypes = { - ...View.propTypes, - images: PropTypes.arrayOf(PropTypes.object), - initialPage: PropTypes.number, - scrollViewStyle: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style, - pageMargin: PropTypes.number, - onPageSelected: PropTypes.func, - onPageScrollStateChanged: PropTypes.func, - onPageScroll: PropTypes.func, - onSingleTapConfirmed: PropTypes.func, - onGalleryStateChanged: PropTypes.func, - onLongPress: PropTypes.func, - removeClippedSubviews: PropTypes.bool, - imageComponent: PropTypes.func, - errorComponent: PropTypes.func, - flatListProps: PropTypes.object - }; + static propTypes = { + ...View.propTypes, + images: PropTypes.arrayOf(PropTypes.object), + initialPage: PropTypes.number, + scrollViewStyle: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style, + pageMargin: PropTypes.number, + onPageSelected: PropTypes.func, + onPageScrollStateChanged: PropTypes.func, + onPageScroll: PropTypes.func, + onSingleTapConfirmed: PropTypes.func, + onGalleryStateChanged: PropTypes.func, + onLongPress: PropTypes.func, + removeClippedSubviews: PropTypes.bool, + imageComponent: PropTypes.func, + errorComponent: PropTypes.func, + flatListProps: PropTypes.object, + } - static defaultProps = { - removeClippedSubviews: true, - imageComponent: undefined, - scrollViewStyle: {}, - flatListProps: DEFAULT_FLAT_LIST_PROPS - }; + static defaultProps = { + removeClippedSubviews: true, + imageComponent: undefined, + scrollViewStyle: {}, + flatListProps: DEFAULT_FLAT_LIST_PROPS, + } - imageRefs = new Map(); - activeResponder = undefined; - firstMove = true; - currentPage = 0; - pageCount = 0; - gestureResponder = undefined; + imageRefs = new Map() + activeResponder = undefined + firstMove = true + currentPage = 0 + pageCount = 0 + gestureResponder = undefined - constructor (props) { - super(props); + constructor(props) { + super(props) - this.renderPage = this.renderPage.bind(this); - this.onPageSelected = this.onPageSelected.bind(this); - this.onPageScrollStateChanged = this.onPageScrollStateChanged.bind(this); - this.getViewPagerInstance = this.getViewPagerInstance.bind(this); - this.getCurrentImageTransformer = this.getCurrentImageTransformer.bind(this); - this.getImageTransformer = this.getImageTransformer.bind(this); - this.getViewPagerInstance = this.getViewPagerInstance.bind(this); - this.activeImageResponder = this.activeImageResponder.bind(this); - } - - componentWillMount () { - let onResponderReleaseOrTerminate = (evt, gestureState) => { - if (this.activeResponder) { - if (this.activeResponder === this.viewPagerResponder && - !this.shouldScrollViewPager(evt, gestureState) && - Math.abs(gestureState.vx) > 0.5) { - this.activeResponder.onEnd(evt, gestureState, true); - this.getViewPagerInstance().flingToPage(this.currentPage, gestureState.vx); - } else { - this.activeResponder.onEnd(evt, gestureState); - } - this.activeResponder = null; - } - this.firstMove = true; - this.props.onGalleryStateChanged && this.props.onGalleryStateChanged(true); - }; + this.renderPage = this.renderPage.bind(this) + this.onPageSelected = this.onPageSelected.bind(this) + this.onPageScrollStateChanged = this.onPageScrollStateChanged.bind(this) + this.getViewPagerInstance = this.getViewPagerInstance.bind(this) + this.getCurrentImageTransformer = this.getCurrentImageTransformer.bind(this) + this.getImageTransformer = this.getImageTransformer.bind(this) + this.getViewPagerInstance = this.getViewPagerInstance.bind(this) + this.activeImageResponder = this.activeImageResponder.bind(this) + } - this.gestureResponder = createResponder({ - onStartShouldSetResponderCapture: (evt, gestureState) => true, - onStartShouldSetResponder: (evt, gestureState) => true, - onResponderGrant: this.activeImageResponder, - onResponderMove: (evt, gestureState) => { - if (this.firstMove) { - this.firstMove = false; - if (this.shouldScrollViewPager(evt, gestureState)) { - this.activeViewPagerResponder(evt, gestureState); - } - this.props.onGalleryStateChanged && this.props.onGalleryStateChanged(false); - } - if (this.activeResponder === this.viewPagerResponder) { - const dx = gestureState.moveX - gestureState.previousMoveX; - const offset = this.getViewPagerInstance().getScrollOffsetFromCurrentPage(); - if (dx > 0 && offset > 0 && !this.shouldScrollViewPager(evt, gestureState)) { - if (dx > offset) { // active image responder - this.getViewPagerInstance().scrollByOffset(offset); - gestureState.moveX -= offset; - this.activeImageResponder(evt, gestureState); - } - } else if (dx < 0 && offset < 0 && !this.shouldScrollViewPager(evt, gestureState)) { - if (dx < offset) { // active image responder - this.getViewPagerInstance().scrollByOffset(offset); - gestureState.moveX -= offset; - this.activeImageResponder(evt, gestureState); - } - } - } - this.activeResponder.onMove(evt, gestureState); - }, - onResponderRelease: onResponderReleaseOrTerminate, - onResponderTerminate: onResponderReleaseOrTerminate, - onResponderTerminationRequest: (evt, gestureState) => false, // Do not allow parent view to intercept gesture - onResponderSingleTapConfirmed: (evt, gestureState) => { - this.props.onSingleTapConfirmed && this.props.onSingleTapConfirmed(this.currentPage); - } - }); + componentWillMount() { + let onResponderReleaseOrTerminate = (evt, gestureState) => { + if (this.activeResponder) { + if ( + this.activeResponder === this.viewPagerResponder && + !this.shouldScrollViewPager(evt, gestureState) && + Math.abs(gestureState.vx) > 0.5 + ) { + this.activeResponder.onEnd(evt, gestureState, true) + this.getViewPagerInstance().flingToPage(this.currentPage, gestureState.vx) + } else { + this.activeResponder.onEnd(evt, gestureState) + } + this.activeResponder = null + } + this.firstMove = true + this.props.onGalleryStateChanged && this.props.onGalleryStateChanged(true) + } - this.viewPagerResponder = { - onStart: (evt, gestureState) => { - this.getViewPagerInstance().onResponderGrant(evt, gestureState); - }, - onMove: (evt, gestureState) => { - this.getViewPagerInstance().onResponderMove(evt, gestureState); - }, - onEnd: (evt, gestureState, disableSettle) => { - this.getViewPagerInstance().onResponderRelease(evt, gestureState, disableSettle); + this.gestureResponder = createResponder({ + onStartShouldSetResponderCapture: (evt, gestureState) => true, + onStartShouldSetResponder: (evt, gestureState) => true, + onResponderGrant: this.activeImageResponder, + onResponderMove: (evt, gestureState) => { + if (this.firstMove) { + this.firstMove = false + if (this.shouldScrollViewPager(evt, gestureState)) { + this.activeViewPagerResponder(evt, gestureState) + } + this.props.onGalleryStateChanged && this.props.onGalleryStateChanged(false) + } + if (this.activeResponder === this.viewPagerResponder) { + const dx = gestureState.moveX - gestureState.previousMoveX + const offset = this.getViewPagerInstance().getScrollOffsetFromCurrentPage() + if (dx > 0 && offset > 0 && !this.shouldScrollViewPager(evt, gestureState)) { + if (dx > offset) { + // active image responder + this.getViewPagerInstance().scrollByOffset(offset) + gestureState.moveX -= offset + this.activeImageResponder(evt, gestureState) } - }; - - this.imageResponder = { - onStart: (evt, gestureState) => { - const currentImageTransformer = this.getCurrentImageTransformer(); - currentImageTransformer && currentImageTransformer.onResponderGrant(evt, gestureState); - if (this.props.onLongPress) { - this._longPressTimeout = setTimeout(() => { - this.props.onLongPress(gestureState); - }, 600); - } - }, - onMove: (evt, gestureState) => { - const currentImageTransformer = this.getCurrentImageTransformer(); - currentImageTransformer && currentImageTransformer.onResponderMove(evt, gestureState); - clearTimeout(this._longPressTimeout); - }, - onEnd: (evt, gestureState) => { - const currentImageTransformer = this.getCurrentImageTransformer(); - currentImageTransformer && currentImageTransformer.onResponderRelease(evt, gestureState); - clearTimeout(this._longPressTimeout); + } else if (dx < 0 && offset < 0 && !this.shouldScrollViewPager(evt, gestureState)) { + if (dx < offset) { + // active image responder + this.getViewPagerInstance().scrollByOffset(offset) + gestureState.moveX -= offset + this.activeImageResponder(evt, gestureState) } - }; - } + } + } + this.activeResponder.onMove(evt, gestureState) + }, + onResponderRelease: onResponderReleaseOrTerminate, + onResponderTerminate: onResponderReleaseOrTerminate, + onResponderTerminationRequest: (evt, gestureState) => false, // Do not allow parent view to intercept gesture + onResponderSingleTapConfirmed: (evt, gestureState) => { + this.props.onSingleTapConfirmed && this.props.onSingleTapConfirmed(this.currentPage) + }, + }) - componentDidMount () { - this._isMounted = true; + this.viewPagerResponder = { + onStart: (evt, gestureState) => { + this.getViewPagerInstance().onResponderGrant(evt, gestureState) + }, + onMove: (evt, gestureState) => { + this.getViewPagerInstance().onResponderMove(evt, gestureState) + }, + onEnd: (evt, gestureState, disableSettle) => { + this.getViewPagerInstance().onResponderRelease(evt, gestureState, disableSettle) + }, } - componentWillUnmount () { - this._isMounted = false; + this.imageResponder = { + onStart: (evt, gestureState) => { + const currentImageTransformer = this.getCurrentImageTransformer() + currentImageTransformer && currentImageTransformer.onResponderGrant(evt, gestureState) + if (this.props.onLongPress) { + this._longPressTimeout = setTimeout(() => { + this.props.onLongPress(gestureState) + }, 600) + } + }, + onMove: (evt, gestureState) => { + const currentImageTransformer = this.getCurrentImageTransformer() + currentImageTransformer && currentImageTransformer.onResponderMove(evt, gestureState) + clearTimeout(this._longPressTimeout) + }, + onEnd: (evt, gestureState) => { + const currentImageTransformer = this.getCurrentImageTransformer() + currentImageTransformer && currentImageTransformer.onResponderRelease(evt, gestureState) + clearTimeout(this._longPressTimeout) + }, } + } - shouldScrollViewPager (evt, gestureState) { - if (gestureState.numberActiveTouches > 1) { - return false; - } - const viewTransformer = this.getCurrentImageTransformer(); - if (!viewTransformer) { - return false; - } + componentDidMount() { + this._isMounted = true + } - const space = viewTransformer.getAvailableTranslateSpace(); - const dx = gestureState.moveX - gestureState.previousMoveX; + componentWillUnmount() { + this._isMounted = false + } - if (dx > 0 && space.left <= 0 && this.currentPage > 0) { - return true; - } - if (dx < 0 && space.right <= 0 && this.currentPage < this.pageCount - 1) { - return true; - } - return false; + shouldScrollViewPager(evt, gestureState) { + if (gestureState.numberActiveTouches > 1) { + return false } - - activeImageResponder (evt, gestureState) { - if (this.activeResponder !== this.imageResponder) { - if (this.activeResponder === this.viewPagerResponder) { - this.viewPagerResponder.onEnd(evt, gestureState, true); // pass true to disable ViewPager settle - } - this.activeResponder = this.imageResponder; - this.imageResponder.onStart(evt, gestureState); - } + const viewTransformer = this.getCurrentImageTransformer() + if (!viewTransformer) { + return false } - activeViewPagerResponder (evt, gestureState) { - if (this.activeResponder !== this.viewPagerResponder) { - if (this.activeResponder === this.imageResponder) { - this.imageResponder.onEnd(evt, gestureState); - } - this.activeResponder = this.viewPagerResponder; - this.viewPagerResponder.onStart(evt, gestureState); - } - } + const space = viewTransformer.getAvailableTranslateSpace() + const dx = gestureState.moveX - gestureState.previousMoveX - getImageTransformer (page) { - if (page >= 0 && page < this.pageCount) { - let ref = this.imageRefs.get(page); - if (ref) { - return ref.getViewTransformerInstance(); - } - } + if (dx > 0 && space.left <= 0 && this.currentPage > 0) { + return true } - - getCurrentImageTransformer () { - return this.getImageTransformer(this.currentPage); + if (dx < 0 && space.right <= 0 && this.currentPage < this.pageCount - 1) { + return true } + return false + } - getViewPagerInstance () { - return this.refs['galleryViewPager']; + activeImageResponder(evt, gestureState) { + if (this.activeResponder !== this.imageResponder) { + if (this.activeResponder === this.viewPagerResponder) { + this.viewPagerResponder.onEnd(evt, gestureState, true) // pass true to disable ViewPager settle + } + this.activeResponder = this.imageResponder + this.imageResponder.onStart(evt, gestureState) } + } - onPageSelected (page) { - this.currentPage = page; - this.props.onPageSelected && this.props.onPageSelected(page); + activeViewPagerResponder(evt, gestureState) { + if (this.activeResponder !== this.viewPagerResponder) { + if (this.activeResponder === this.imageResponder) { + this.imageResponder.onEnd(evt, gestureState) + } + this.activeResponder = this.viewPagerResponder + this.viewPagerResponder.onStart(evt, gestureState) } + } - onPageScrollStateChanged (state) { - if (state === 'idle') { - this.resetHistoryImageTransform(); - } - this.props.onPageScrollStateChanged && this.props.onPageScrollStateChanged(state); + getImageTransformer(page) { + if (page >= 0 && page < this.pageCount) { + let ref = this.imageRefs.get(page) + if (ref) { + return ref.getViewTransformerInstance() + } } + } - renderPage (pageData, pageId) { - const { onViewTransformed, onTransformGestureReleased, errorComponent, imageComponent } = this.props; - return ( - { - onViewTransformed && onViewTransformed(transform, pageId); - })} - onTransformGestureReleased={((transform) => { - // need the 'return' here because the return value is checked in ViewTransformer - return onTransformGestureReleased && onTransformGestureReleased(transform, pageId); - })} - ref={((ref) => { this.imageRefs.set(pageId, ref); })} - key={'innerImage#' + pageId} - errorComponent={errorComponent} - imageComponent={imageComponent} - image={pageData} - /> - ); - } + getCurrentImageTransformer() { + return this.getImageTransformer(this.currentPage) + } - resetHistoryImageTransform () { - let transformer = this.getImageTransformer(this.currentPage + 1); - if (transformer) { - transformer.forceUpdateTransform({scale: 1, translateX: 0, translateY: 0}); - } + getViewPagerInstance() { + return this.refs["galleryViewPager"] + } - transformer = this.getImageTransformer(this.currentPage - 1); - if (transformer) { - transformer.forceUpdateTransform({scale: 1, translateX: 0, translateY: 0}); - } + onPageSelected(page) { + this.currentPage = page + this.props.onPageSelected && this.props.onPageSelected(page) + } + + onPageScrollStateChanged(state) { + if (state === "idle") { + this.resetHistoryImageTransform() } + this.props.onPageScrollStateChanged && this.props.onPageScrollStateChanged(state) + } - render () { - let gestureResponder = this.gestureResponder; + renderPage(pageData, pageId) { + const { + onViewTransformed, + onTransformGestureReleased, + errorComponent, + imageComponent, + minScale, + maxScale, + } = this.props + return ( + { + onViewTransformed && onViewTransformed(transform, pageId) + }} + onTransformGestureReleased={(transform) => { + // need the 'return' here because the return value is checked in ViewTransformer + return onTransformGestureReleased && onTransformGestureReleased(transform, pageId) + }} + ref={(ref) => { + this.imageRefs.set(pageId, ref) + }} + key={"innerImage#" + pageId} + errorComponent={errorComponent} + imageComponent={imageComponent} + image={pageData} + minScale={minScale} + maxScale={maxScale} + /> + ) + } - let images = this.props.images; - if (!images) { - images = []; - } - this.pageCount = images.length; + resetHistoryImageTransform() { + let transformer = this.getImageTransformer(this.currentPage + 1) + if (transformer) { + transformer.forceUpdateTransform({ scale: 1, translateX: 0, translateY: 0 }) + } - if (this.pageCount <= 0) { - gestureResponder = {}; - } + transformer = this.getImageTransformer(this.currentPage - 1) + if (transformer) { + transformer.forceUpdateTransform({ scale: 1, translateX: 0, translateY: 0 }) + } + } - const flatListProps = {...DEFAULT_FLAT_LIST_PROPS, ...this.props.flatListProps}; + render() { + let gestureResponder = this.gestureResponder - return ( - - ); + let images = this.props.images + if (!images) { + images = [] } + this.pageCount = images.length + + if (this.pageCount <= 0) { + gestureResponder = {} + } + + const flatListProps = { ...DEFAULT_FLAT_LIST_PROPS, ...this.props.flatListProps } + + return ( + + ) + } } diff --git a/src/libraries/GestureResponder/createResponder.js b/src/libraries/GestureResponder/createResponder.js index 9096690..67443a3 100644 --- a/src/libraries/GestureResponder/createResponder.js +++ b/src/libraries/GestureResponder/createResponder.js @@ -16,7 +16,7 @@ const previousCentroidYOfTouchesChangedAfter = TouchHistoryMath.previousCentroid const currentCentroidX = TouchHistoryMath.currentCentroidX; const currentCentroidY = TouchHistoryMath.currentCentroidY; -const TAP_UP_TIME_THRESHOLD = 400; +const TAP_UP_TIME_THRESHOLD = 200; const TAP_MOVE_THRESHOLD = 10; const MOVE_THRESHOLD = 2; diff --git a/src/libraries/TransformableImage/index.js b/src/libraries/TransformableImage/index.js index 8ff8469..1053966 100644 --- a/src/libraries/TransformableImage/index.js +++ b/src/libraries/TransformableImage/index.js @@ -1,207 +1,249 @@ -import React, { PureComponent } from 'react'; -import { View, Text, Image, ViewPropTypes } from 'react-native'; -import PropTypes from 'prop-types'; -import ViewTransformer from '../ViewTransformer'; +import React, { PureComponent } from "react" +import { View, Text, Image, ViewPropTypes } from "react-native" +import PropTypes from "prop-types" +import ViewTransformer from "../ViewTransformer" export default class TransformableImage extends PureComponent { - static propTypes = { - image: PropTypes.shape({ - source: PropTypes.oneOfType([ - PropTypes.object, - PropTypes.number - ]).isRequired, - dimensions: PropTypes.shape({ width: PropTypes.number, height: PropTypes.number }) - }).isRequired, - style: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style, - onLoad: PropTypes.func, - onLoadStart: PropTypes.func, - enableTransform: PropTypes.bool, - enableScale: PropTypes.bool, - enableTranslate: PropTypes.bool, - onTransformGestureReleased: PropTypes.func, - onViewTransformed: PropTypes.func, - imageComponent: PropTypes.func, - resizeMode: PropTypes.string, - errorComponent: PropTypes.func - }; - - static defaultProps = { - enableTransform: true, - enableScale: true, - enableTranslate: true, - imageComponent: undefined, - resizeMode: 'contain' - }; - - constructor (props) { - super(props); - - this.onLayout = this.onLayout.bind(this); - this.onLoad = this.onLoad.bind(this); - this.onLoadStart = this.onLoadStart.bind(this); - this.getViewTransformerInstance = this.getViewTransformerInstance.bind(this); - this.renderError = this.renderError.bind(this); - - this.state = { - viewWidth: 0, - viewHeight: 0, - imageLoaded: false, - imageDimensions: props.image.dimensions, - keyAcumulator: 1 - }; + static propTypes = { + image: PropTypes.shape({ + source: PropTypes.oneOfType([PropTypes.object, PropTypes.number]).isRequired, + dimensions: PropTypes.shape({ width: PropTypes.number, height: PropTypes.number }), + }).isRequired, + style: ViewPropTypes ? ViewPropTypes.style : View.propTypes.style, + onLoad: PropTypes.func, + onLoadStart: PropTypes.func, + enableTransform: PropTypes.bool, + enableScale: PropTypes.bool, + enableTranslate: PropTypes.bool, + onTransformGestureReleased: PropTypes.func, + onViewTransformed: PropTypes.func, + imageComponent: PropTypes.func, + resizeMode: PropTypes.string, + errorComponent: PropTypes.func, + } + + static defaultProps = { + enableTransform: true, + enableScale: true, + enableTranslate: true, + imageComponent: undefined, + resizeMode: "contain", + } + + constructor(props) { + super(props) + + this.onLayout = this.onLayout.bind(this) + this.onLoad = this.onLoad.bind(this) + this.onLoadStart = this.onLoadStart.bind(this) + this.getViewTransformerInstance = this.getViewTransformerInstance.bind(this) + this.renderError = this.renderError.bind(this) + + this.state = { + viewWidth: 0, + viewHeight: 0, + imageLoaded: false, + imageDimensions: props.image.dimensions, + keyAcumulator: 1, } + } - componentWillMount () { - if (!this.state.imageDimensions) { - this.getImageSize(this.props.image); - } + componentWillMount() { + if (!this.state.imageDimensions) { + this.getImageSize(this.props.image) } - - componentDidMount () { - this._mounted = true; + } + + componentDidMount() { + this._mounted = true + } + + componentWillReceiveProps(nextProps) { + if (!sameImage(this.props.image, nextProps.image)) { + // image source changed, clear last image's imageDimensions info if any + this.setState({ + imageDimensions: nextProps.image.dimensions, + keyAcumulator: this.state.keyAcumulator + 1, + }) + if (!nextProps.image.dimensions) { + // if we don't have image dimensions provided in source + this.getImageSize(nextProps.image) + } } + } - componentWillReceiveProps (nextProps) { - if (!sameImage(this.props.image, nextProps.image)) { - // image source changed, clear last image's imageDimensions info if any - this.setState({ imageDimensions: nextProps.image.dimensions, keyAcumulator: this.state.keyAcumulator + 1 }); - if (!nextProps.image.dimensions) { // if we don't have image dimensions provided in source - this.getImageSize(nextProps.image); - } - } - } + componentWillUnmount() { + this._mounted = false + } - componentWillUnmount () { - this._mounted = false; + onLoadStart(e) { + this.props.onLoadStart && this.props.onLoadStart(e) + if (this.state.imageLoaded) { + this.setState({ imageLoaded: false }) } + } - onLoadStart (e) { - this.props.onLoadStart && this.props.onLoadStart(e); - if (this.state.imageLoaded) { - this.setState({ imageLoaded: false }); - } + onLoad(e) { + this.props.onLoad && this.props.onLoad(e) + if (!this.state.imageLoaded) { + this.setState({ imageLoaded: true }) } + } - onLoad (e) { - this.props.onLoad && this.props.onLoad(e); - if (!this.state.imageLoaded) { - this.setState({ imageLoaded: true }); - } + onLayout(e) { + let { width, height } = e.nativeEvent.layout + if (this.state.viewWidth !== width || this.state.viewHeight !== height) { + this.setState({ viewWidth: width, viewHeight: height }) } + } - onLayout (e) { - let {width, height} = e.nativeEvent.layout; - if (this.state.viewWidth !== width || this.state.viewHeight !== height) { - this.setState({ viewWidth: width, viewHeight: height }); - } + getImageSize(image) { + if (!image) { + return } + const { source, dimensions } = image - getImageSize (image) { - if (!image) { - return; - } - const { source, dimensions } = image; - - if (dimensions) { - this.setState({ imageDimensions: dimensions }); - return; - } - - if (source && source.uri) { - Image.getSize( - source.uri, - (width, height) => { - if (width && height) { - if (this.state.imageDimensions && this.state.imageDimensions.width === width && this.state.imageDimensions.height === height) { - // no need to update state - } else { - this._mounted && this.setState({ imageDimensions: { width, height } }); - } - } - }, - () => { - this._mounted && this.setState({ error: true }); - } - ); - } else { - console.warn('react-native-image-gallery', 'Please provide dimensions of your local images'); - } + if (dimensions) { + this.setState({ imageDimensions: dimensions }) + return } - getViewTransformerInstance () { - return this.refs['viewTransformer']; + if (source && source.uri) { + Image.getSize( + source.uri, + (width, height) => { + if (width && height) { + if ( + this.state.imageDimensions && + this.state.imageDimensions.width === width && + this.state.imageDimensions.height === height + ) { + // no need to update state + } else { + this._mounted && this.setState({ imageDimensions: { width, height } }) + } + } + }, + () => { + this._mounted && this.setState({ error: true }) + }, + ) + } else { + console.warn("react-native-image-gallery", "Please provide dimensions of your local images") } - - renderError () { - return (this.props.errorComponent && this.props.errorComponent()) || ( - - This image cannot be displayed... - - ); + } + + getViewTransformerInstance() { + return this.refs["viewTransformer"] + } + + renderError() { + return ( + (this.props.errorComponent && this.props.errorComponent()) || ( + + + This image cannot be displayed... + + + ) + ) + } + + render() { + const { + imageDimensions, + viewWidth, + viewHeight, + error, + keyAccumulator, + imageLoaded, + } = this.state + const { + style, + image, + imageComponent, + resizeMode, + enableTransform, + enableScale, + enableTranslate, + onTransformGestureReleased, + onViewTransformed, + minScale, + } = this.props + + let maxScale = 1 + let contentAspectRatio + let width, height // imageDimensions + + if (imageDimensions) { + width = imageDimensions.width + height = imageDimensions.height } - render () { - const { imageDimensions, viewWidth, viewHeight, error, keyAccumulator, imageLoaded } = this.state; - const { style, image, imageComponent, resizeMode, enableTransform, enableScale, enableTranslate, onTransformGestureReleased, onViewTransformed } = this.props; - - let maxScale = 1; - let contentAspectRatio; - let width, height; // imageDimensions - - if (imageDimensions) { - width = imageDimensions.width; - height = imageDimensions.height; - } + if (this.props.maxScale) { + maxScale = this.props.maxScale + } else if (width && height) { + contentAspectRatio = width / height + if (viewWidth && viewHeight) { + maxScale = Math.max(width / viewWidth, height / viewHeight) + maxScale = Math.max(1, maxScale) + } + } - if (width && height) { - contentAspectRatio = width / height; - if (viewWidth && viewHeight) { - maxScale = Math.max(width / viewWidth, height / viewHeight); - maxScale = Math.max(1, maxScale); - } - } - - const imageProps = { - ...this.props, - imageLoaded, - source: image.source, - style: [style, { backgroundColor: 'transparent' }], - resizeMode: resizeMode, - onLoadStart: this.onLoadStart, - onLoad: this.onLoad, - capInsets: { left: 0.1, top: 0.1, right: 0.1, bottom: 0.1 } - }; - - const content = imageComponent ? imageComponent(imageProps, imageDimensions) : ; - - return ( - - { error ? this.renderError() : content } - - ); + const imageProps = { + ...this.props, + imageLoaded, + source: image.source, + style: [style, { backgroundColor: "transparent" }], + resizeMode: resizeMode, + onLoadStart: this.onLoadStart, + onLoad: this.onLoad, + capInsets: { left: 0.1, top: 0.1, right: 0.1, bottom: 0.1 }, } + + const content = imageComponent ? ( + imageComponent(imageProps, imageDimensions) + ) : ( + + ) + + return ( + + {error ? this.renderError() : content} + + ) + } } -function sameImage (source, nextSource) { - if (source === nextSource) { - return true; - } - if (source && nextSource) { - if (source.uri && nextSource.uri) { - return source.uri === nextSource.uri; - } +function sameImage(source, nextSource) { + if (source === nextSource) { + return true + } + if (source && nextSource) { + if (source.uri && nextSource.uri) { + return source.uri === nextSource.uri } - return false; + } + return false } diff --git a/src/libraries/ViewPager/index.js b/src/libraries/ViewPager/index.js index b786ad1..81f27f9 100644 --- a/src/libraries/ViewPager/index.js +++ b/src/libraries/ViewPager/index.js @@ -296,7 +296,7 @@ export default class ViewPager extends PureComponent { render () { const { width, height } = this.state; - const { pageDataArray, scrollEnabled, style, scrollViewStyle } = this.props; + const { pageDataArray, scrollEnabled, style, scrollViewStyle,initialListSize,initialPage } = this.props; if (width && height) { let list = pageDataArray; @@ -310,6 +310,11 @@ export default class ViewPager extends PureComponent { gestureResponder = {}; } + let initialNumToRender = initialListSize; + if(initialPage > initialNumToRender - 1){ + initialNumToRender = initialPage+1 + } + return ( { - if (dx === 0 && dy === 0 && scroller.isFinished()) { - this.animateBounce(); - return; - } - - this.updateTransform({ - translateX: this.state.translateX + dx / this.state.scale, - translateY: this.state.translateY + dy / this.state.scale - }); - }); + static Rect = Rect + static getTransform = getTransform + + static propTypes = { + enableTransform: PropTypes.bool, + enableScale: PropTypes.bool, + enableTranslate: PropTypes.bool, + maxOverScrollDistance: PropTypes.number, + maxScale: PropTypes.number, + contentAspectRatio: PropTypes.number, + enableResistance: PropTypes.bool, + onViewTransformed: PropTypes.func, + onTransformGestureReleased: PropTypes.func, + onSingleTapConfirmed: PropTypes.func, + onLayout: PropTypes.func, + onTransformStart: PropTypes.func, + children: PropTypes.node, + } + + static defaultProps = { + maxOverScrollDistance: 20, + enableScale: true, + enableTranslate: true, + enableTransform: true, + maxScale: 1, + enableResistance: false, + } + + constructor(props) { + super(props) + this.state = { + // transform state + scale: 1, + translateX: 0, + translateY: 0, + // animation state + animator: new Animated.Value(0), + // layout + width: 0, + height: 0, + pageX: 0, + pageY: 0, } - - viewPortRect () { - this._viewPortRect.set(0, 0, this.state.width, this.state.height); - return this._viewPortRect; - } - - contentRect () { - let rect = this.viewPortRect().copy(); - if (this.props.contentAspectRatio && this.props.contentAspectRatio > 0) { - rect = fitCenterRect(this.props.contentAspectRatio, rect); - } - return rect; + this._viewPortRect = new Rect() // A holder to avoid new too much + + this.onLayout = this.onLayout.bind(this) + this.cancelAnimation = this.cancelAnimation.bind(this) + this.contentRect = this.contentRect.bind(this) + this.transformedContentRect = this.transformedContentRect.bind(this) + this.animate = this.animate.bind(this) + + this.scroller = new Scroller(true, (dx, dy, scroller) => { + if (dx === 0 && dy === 0 && scroller.isFinished()) { + this.animateBounce() + return + } + + this.updateTransform({ + translateX: this.state.translateX + dx / this.state.scale, + translateY: this.state.translateY + dy / this.state.scale, + }) + }) + } + + viewPortRect() { + this._viewPortRect.set(0, 0, this.state.width, this.state.height) + return this._viewPortRect + } + + contentRect() { + let rect = this.viewPortRect().copy() + if (this.props.contentAspectRatio && this.props.contentAspectRatio > 0) { + rect = fitCenterRect(this.props.contentAspectRatio, rect) } + return rect + } - transformedContentRect () { - let rect = transformedRect(this.viewPortRect(), this.currentTransform()); - if (this.props.contentAspectRatio && this.props.contentAspectRatio > 0) { - rect = fitCenterRect(this.props.contentAspectRatio, rect); - } - return rect; + transformedContentRect() { + let rect = transformedRect(this.viewPortRect(), this.currentTransform()) + if (this.props.contentAspectRatio && this.props.contentAspectRatio > 0) { + rect = fitCenterRect(this.props.contentAspectRatio, rect) } - - currentTransform () { - return new Transform(this.state.scale, this.state.translateX, this.state.translateY); + return rect + } + + currentTransform() { + return new Transform(this.state.scale, this.state.translateX, this.state.translateY) + } + + componentWillMount() { + this.gestureResponder = createResponder({ + onStartShouldSetResponder: (evt, gestureState) => true, + onMoveShouldSetResponderCapture: (evt, gestureState) => true, + // onMoveShouldSetResponder: this.handleMove, + onResponderMove: this.onResponderMove, + onResponderGrant: this.onResponderGrant, + onResponderRelease: this.onResponderRelease, + onResponderTerminate: this.onResponderRelease, + onResponderTerminationRequest: (evt, gestureState) => false, // Do not allow parent view to intercept gesture + onResponderSingleTapConfirmed: (evt, gestureState) => { + this.props.onSingleTapConfirmed && this.props.onSingleTapConfirmed() + }, + }) + } + + componentDidUpdate(prevProps, prevState) { + this.props.onViewTransformed && + this.props.onViewTransformed({ + scale: this.state.scale, + translateX: this.state.translateX, + translateY: this.state.translateY, + }) + } + + componentWillUnmount() { + this.cancelAnimation() + } + + render() { + let gestureResponder = this.gestureResponder + if (!this.props.enableTransform) { + gestureResponder = {} } - componentWillMount () { - this.gestureResponder = createResponder({ - onStartShouldSetResponder: (evt, gestureState) => true, - onMoveShouldSetResponderCapture: (evt, gestureState) => true, - // onMoveShouldSetResponder: this.handleMove, - onResponderMove: this.onResponderMove, - onResponderGrant: this.onResponderGrant, - onResponderRelease: this.onResponderRelease, - onResponderTerminate: this.onResponderRelease, - onResponderTerminationRequest: (evt, gestureState) => false, // Do not allow parent view to intercept gesture - onResponderSingleTapConfirmed: (evt, gestureState) => { - this.props.onSingleTapConfirmed && this.props.onSingleTapConfirmed(); - } - }); + return ( + + + {this.props.children} + + + ) + } + + onLayout(e) { + const { width, height } = e.nativeEvent.layout + if (width !== this.state.width || height !== this.state.height) { + this.setState({ width, height }) } - - componentDidUpdate (prevProps, prevState) { - this.props.onViewTransformed && this.props.onViewTransformed({ - scale: this.state.scale, - translateX: this.state.translateX, - translateY: this.state.translateY - }); - } - - componentWillUnmount () { - this.cancelAnimation(); - } - - render () { - let gestureResponder = this.gestureResponder; - if (!this.props.enableTransform) { - gestureResponder = {}; + this.measureLayout() + + this.props.onLayout && this.props.onLayout(e) + } + + measureLayout() { + let handle = ReactNative.findNodeHandle(this.refs["innerViewRef"]) + NativeModules.UIManager.measure(handle, (x, y, width, height, pageX, pageY) => { + if (typeof pageX === "number" && typeof pageY === "number") { + // avoid undefined values on Android devices + if (this.state.pageX !== pageX || this.state.pageY !== pageY) { + this.setState({ pageX: pageX, pageY: pageY }) } - - return ( - - - { this.props.children } - - - ); + } + }) + } + + onResponderGrant(evt, gestureState) { + this.props.onTransformStart && this.props.onTransformStart() + this.setState({ responderGranted: true }) + this.measureLayout() + } + + onResponderMove(evt, gestureState) { + const {height} = this.state; + const { minScale } = this.props + this.cancelAnimation() + + let dx = gestureState.moveX - gestureState.previousMoveX + let dy = gestureState.moveY - gestureState.previousMoveY + if (this.props.enableResistance) { + let d = this.applyResistance(dx, dy) + dx = d.dx + dy = d.dy } - onLayout (e) { - const {width, height} = e.nativeEvent.layout; - if (width !== this.state.width || height !== this.state.height) { - this.setState({width, height}); - } - this.measureLayout(); - - this.props.onLayout && this.props.onLayout(e); + if (!this.props.enableTranslate) { + dx = dy = 0 } - measureLayout () { - let handle = ReactNative.findNodeHandle(this.refs['innerViewRef']); - NativeModules.UIManager.measure(handle, (x, y, width, height, pageX, pageY) => { - if (typeof pageX === 'number' && typeof pageY === 'number') { // avoid undefined values on Android devices - if (this.state.pageX !== pageX || this.state.pageY !== pageY) { - this.setState({ pageX: pageX, pageY: pageY }); - } - } - }); + let transform = {} + if (gestureState.previousPinch && gestureState.pinch && this.props.enableScale) { + let scaleBy = gestureState.pinch / gestureState.previousPinch + + if (minScale > 0 && this.currentTransform().scale * scaleBy < minScale) { + scaleBy = minScale / this.currentTransform().scale + } + let pivotX = gestureState.moveX - this.state.pageX + let pivotY = gestureState.moveY - this.state.pageY + + let rect = transformedRect( + transformedRect(this.contentRect(), this.currentTransform()), + new Transform(scaleBy, dx, dy, { x: pivotX, y: pivotY }), + ) + transform = getTransform(this.contentRect(), rect) + } else { + if (Math.abs(dx) > 2 * Math.abs(dy)) { + dy = 0 + } else if (Math.abs(dy) > 2 * Math.abs(dx)) { + dx = 0 + } + transform.translateX = this.state.translateX + dx / this.state.scale + transform.translateY = this.state.translateY + dy / this.state.scale } - - onResponderGrant (evt, gestureState) { - this.props.onTransformStart && this.props.onTransformStart(); - this.setState({responderGranted: true}); - this.measureLayout(); + //not bounce vertical + if (this.currentTransform().scale === minScale) { + transform.translateY = 0 + } else { + const amplitude = + (height * (this.currentTransform().scale - 1)) / (2 * this.currentTransform().scale) + if (transform.translateY <= -amplitude) { + transform.translateY = -amplitude + } + if (transform.translateY >= amplitude) { + transform.translateY = amplitude + } } - - onResponderMove (evt, gestureState) { - this.cancelAnimation(); - - let dx = gestureState.moveX - gestureState.previousMoveX; - let dy = gestureState.moveY - gestureState.previousMoveY; - if (this.props.enableResistance) { - let d = this.applyResistance(dx, dy); - dx = d.dx; - dy = d.dy; - } - - if (!this.props.enableTranslate) { - dx = dy = 0; - } - - let transform = {}; - if (gestureState.previousPinch && gestureState.pinch && this.props.enableScale) { - let scaleBy = gestureState.pinch / gestureState.previousPinch; - let pivotX = gestureState.moveX - this.state.pageX; - let pivotY = gestureState.moveY - this.state.pageY; - - let rect = transformedRect( - transformedRect( - this.contentRect(), - this.currentTransform() - ), - new Transform(scaleBy, dx, dy, { x: pivotX, y: pivotY }) - ); - transform = getTransform(this.contentRect(), rect); - } else { - if (Math.abs(dx) > 2 * Math.abs(dy)) { - dy = 0; - } else if (Math.abs(dy) > 2 * Math.abs(dx)) { - dx = 0; - } - transform.translateX = this.state.translateX + dx / this.state.scale; - transform.translateY = this.state.translateY + dy / this.state.scale; - } - - this.updateTransform(transform); - return true; + this.updateTransform(transform) + return true + } + + onResponderRelease(evt, gestureState) { + let handled = + this.props.onTransformGestureReleased && + this.props.onTransformGestureReleased({ + scale: this.state.scale, + translateX: this.state.translateX, + translateY: this.state.translateY, + }) + if (handled) { + return } - onResponderRelease (evt, gestureState) { - let handled = this.props.onTransformGestureReleased && this.props.onTransformGestureReleased({ - scale: this.state.scale, - translateX: this.state.translateX, - translateY: this.state.translateY - }); - if (handled) { - return; - } - - if (gestureState.doubleTapUp) { - if (!this.props.enableScale) { - this.animateBounce(); - return; - } - let pivotX = 0; - let pivotY = 0; - if (gestureState.dx || gestureState.dy) { - pivotX = gestureState.moveX - this.state.pageX; - pivotY = gestureState.moveY - this.state.pageY; - } else { - pivotX = gestureState.x0 - this.state.pageX; - pivotY = gestureState.y0 - this.state.pageY; - } - - this.performDoubleTapUp(pivotX, pivotY); - } else { - if (this.props.enableTranslate) { - this.performFling(gestureState.vx, gestureState.vy); - } else { - this.animateBounce(); - } - } + if (gestureState.doubleTapUp) { + if (!this.props.enableScale) { + this.animateBounce() + return + } + let pivotX = 0 + let pivotY = 0 + if (gestureState.dx || gestureState.dy) { + pivotX = gestureState.moveX - this.state.pageX + pivotY = gestureState.moveY - this.state.pageY + } else { + pivotX = gestureState.x0 - this.state.pageX + pivotY = gestureState.y0 - this.state.pageY + } + + this.performDoubleTapUp(pivotX, pivotY) + } else { + if (this.props.enableTranslate) { + this.performFling(gestureState.vx, gestureState.vy) + } else { + this.animateBounce() + } } - - performFling (vx, vy) { - let startX = 0; - let startY = 0; - let maxX, minX, maxY, minY; - let availablePanDistance = availableTranslateSpace(this.transformedContentRect(), this.viewPortRect()); - if (vx > 0) { - minX = 0; - if (availablePanDistance.left > 0) { - maxX = availablePanDistance.left + this.props.maxOverScrollDistance; - } else { - maxX = 0; - } - } else { - maxX = 0; - if (availablePanDistance.right > 0) { - minX = -availablePanDistance.right - this.props.maxOverScrollDistance; - } else { - minX = 0; - } - } - if (vy > 0) { - minY = 0; - if (availablePanDistance.top > 0) { - maxY = availablePanDistance.top + this.props.maxOverScrollDistance; - } else { - maxY = 0; - } - } else { - maxY = 0; - if (availablePanDistance.bottom > 0) { - minY = -availablePanDistance.bottom - this.props.maxOverScrollDistance; - } else { - minY = 0; - } - } - - vx *= 1000; // per second - vy *= 1000; - if (Math.abs(vx) > 2 * Math.abs(vy)) { - vy = 0; - } else if (Math.abs(vy) > 2 * Math.abs(vx)) { - vx = 0; - } - - this.scroller.fling(startX, startY, vx, vy, minX, maxX, minY, maxY); + } + + performFling(vx, vy) { + let startX = 0 + let startY = 0 + let maxX, minX, maxY, minY + let availablePanDistance = availableTranslateSpace( + this.transformedContentRect(), + this.viewPortRect(), + ) + if (vx > 0) { + minX = 0 + if (availablePanDistance.left > 0) { + maxX = availablePanDistance.left + this.props.maxOverScrollDistance + } else { + maxX = 0 + } + } else { + maxX = 0 + if (availablePanDistance.right > 0) { + minX = -availablePanDistance.right - this.props.maxOverScrollDistance + } else { + minX = 0 + } + } + if (vy > 0) { + minY = 0 + if (availablePanDistance.top > 0) { + maxY = availablePanDistance.top + this.props.maxOverScrollDistance + } else { + maxY = 0 + } + } else { + maxY = 0 + if (availablePanDistance.bottom > 0) { + minY = -availablePanDistance.bottom - this.props.maxOverScrollDistance + } else { + minY = 0 + } } - performDoubleTapUp (pivotX, pivotY) { - let curScale = this.state.scale; - let scaleBy; - if (curScale > (1 + this.props.maxScale) / 2) { - scaleBy = 1 / curScale; - } else { - scaleBy = this.props.maxScale / curScale; - } - - let rect = transformedRect( - this.transformedContentRect(), - new Transform(scaleBy, 0, 0, { x: pivotX, y: pivotY }) - ); - rect = transformedRect( - rect, - new Transform( - 1, - this.viewPortRect().centerX() - pivotX, - this.viewPortRect().centerY() - pivotY - ) - ); - rect = alignedRect(rect, this.viewPortRect()); - this.animate(rect); + vx *= 1000 // per second + vy *= 1000 + if (Math.abs(vx) > 2 * Math.abs(vy)) { + vy = 0 + } else if (Math.abs(vy) > 2 * Math.abs(vx)) { + vx = 0 } - applyResistance (dx, dy) { - let availablePanDistance = availableTranslateSpace(this.transformedContentRect(), this.viewPortRect()); + this.scroller.fling(startX, startY, vx, vy, minX, maxX, minY, maxY) + } - if ((dx > 0 && availablePanDistance.left < 0) || - (dx < 0 && availablePanDistance.right < 0)) { - dx /= 3; - } - if ((dy > 0 && availablePanDistance.top < 0) || - (dy < 0 && availablePanDistance.bottom < 0)) { - dy /= 3; - } - return { dx, dy }; + performDoubleTapUp(pivotX, pivotY) { + let curScale = this.state.scale + let scaleBy + if (curScale > (1 + this.props.maxScale) / 2) { + scaleBy = 1 / curScale + } else { + scaleBy = this.props.maxScale / curScale } - cancelAnimation () { - this.state.animator.stopAnimation(); + let rect = transformedRect( + this.transformedContentRect(), + new Transform(scaleBy, 0, 0, { x: pivotX, y: pivotY }), + ) + rect = transformedRect( + rect, + new Transform( + 1, + this.viewPortRect().centerX() - pivotX, + this.viewPortRect().centerY() - pivotY, + ), + ) + rect = alignedRect(rect, this.viewPortRect()) + this.animate(rect) + } + + applyResistance(dx, dy) { + let availablePanDistance = availableTranslateSpace( + this.transformedContentRect(), + this.viewPortRect(), + ) + + if ((dx > 0 && availablePanDistance.left < 0) || (dx < 0 && availablePanDistance.right < 0)) { + dx /= 3 } - - animate (targetRect, durationInMillis) { - let duration = 200; - if (durationInMillis) { - duration = durationInMillis; - } - - let fromRect = this.transformedContentRect(); - if (fromRect.equals(targetRect, 0.01)) { - return; - } - - this.state.animator.removeAllListeners(); - this.state.animator.setValue(0); - this.state.animator.addListener((state) => { - let progress = state.value; - - let left = fromRect.left + (targetRect.left - fromRect.left) * progress; - let right = fromRect.right + (targetRect.right - fromRect.right) * progress; - let top = fromRect.top + (targetRect.top - fromRect.top) * progress; - let bottom = fromRect.bottom + (targetRect.bottom - fromRect.bottom) * progress; - - let transform = getTransform(this.contentRect(), new Rect(left, top, right, bottom)); - this.updateTransform(transform); - }); - - Animated.timing( - this.state.animator, - { - toValue: 1, - duration: duration, - easing: Easing.inOut(Easing.ease) - } - ).start(); + if ((dy > 0 && availablePanDistance.top < 0) || (dy < 0 && availablePanDistance.bottom < 0)) { + dy /= 3 } + return { dx, dy } + } - animateBounce () { - let curScale = this.state.scale; - let minScale = 1; - let maxScale = this.props.maxScale; - let scaleBy = 1; - if (curScale > maxScale) { - scaleBy = maxScale / curScale; - } else if (curScale < minScale) { - scaleBy = minScale / curScale; - } + cancelAnimation() { + this.state.animator.stopAnimation() + } - let rect = transformedRect( - this.transformedContentRect(), - new Transform( - scaleBy, - 0, - 0, - { - x: this.viewPortRect().centerX(), - y: this.viewPortRect().centerY() - } - ) - ); - rect = alignedRect(rect, this.viewPortRect()); - this.animate(rect); + animate(targetRect, durationInMillis) { + let duration = 200 + if (durationInMillis) { + duration = durationInMillis } - updateTransform (transform) { - this.setState(transform); + let fromRect = this.transformedContentRect() + if (fromRect.equals(targetRect, 0.01)) { + return } - forceUpdateTransform (transform) { - this.setState(transform); + this.state.animator.removeAllListeners() + this.state.animator.setValue(0) + this.state.animator.addListener((state) => { + let progress = state.value + + let left = fromRect.left + (targetRect.left - fromRect.left) * progress + let right = fromRect.right + (targetRect.right - fromRect.right) * progress + let top = fromRect.top + (targetRect.top - fromRect.top) * progress + let bottom = fromRect.bottom + (targetRect.bottom - fromRect.bottom) * progress + + let transform = getTransform(this.contentRect(), new Rect(left, top, right, bottom)) + this.updateTransform(transform) + }) + + Animated.timing(this.state.animator, { + toValue: 1, + duration: duration, + easing: Easing.inOut(Easing.ease), + }).start() + } + + animateBounce() { + let curScale = this.state.scale + let minScale = 1 + let scaleBy = 1 + if (curScale < minScale) { + scaleBy = minScale / curScale } - getAvailableTranslateSpace () { - return availableTranslateSpace(this.transformedContentRect(), this.viewPortRect()); - } + let rect = transformedRect( + this.transformedContentRect(), + new Transform(scaleBy, 0, 0, { + x: this.viewPortRect().centerX(), + y: this.viewPortRect().centerY(), + }), + ) + rect = alignedRect(rect, this.viewPortRect()) + this.animate(rect) + } + + updateTransform(transform) { + this.setState(transform) + } + + forceUpdateTransform(transform) { + this.setState(transform) + } + + getAvailableTranslateSpace() { + return availableTranslateSpace(this.transformedContentRect(), this.viewPortRect()) + } }