diff --git a/README.md b/README.md index 2adfaea..9e3999f 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ render() { } ``` -You can provide `enableTransform`, `enableScale` and `enableTranslate` props to control corresponding features. +You can provide `enableTransform`, `enableScale`, `enableTranslate` and `enableLimits` props to control corresponding features. #### Other props @@ -42,6 +42,8 @@ You can provide `enableTransform`, `enableScale` and `enableTranslate` props to ​ inherited from [react-native-view-transformer](https://github.com/ldn0x7dc/react-native-view-transformer) +* `automaticCoverInitialScale`: When set to `true` makes the Image look like if `resizeMode="cover"` + ### Attention * If you are using react-native v0.27 and below, or if the image source is local (`source={require('...')}`), you should provide the **pixels** prop, like `pixels={{width: 3607, height: 2400}}` (ask your API server to provide the pixels info for remote images). This prop is used to align the edge of the image content with the view's boundry and to determine the max scale. @@ -51,4 +53,4 @@ You can provide `enableTransform`, `enableScale` and `enableTranslate` props to ## Image Gallery -If you are looking for an image gallery component, please refer to [**react-native-gallery**](https://github.com/ldn0x7dc/react-native-gallery), which is based on this component. \ No newline at end of file +If you are looking for an image gallery component, please refer to [**react-native-gallery**](https://github.com/ldn0x7dc/react-native-gallery), which is based on this component. diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 6da1c08..c9fe90f 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -1,7 +1,10 @@ 'use strict'; -import React, { Component, PropTypes } from 'react'; -import { Image } from 'react-native'; +import FastImage from 'react-native-fast-image' + +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { Image, View, PixelRatio } from 'react-native'; import ViewTransformer from 'react-native-view-transformer'; @@ -22,6 +25,8 @@ export default class TransformableImage extends Component { enableTransform: PropTypes.bool, enableScale: PropTypes.bool, enableTranslate: PropTypes.bool, + initialScale: PropTypes.number, + automaticInitialCoverScale: PropTypes.bool, onTransformGestureReleased: PropTypes.func, onViewTransformed: PropTypes.func }; @@ -29,19 +34,24 @@ export default class TransformableImage extends Component { static defaultProps = { enableTransform: true, enableScale: true, - enableTranslate: true + enableTranslate: true, + initialScale: null, + updateTransform: false, + automaticInitialCoverScale: false, }; constructor(props) { super(props); + this.setInitialCoverScale = this.setInitialCoverScale.bind(this); this.state = { width: 0, height: 0, + initialScale: props.initialScale, imageLoaded: false, pixels: undefined, - keyAcumulator: 1 + keyAccumulator: 1 }; } @@ -53,35 +63,65 @@ export default class TransformableImage extends Component { componentWillReceiveProps(nextProps) { if (!sameSource(this.props.source, nextProps.source)) { - //image source changed, clear last image's pixels info if any - this.setState({pixels: undefined, keyAcumulator: this.state.keyAcumulator + 1}) - this.getImageSize(nextProps.source); + this.setState({ keyAcumulator: this.state.keyAccumulator + 1 }) + + // Make sure new image resets its initial cover scale + if (nextProps.automaticInitialCoverScale) { + this.setState({ updateTransform: true }) + } + + // image source changed, clear last image's pixels info if any + this.setInitialCoverScale(this.state.width, this.state.height); + } + } + + componentDidUpdate(prevProps, prevState) { + // When we are given the image size and have to calculate initialScale + if ( + this.props.pixels != prevProps.pixels && + this.state.initialScale == null && + this.props.automaticInitialCoverScale + ) { + this.setInitialCoverScale(this.state.width, this.state.height); } } render() { let maxScale = 1; let contentAspectRatio = undefined; - let width, height; //pixels - - if (this.props.pixels) { - //if provided via props - width = this.props.pixels.width; - height = this.props.pixels.height; - } else if (this.state.pixels) { - //if got using Image.getSize() - width = this.state.pixels.width; - height = this.state.pixels.height; - } + let { width, height } = this.getWidthAndHeight(); + let initialScale = this.state.initialScale; if (width && height) { contentAspectRatio = width / height; + if (this.state.width && this.state.height) { maxScale = Math.max(width / this.state.width, height / this.state.height); maxScale = Math.max(1, maxScale); } + + if (maxScale < initialScale && initialScale != null) { + maxScale = initialScale + 2 + } } + var child = null + if (initialScale == null && this.props.automaticInitialCoverScale) { + child = ( + + ) + } else { + child = ( + + ) + } return ( - + style={this.props.style} + > + {child} ); } @@ -123,13 +159,87 @@ export default class TransformableImage extends Component { }); } + getWidthAndHeight() { + let width, height; + + if (this.props.pixels) { + // If provided via props + width = this.props.pixels.width; + height = this.props.pixels.height; + } else if (this.state.pixels) { + // If got using Image.getSize() + width = this.state.pixels.width; + height = this.state.pixels.height; + } + + return { width, height } + } + + setInitialCoverScale(viewWidth, viewHeight) { + // automatic cover scale using a rule of three + let { width, height } = this.getWidthAndHeight(); + + if ( + viewWidth == 0 || viewHeight == 0 || + typeof width == 'undefined' || typeof height == 'undefined' + ) { + return + } + + let initialScale = this.props.initialScale; + + viewHeight = PixelRatio.getPixelSizeForLayoutSize(viewHeight) + viewWidth = PixelRatio.getPixelSizeForLayoutSize(viewWidth) + + if (this.props.automaticInitialCoverScale) { + + if (height > width) { + var proportionalImageHeight = (viewHeight * width) / viewWidth; + + if (proportionalImageHeight > height) { + initialScale = proportionalImageHeight / height; + } else { + initialScale = height / proportionalImageHeight; + } + } else { + var proportionalImageWidth = (viewWidth * height) / viewHeight; + + if (proportionalImageWidth > width) { + initialScale = proportionalImageWidth / width; + } else { + initialScale = width / proportionalImageWidth; + } + } + } + + let newState = { initialScale: initialScale } + if (this.state.updateTransform) { + this.refs['viewTransformer'].updateTransform({ + scale: initialScale, + translateX: 0, + translateY: 0, + }); + newState['updateTransform'] = false + } + + this.setState(newState) + return initialScale; + } + onLayout(e) { - let {width, height} = e.nativeEvent.layout; + let { width, height } = e.nativeEvent.layout; + if (this.state.width !== width || this.state.height !== height) { - this.setState({ + let newState = { width: width, - height: height - }); + height: height, + }; + + if (this.state.initialScale == null && this.props.automaticInitialCoverScale) { + this.setInitialCoverScale(width, height); + } + + this.setState(newState); } } @@ -145,10 +255,15 @@ export default class TransformableImage extends Component { (width, height) => { DEV && console.log('getImageSize...width=' + width + ', height=' + height); if (width && height) { - if(this.state.pixels && this.state.pixels.width === width && this.state.pixels.height === height) { - //no need to update state - } else { - this.setState({pixels: {width, height}}); + this.setState( + {pixels: {width, height}}, + () => { + this.setInitialCoverScale(this.state.width, this.state.height) + } + ); + + if (this.props.onSizeFound) { + this.props.onSizeFound({width, height}); } } }, @@ -178,4 +293,4 @@ function sameSource(source, nextSource) { } } return false; -} \ No newline at end of file +} diff --git a/package.json b/package.json index 264332f..203909e 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,6 @@ }, "homepage": "https://github.com/ldn0x7dc/react-native-transformable-image#readme", "dependencies": { - "react-native-view-transformer": "0.0.28" + "react-native-view-transformer": "git+https://github.com/maraujop/react-native-view-transformer.git#755dfcd09ea01d75244728e69c63d9c89ef69a5c" } }