Skip to content

Commit

Permalink
Added automaticInitialCoverScale
Browse files Browse the repository at this point in the history
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
  • Loading branch information
maraujop committed Sep 8, 2016
1 parent 6691d4e commit c9ba129
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 29 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ 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

* `onTransformGestureReleased` and `onViewTransformed`:

​ 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.
Expand All @@ -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.
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.
150 changes: 124 additions & 26 deletions library/TransformableImage.js
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -22,23 +22,30 @@ 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
};

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
Expand All @@ -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 <View onLayout={this.onLayout.bind(this)} style={this.props.style} ></View>
}

return (
<ViewTransformer
Expand All @@ -91,12 +107,15 @@ export default class TransformableImage extends Component {
enableScale={this.props.enableScale}
enableTranslate={this.props.enableTranslate}
enableResistance={true}
enableLimits={true}
onTransformGestureReleased={this.props.onTransformGestureReleased}
onViewTransformed={this.props.onViewTransformed}
maxScale={maxScale}
initialScale={initialScale == null ? 1 : initialScale}
contentAspectRatio={contentAspectRatio}
onLayout={this.onLayout.bind(this)}
style={this.props.style}>
style={this.props.style}
>
<Image
{...this.props}
style={[this.props.style, {backgroundColor: 'transparent'}]}
Expand All @@ -123,13 +142,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);
}
}

Expand All @@ -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});
}
}
},
Expand Down Expand Up @@ -178,4 +276,4 @@ function sameSource(source, nextSource) {
}
}
return false;
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}

0 comments on commit c9ba129

Please sign in to comment.