pinch and zoom image position
See original GitHub issueHey thanks for this nice library!
Im trying to make a instagram pinch and zoom image, I have a working zoomable and movable image, but i struggle with the release phase, when i just pinch on release the image is way of the position so it “jumps” at start.
onGestureMove
works it updates the position correct, but then when i do onGestureRelease
the animated value of gesturePosition.x
and gesturePosition.y
is wrong.
Would love some help on this 💯
Element.js
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Animated, Easing } from 'react-native'
import { PanGestureHandler, PinchGestureHandler, State } from 'react-native-gesture-handler'
const ANIMATION_DURATION = 200
export default class Element extends PureComponent {
opacity = new Animated.Value(1)
static propTypes = {
children: PropTypes.element.isRequired,
}
static contextTypes = {
scaleValue: PropTypes.object,
onGestureStart: PropTypes.func,
onGestureRelease: PropTypes.func,
gesturePosition: PropTypes.object,
}
onPanStateChange = ({ nativeEvent }) => {
switch (nativeEvent.state) {
case State.BEGAN:
return this.onGestureStart()
case State.END:
case State.FAILED:
case State.UNDETERMINED:
case State.CANCELLED:
return this.onGestureRelease()
default:
return null
}
}
onGestureStart = async () => {
const { onGestureStart, gesturePosition } = this.context
const measurement = await this.measureSelected()
this.measurement = measurement
onGestureStart({ element: this, measurement })
gesturePosition.setValue({ x: 0, y: 0 })
gesturePosition.setOffset({
x: measurement.x,
y: measurement.y,
})
Animated.timing(this.opacity, {
toValue: 0,
duration: ANIMATION_DURATION,
}).start()
}
onGestureRelease() {
const { gesturePosition, scaleValue, onGestureRelease } = this.context
Animated.parallel([
Animated.timing(gesturePosition.x, {
toValue: 0,
duration: ANIMATION_DURATION,
easing: Easing.ease,
useNativeDriver: true,
}),
Animated.timing(gesturePosition.y, {
toValue: 0,
duration: ANIMATION_DURATION,
easing: Easing.ease,
useNativeDriver: true,
}),
Animated.timing(scaleValue, {
toValue: 1,
duration: ANIMATION_DURATION,
easing: Easing.ease,
useNativeDriver: true,
}),
]).start(() => {
gesturePosition.setOffset({
x: this.measurement.x,
y: this.measurement.y,
})
// Reset original component opacity
this.opacity.setValue(1)
// Reset scale value
scaleValue.setValue(1)
requestAnimationFrame(() => {
onGestureRelease()
})
})
}
onGestureMove = ({ nativeEvent }) => {
const { gesturePosition } = this.context
const { translationX, translationY } = nativeEvent
gesturePosition.setValue({
x: translationX,
y: translationY,
})
}
onGesturePinch = ({ nativeEvent }) => {
const { scaleValue } = this.context
scaleValue.setValue(nativeEvent.scale)
}
setRef = el => {
this.parent = el
}
/* eslint-disable no-underscore-dangle */
measureSelected = async () => {
const parentMeasurement = await new Promise((resolve, reject) => {
try {
this.parent._component.measureInWindow((x, y) => {
resolve({ x, y })
})
} catch (err) {
reject(err)
}
})
return {
x: parentMeasurement.x,
y: parentMeasurement.y,
}
}
render() {
const imagePan = React.createRef()
return (
<PanGestureHandler
onGestureEvent={this.onGestureMove}
onHandlerStateChange={this.onPanStateChange}
ref={imagePan}
minPointers={2}
maxPointers={2}
minDist={0}
minDeltaX={0}
avgTouches
>
<PinchGestureHandler simultaneousHandlers={imagePan} onGestureEvent={this.onGesturePinch}>
<Animated.View ref={this.setRef} style={{ opacity: this.opacity }}>
{this.props.children}
</Animated.View>
</PinchGestureHandler>
</PanGestureHandler>
)
}
}
Selected.js
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Animated, View } from 'react-native'
const styles = {
container: {
flex: 1,
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
},
background: {
position: 'absolute',
top: 0,
left: 0,
bottom: 0,
right: 0,
backgroundColor: 'black',
},
}
const MINIMUM_SCALE = 1
const MAXIMUM_SCALE = 5
const SCALE_MULTIPLIER = 1.2
export default class Selected extends PureComponent {
static propTypes = {
selected: PropTypes.object,
}
static contextTypes = {
gesturePosition: PropTypes.object,
scaleValue: PropTypes.object,
}
render() {
const { selected } = this.props
const { gesturePosition, scaleValue } = this.context
const scale = scaleValue.interpolate({
inputRange: [MINIMUM_SCALE, MAXIMUM_SCALE],
outputRange: [MINIMUM_SCALE, MAXIMUM_SCALE * SCALE_MULTIPLIER],
extrapolate: 'clamp',
})
const backgroundOpacityValue = scaleValue.interpolate({
inputRange: [1, 1.2, 3],
outputRange: [0, 0.5, 0.8],
})
const transform = [...gesturePosition.getTranslateTransform(), { scale }]
// TODO: See if cloneElement glitches
return (
<View style={styles.container}>
<Animated.View style={[styles.background, { opacity: backgroundOpacityValue }]} />
<Animated.View
style={{
position: 'absolute',
zIndex: 10,
transform,
}}
>
{React.cloneElement(selected.element.props.children, { disableAnimation: true })}
</Animated.View>
</View>
)
}
}
Issue Analytics
- State:
- Created 5 years ago
- Comments:8 (2 by maintainers)
Top Results From Across the Web
Pinch Zoom, Pan Image and Double Tap to ... - Medium
This story shows how to zoom an image in SwiftUI using pinch gesture and tap gesture. We are using UIPinchGestureRecognizer for pinch to...
Read more >pinch and zoom image position · Issue #244
Im trying to make a instagram pinch and zoom image, I have a working zoomable and movable image, but i struggle with the...
Read more >Adding pinch to zoom support to image views
In this tutorial, we'll see how it's possible to use a UIScrollView to make any image view support pinch to zoom.
Read more >Flutter Zoom Image | Pinch To Zoom - YouTube
How to zoom images in Flutter and how to create pinch to zoom in Flutter.Click here to Subscribe to Johannes Milke: ...
Read more >Recognize a pinch gesture - .NET MAUI
This article explains how to use the pinch gesture to perform interactive zoom of an image in .NET MAUI, at the pinch location....
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Yep, @tomasgcs idea worked! Thanks
Hello this happened to me also so what I did is I don’t use
setOffset
Offset somehow doesn’t work right when used togehter withAnimated.timing
so what I did I defined a separateAnimated.Value
to be used asgestureOffset
which is then added to thegesturePosition
usingAnimated.add
For example:const transform = [{ translateX: Animated.add(gesturePosition.x, gestureOffset.x), }, { translateY: Animated.add(gesturePosition.y, gestureOffset.y), }, { scale }]
This workaround works, but I think it is worth looking at Native code to see where is the issue. Hope this will help.