From c9ba129f1609b2a60d4d50e7527b3a827ef4edb6 Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Wed, 31 Aug 2016 10:31:57 +0200 Subject: [PATCH 1/7] Added automaticInitialCoverScale When this prop is activated the image is rendered like if `resizeMode` was set to `cover`. * It takes into account screen density * Recalculate when source changes (this can happen when reusing a not yet unmounted component). Point to view-transformer branch with applyLimits feature --- README.md | 6 +- library/TransformableImage.js | 150 ++++++++++++++++++++++++++++------ package.json | 2 +- 3 files changed, 129 insertions(+), 29 deletions(-) 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..474155d 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -1,7 +1,7 @@ 'use strict'; import React, { Component, PropTypes } from 'react'; -import { Image } from 'react-native'; +import { Image, View, PixelRatio } from 'react-native'; import ViewTransformer from 'react-native-view-transformer'; @@ -22,6 +22,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,16 +31,21 @@ 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 @@ -53,35 +60,44 @@ 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.keyAcumulator + 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 + if (typeof nextProps.pixels == 'undefined') { + this.getImageSize(nextProps.source); + } else { + 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 + } } + if (initialScale == null && this.props.automaticInitialCoverScale) { + return + } return ( + style={this.props.style} + > 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 +238,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 +276,4 @@ function sameSource(source, nextSource) { } } return false; -} \ No newline at end of file +} diff --git a/package.json b/package.json index 264332f..04c9dfa 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#feature-enableLimits" } } From 19fc95cf3de7fdb28c036036e1af0c8ece1178c7 Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Mon, 26 Sep 2016 08:58:48 +0200 Subject: [PATCH 2/7] Wrap placeholder into a ViewTransformer This way until image is loaded pan responders are available and prevent crashing if they are expected to exist. --- library/TransformableImage.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 474155d..69dd897 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -48,7 +48,7 @@ export default class TransformableImage extends Component { initialScale: props.initialScale, imageLoaded: false, pixels: undefined, - keyAcumulator: 1 + keyAccumulator: 1 }; } @@ -60,7 +60,7 @@ export default class TransformableImage extends Component { componentWillReceiveProps(nextProps) { if (!sameSource(this.props.source, nextProps.source)) { - this.setState({ keyAcumulator: this.state.keyAcumulator + 1 }) + //this.setState({ keyAccumulator: this.state.keyAccumulator + 1 }) // Make sure new image resets its initial cover scale if (nextProps.automaticInitialCoverScale) { @@ -95,8 +95,22 @@ export default class TransformableImage extends Component { } } + var child = null if (initialScale == null && this.props.automaticInitialCoverScale) { - return + child = ( + + ) + } else { + child = ( + + ) } return ( @@ -116,14 +130,7 @@ export default class TransformableImage extends Component { onLayout={this.onLayout.bind(this)} style={this.props.style} > - + {child} ); } @@ -195,7 +202,9 @@ export default class TransformableImage extends Component { } } - let newState = { initialScale: initialScale } + let newState = { + initialScale: initialScale, + } if (this.state.updateTransform) { this.refs['viewTransformer'].updateTransform({ scale: initialScale, From 03a0fde3a5b7d11c5a85620714448fc248beaa85 Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Wed, 28 Sep 2016 14:49:10 +0200 Subject: [PATCH 3/7] Prevent calling Image.getSize from TransformableImage --- library/TransformableImage.js | 64 ++++++++--------------------------- 1 file changed, 14 insertions(+), 50 deletions(-) diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 69dd897..931700a 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -52,15 +52,9 @@ export default class TransformableImage extends Component { }; } - componentWillMount() { - if (!this.props.pixels) { - this.getImageSize(this.props.source); - } - } - componentWillReceiveProps(nextProps) { if (!sameSource(this.props.source, nextProps.source)) { - //this.setState({ keyAccumulator: this.state.keyAccumulator + 1 }) + this.setState({ keyAcumulator: this.state.keyAccumulator + 1 }) // Make sure new image resets its initial cover scale if (nextProps.automaticInitialCoverScale) { @@ -68,11 +62,18 @@ export default class TransformableImage extends Component { } // image source changed, clear last image's pixels info if any - if (typeof nextProps.pixels == 'undefined') { - this.getImageSize(nextProps.source); - } else { - this.setInitialCoverScale(this.state.width, this.state.height); - } + 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); } } @@ -202,9 +203,7 @@ export default class TransformableImage extends Component { } } - let newState = { - initialScale: initialScale, - } + let newState = { initialScale: initialScale } if (this.state.updateTransform) { this.refs['viewTransformer'].updateTransform({ scale: initialScale, @@ -235,41 +234,6 @@ export default class TransformableImage extends Component { } } - getImageSize(source) { - if(!source) return; - - DEV && console.log('getImageSize...' + JSON.stringify(source)); - - if (typeof Image.getSize === 'function') { - if (source && source.uri) { - Image.getSize( - source.uri, - (width, height) => { - DEV && console.log('getImageSize...width=' + width + ', height=' + height); - if (width && height) { - this.setState( - {pixels: {width, height}}, - () => { - this.setInitialCoverScale(this.state.width, this.state.height) - } - ); - - if (this.props.onSizeFound) { - this.props.onSizeFound({width, height}); - } - } - }, - (error) => { - console.error('getImageSize...error=' + JSON.stringify(error) + ', source=' + JSON.stringify(source)); - }) - } else { - console.warn('getImageSize...please provide pixels prop for local images'); - } - } else { - console.warn('getImageSize...Image.getSize function not available before react-native v0.28'); - } - } - getViewTransformerInstance() { return this.refs['viewTransformer']; } From 69192dde77de4e15fa642bb9270f72cc24346adf Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Tue, 14 Nov 2017 08:04:51 +0100 Subject: [PATCH 4/7] Call Image.getSize if pixels are not passed --- library/TransformableImage.js | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 931700a..0e8fa6e 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -52,6 +52,12 @@ export default class TransformableImage extends Component { }; } + componentWillMount() { + if (!this.props.pixels) { + this.getImageSize(this.props.source); + } + } + componentWillReceiveProps(nextProps) { if (!sameSource(this.props.source, nextProps.source)) { this.setState({ keyAcumulator: this.state.keyAccumulator + 1 }) @@ -234,6 +240,41 @@ export default class TransformableImage extends Component { } } + getImageSize(source) { + if(!source) return; + + DEV && console.log('getImageSize...' + JSON.stringify(source)); + + if (typeof Image.getSize === 'function') { + if (source && source.uri) { + Image.getSize( + source.uri, + (width, height) => { + DEV && console.log('getImageSize...width=' + width + ', height=' + height); + if (width && height) { + this.setState( + {pixels: {width, height}}, + () => { + this.setInitialCoverScale(this.state.width, this.state.height) + } + ); + + if (this.props.onSizeFound) { + this.props.onSizeFound({width, height}); + } + } + }, + (error) => { + console.error('getImageSize...error=' + JSON.stringify(error) + ', source=' + JSON.stringify(source)); + }) + } else { + console.warn('getImageSize...please provide pixels prop for local images'); + } + } else { + console.warn('getImageSize...Image.getSize function not available before react-native v0.28'); + } + } + getViewTransformerInstance() { return this.refs['viewTransformer']; } From bc7494e6d996aa7810855bee8bce0a116e6435fc Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Mon, 12 Feb 2018 18:14:21 +0100 Subject: [PATCH 5/7] Pinpointing view-transformer commit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 04c9dfa..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": "git+https://github.com/maraujop/react-native-view-transformer.git#feature-enableLimits" + "react-native-view-transformer": "git+https://github.com/maraujop/react-native-view-transformer.git#755dfcd09ea01d75244728e69c63d9c89ef69a5c" } } From 69767daea8f000c4e2f8263094ca6867d5ea28e8 Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Mon, 12 Feb 2018 18:15:27 +0100 Subject: [PATCH 6/7] using separate propTypes --- library/TransformableImage.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 0e8fa6e..7b20821 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -1,6 +1,7 @@ 'use strict'; -import React, { Component, PropTypes } from 'react'; +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; import { Image, View, PixelRatio } from 'react-native'; import ViewTransformer from 'react-native-view-transformer'; From b1eb97faa5f9fc9d23bd284483bb2225cb7ea35c Mon Sep 17 00:00:00 2001 From: Miguel Araujo Perez Date: Tue, 24 Apr 2018 17:41:36 +0200 Subject: [PATCH 7/7] Use react-native-fast-image --- library/TransformableImage.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/TransformableImage.js b/library/TransformableImage.js index 7b20821..c9fe90f 100644 --- a/library/TransformableImage.js +++ b/library/TransformableImage.js @@ -1,5 +1,7 @@ 'use strict'; +import FastImage from 'react-native-fast-image' + import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Image, View, PixelRatio } from 'react-native'; @@ -110,7 +112,7 @@ export default class TransformableImage extends Component { ) } else { child = ( -