@@ -5,43 +5,259 @@ import Animated, {
55 useSharedValue ,
66} from 'react-native-reanimated' ;
77import { GestureDetector , Gesture } from 'react-native-gesture-handler' ;
8+ import { useState } from 'react' ;
89
910// @ts -ignore it's an image
1011import SIGNET from '../../../ListWithHeader/signet.png' ;
1112
13+ function identity4 ( ) {
14+ 'worklet' ;
15+ return [ 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 ] ;
16+ }
17+
18+ function multiply4 ( a : number [ ] , b : number [ ] ) {
19+ 'worklet' ;
20+ return [
21+ a [ 0 ] * b [ 0 ] + a [ 1 ] * b [ 4 ] + a [ 2 ] * b [ 8 ] + a [ 3 ] * b [ 12 ] ,
22+ a [ 0 ] * b [ 1 ] + a [ 1 ] * b [ 5 ] + a [ 2 ] * b [ 9 ] + a [ 3 ] * b [ 13 ] ,
23+ a [ 0 ] * b [ 2 ] + a [ 1 ] * b [ 6 ] + a [ 2 ] * b [ 10 ] + a [ 3 ] * b [ 14 ] ,
24+ a [ 0 ] * b [ 3 ] + a [ 1 ] * b [ 7 ] + a [ 2 ] * b [ 11 ] + a [ 3 ] * b [ 15 ] ,
25+ a [ 4 ] * b [ 0 ] + a [ 5 ] * b [ 4 ] + a [ 6 ] * b [ 8 ] + a [ 7 ] * b [ 12 ] ,
26+ a [ 4 ] * b [ 1 ] + a [ 5 ] * b [ 5 ] + a [ 6 ] * b [ 9 ] + a [ 7 ] * b [ 13 ] ,
27+ a [ 4 ] * b [ 2 ] + a [ 5 ] * b [ 6 ] + a [ 6 ] * b [ 10 ] + a [ 7 ] * b [ 14 ] ,
28+ a [ 4 ] * b [ 3 ] + a [ 5 ] * b [ 7 ] + a [ 6 ] * b [ 11 ] + a [ 7 ] * b [ 15 ] ,
29+ a [ 8 ] * b [ 0 ] + a [ 9 ] * b [ 4 ] + a [ 10 ] * b [ 8 ] + a [ 11 ] * b [ 12 ] ,
30+ a [ 8 ] * b [ 1 ] + a [ 9 ] * b [ 5 ] + a [ 10 ] * b [ 9 ] + a [ 11 ] * b [ 13 ] ,
31+ a [ 8 ] * b [ 2 ] + a [ 9 ] * b [ 6 ] + a [ 10 ] * b [ 10 ] + a [ 11 ] * b [ 14 ] ,
32+ a [ 8 ] * b [ 3 ] + a [ 9 ] * b [ 7 ] + a [ 10 ] * b [ 11 ] + a [ 11 ] * b [ 15 ] ,
33+ a [ 12 ] * b [ 0 ] + a [ 13 ] * b [ 4 ] + a [ 14 ] * b [ 8 ] + a [ 15 ] * b [ 12 ] ,
34+ a [ 12 ] * b [ 1 ] + a [ 13 ] * b [ 5 ] + a [ 14 ] * b [ 9 ] + a [ 15 ] * b [ 13 ] ,
35+ a [ 12 ] * b [ 2 ] + a [ 13 ] * b [ 6 ] + a [ 14 ] * b [ 10 ] + a [ 15 ] * b [ 14 ] ,
36+ a [ 12 ] * b [ 3 ] + a [ 13 ] * b [ 7 ] + a [ 14 ] * b [ 11 ] + a [ 15 ] * b [ 15 ] ,
37+ ] ;
38+ }
39+
40+ function scale4 ( sx : number , sy : number , sz : number ) {
41+ 'worklet' ;
42+ return [ sx , 0 , 0 , 0 , 0 , sy , 0 , 0 , 0 , 0 , sz , 0 , 0 , 0 , 0 , 1 ] ;
43+ }
44+
45+ function translate4 ( tx : number , ty : number , tz : number ) {
46+ 'worklet' ;
47+ return [ 1 , 0 , 0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 , 1 , 0 , tx , ty , tz , 1 ] ;
48+ }
49+
50+ function rotate4 ( rad : number , x : number , y : number , z : number ) {
51+ 'worklet' ;
52+ const len = Math . hypot ( x , y , z ) ;
53+ const c = Math . cos ( rad ) ;
54+ const s = Math . sin ( rad ) ;
55+ const t = 1 - c ;
56+ x /= len ;
57+ y /= len ;
58+ z /= len ;
59+ return [
60+ t * x * x + c ,
61+ t * x * y - s * z ,
62+ t * x * z + s * y ,
63+ 0 ,
64+ t * x * y + s * z ,
65+ t * y * y + c ,
66+ t * y * z - s * x ,
67+ 0 ,
68+ t * x * z - s * y ,
69+ t * y * z + s * x ,
70+ t * z * z + c ,
71+ 0 ,
72+ 0 ,
73+ 0 ,
74+ 0 ,
75+ 1 ,
76+ ] ;
77+ }
78+
79+ function invert2 ( m : number [ ] ) {
80+ 'worklet' ;
81+ const a = m [ 0 ] ;
82+ const b = m [ 1 ] ;
83+ const c = m [ 2 ] ;
84+ const d = m [ 3 ] ;
85+ const det = a * d - b * c ;
86+
87+ return [ d / det , - b / det , - c / det , a / det ] ;
88+ }
89+
90+ function toTransformedCoords (
91+ point : { x : number ; y : number } ,
92+ matrix : number [ ]
93+ ) {
94+ 'worklet' ;
95+ const m2 = [ matrix [ 0 ] , matrix [ 1 ] , matrix [ 4 ] , matrix [ 5 ] ] ;
96+ const inv = invert2 ( m2 ) ;
97+ const x = point . x ;
98+ const y = point . y ;
99+ const newX = inv [ 0 ] * x + inv [ 2 ] * y ;
100+ const newY = inv [ 1 ] * x + inv [ 3 ] * y ;
101+
102+ return { x : newX , y : newY } ;
103+ }
104+
105+ function createMatrix (
106+ translation : { x : number ; y : number } ,
107+ scale : number ,
108+ rotation : number ,
109+ origin : { x : number ; y : number }
110+ ) {
111+ 'worklet' ;
112+ let matrix = identity4 ( ) ;
113+
114+ if ( scale !== 1 ) {
115+ matrix = multiply4 ( matrix , translate4 ( origin . x , origin . y , 0 ) ) ;
116+ matrix = multiply4 ( matrix , scale4 ( scale , scale , 1 ) ) ;
117+ matrix = multiply4 ( matrix , translate4 ( - origin . x , - origin . y , 0 ) ) ;
118+ }
119+ if ( rotation !== 0 ) {
120+ matrix = multiply4 ( matrix , translate4 ( origin . x , origin . y , 0 ) ) ;
121+ matrix = multiply4 ( matrix , rotate4 ( - rotation , 0 , 0 , 1 ) ) ;
122+ matrix = multiply4 ( matrix , translate4 ( - origin . x , - origin . y , 0 ) ) ;
123+ }
124+
125+ if ( translation . x !== 0 || translation . y !== 0 ) {
126+ matrix = multiply4 ( matrix , translate4 ( translation . x , translation . y , 0 ) ) ;
127+ }
128+
129+ return matrix ;
130+ }
131+
132+ function applyTransformations (
133+ translation : { x : number ; y : number } ,
134+ scale : number ,
135+ rotation : number ,
136+ origin : { x : number ; y : number } ,
137+ matrix : number [ ]
138+ ) {
139+ 'worklet' ;
140+ const translationInViewCoords = toTransformedCoords ( translation , matrix ) ;
141+ const transform = createMatrix (
142+ translationInViewCoords ,
143+ scale ,
144+ rotation ,
145+ origin
146+ ) ;
147+ return multiply4 ( transform , matrix ) ;
148+ }
149+
12150function Photo ( ) {
13- const translationX = useSharedValue ( 0 ) ;
14- const translationY = useSharedValue ( 0 ) ;
151+ const [ size , setSize ] = useState ( { width : 0 , height : 0 } ) ;
152+ const translation = useSharedValue ( { x : 0 , y : 0 } ) ;
153+ const origin = useSharedValue ( { x : 0 , y : 0 } ) ;
15154 const scale = useSharedValue ( 1 ) ;
16155 const rotation = useSharedValue ( 0 ) ;
156+ const isRotating = useSharedValue ( false ) ;
157+ const isScaling = useSharedValue ( false ) ;
158+
159+ const transform = useSharedValue ( identity4 ( ) ) ;
17160
18161 const style = useAnimatedStyle ( ( ) => {
162+ const matrix = applyTransformations (
163+ translation . value ,
164+ scale . value ,
165+ rotation . value ,
166+ origin . value ,
167+ transform . value
168+ ) ;
169+
19170 return {
20171 transform : [
21- { translateX : translationX . value } ,
22- { translateY : translationY . value } ,
23- { scale : scale . value } ,
24- { rotateZ : `${ rotation . value } rad` } ,
172+ { translateX : matrix [ 12 ] } ,
173+ { translateY : matrix [ 13 ] } ,
174+ { scale : Math . hypot ( matrix [ 0 ] , matrix [ 1 ] ) } ,
175+ { rotateZ : `${ Math . atan2 ( matrix [ 1 ] , matrix [ 0 ] ) } rad` } ,
25176 ] ,
26177 } ;
27178 } ) ;
28179
29- const rotationGesture = Gesture . Rotation ( ) . onChange ( ( e ) => {
30- 'worklet' ;
31- rotation . value += e . rotationChange ;
32- } ) ;
180+ const rotationGesture = Gesture . Rotation ( )
181+ . onStart ( ( e ) => {
182+ if ( ! isRotating . value && ! isScaling . value ) {
183+ origin . value = {
184+ x : - ( e . anchorX - size . width / 2 ) ,
185+ y : - ( e . anchorY - size . height / 2 ) ,
186+ } ;
187+ }
188+ isRotating . value = true ;
189+ } )
190+ . onChange ( ( e ) => {
191+ 'worklet' ;
192+ rotation . value += e . rotationChange ;
193+ } )
194+ . onEnd ( ( ) => {
195+ 'worklet' ;
196+ transform . value = applyTransformations (
197+ translation . value ,
198+ scale . value ,
199+ rotation . value ,
200+ origin . value ,
201+ transform . value
202+ ) ;
33203
34- const scaleGesture = Gesture . Pinch ( ) . onChange ( ( e ) => {
35- 'worklet' ;
36- scale . value *= e . scaleChange ;
37- } ) ;
204+ rotation . value = 0 ;
205+ translation . value = { x : 0 , y : 0 } ;
206+ scale . value = 1 ;
207+ isRotating . value = false ;
208+ } ) ;
209+
210+ const scaleGesture = Gesture . Pinch ( )
211+ . onStart ( ( e ) => {
212+ if ( ! isRotating . value && ! isScaling . value ) {
213+ origin . value = {
214+ x : - ( e . focalX - size . width / 2 ) ,
215+ y : - ( e . focalY - size . height / 2 ) ,
216+ } ;
217+ }
218+ isScaling . value = true ;
219+ } )
220+ . onChange ( ( e ) => {
221+ 'worklet' ;
222+ scale . value *= e . scaleChange ;
223+ } )
224+ . onEnd ( ( ) => {
225+ 'worklet' ;
226+ transform . value = applyTransformations (
227+ translation . value ,
228+ scale . value ,
229+ rotation . value ,
230+ origin . value ,
231+ transform . value
232+ ) ;
233+ rotation . value = 0 ;
234+ translation . value = { x : 0 , y : 0 } ;
235+ scale . value = 1 ;
236+ isScaling . value = false ;
237+ } ) ;
38238
39239 const panGesture = Gesture . Pan ( )
40240 . averageTouches ( true )
41241 . onChange ( ( e ) => {
42242 'worklet' ;
43- translationX . value += e . changeX ;
44- translationY . value += e . changeY ;
243+ translation . value = {
244+ x : translation . value . x + e . changeX ,
245+ y : translation . value . y + e . changeY ,
246+ } ;
247+ } )
248+ . onEnd ( ( ) => {
249+ 'worklet' ;
250+ transform . value = applyTransformations (
251+ translation . value ,
252+ scale . value ,
253+ rotation . value ,
254+ origin . value ,
255+ transform . value
256+ ) ;
257+
258+ rotation . value = 0 ;
259+ translation . value = { x : 0 , y : 0 } ;
260+ scale . value = 1 ;
45261 } ) ;
46262
47263 const doubleTapGesture = Gesture . Tap ( )
@@ -62,7 +278,14 @@ function Photo() {
62278
63279 return (
64280 < GestureDetector gesture = { gesture } >
65- < Animated . View style = { [ styles . container , style ] } >
281+ < Animated . View
282+ onLayout = { ( { nativeEvent } ) => {
283+ setSize ( {
284+ width : nativeEvent . layout . width ,
285+ height : nativeEvent . layout . height ,
286+ } ) ;
287+ } }
288+ style = { [ styles . container , style ] } >
66289 < Image source = { SIGNET } style = { styles . image } resizeMode = "contain" />
67290 </ Animated . View >
68291 </ GestureDetector >
@@ -80,8 +303,8 @@ export default function Example() {
80303const styles = StyleSheet . create ( {
81304 home : {
82305 flex : 1 ,
83- alignItems : 'center' ,
84306 justifyContent : 'center' ,
307+ alignItems : 'center' ,
85308 } ,
86309 container : {
87310 width : 240 ,
0 commit comments