UNPKG

react-native-image-gallery

Version:

Pure JavaScript image gallery component for iOS and Android

208 lines (182 loc) 7.14 kB
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 }; } componentWillMount () { if (!this.state.imageDimensions) { this.getImageSize(this.props.image); } } 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); } } } componentWillUnmount () { this._mounted = 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 }); } } 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; 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'); } } getViewTransformerInstance () { return this.refs['viewTransformer']; } renderError () { return (this.props.errorComponent && this.props.errorComponent()) || ( <View style={{ flex: 1, backgroundColor: 'black', alignItems: 'center', justifyContent: 'center' }}> <Text style={{ color: 'white', fontSize: 15, fontStyle: 'italic' }}>This image cannot be displayed...</Text> </View> ); } 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 (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) : <Image { ...imageProps } />; return ( <ViewTransformer ref={'viewTransformer'} key={'viewTransformer#' + keyAccumulator} // when image source changes, we should use a different node to avoid reusing previous transform state enableTransform={enableTransform && imageLoaded} // disable transform until image is loaded enableScale={enableScale} enableTranslate={enableTranslate} enableResistance={true} onTransformGestureReleased={onTransformGestureReleased} onViewTransformed={onViewTransformed} maxScale={maxScale} contentAspectRatio={contentAspectRatio} onLayout={this.onLayout} style={style}> { error ? this.renderError() : content } </ViewTransformer> ); } } function sameImage (source, nextSource) { if (source === nextSource) { return true; } if (source && nextSource) { if (source.uri && nextSource.uri) { return source.uri === nextSource.uri; } } return false; }