1- import React , { useState , useMemo , useRef } from 'react' ;
1+ import React , { useState , useMemo , useRef , useEffect } from 'react' ;
22import { StyleSheet , ViewProps , Dimensions , View , Image , Animated } from 'react-native' ;
3- import TransitionImage , { ImageProps } from '../TransitionImage' ;
4- import MaskLayer , { MaskLayerProps } from '../MaskLayer' ;
3+ import TransitionImage from '../TransitionImage' ;
4+ import MaskLayer from '../MaskLayer' ;
55import Swiper from '../Swiper' ;
66import { ActivityIndicator } from 'react-native' ;
77export let ImageMainWidth = Dimensions . get ( 'window' ) . width ;
88export let ImageMainHeight = Dimensions . get ( 'window' ) . height ;
9+ import {
10+ PinchGestureHandler ,
11+ PinchGestureHandlerGestureEvent ,
12+ HandlerStateChangeEvent ,
13+ PinchGestureHandlerEventPayload ,
14+ } from 'react-native-gesture-handler' ;
915
1016const defaultImage = 'https://wx3.sinaimg.cn/mw690/4718260ely1gt2cg7t5udj23dw1wkhdu.jpg' ;
1117
@@ -25,12 +31,31 @@ export interface ImageViewerProps extends ViewProps {
2531}
2632
2733function ImageViewer ( props : ImageViewerProps ) {
28- const { width = 150 , height = 150 , src = defaultImage , defaultIndex = 0 , ...others } = props ;
29- const [ visible , setVisible ] = useState ( false ) ;
34+ const { width = 150 , height = 150 , src = defaultImage , defaultIndex = 0 } = props ;
3035 const [ index , setIndex ] = useState < number > ( 0 ) ;
36+ const [ visible , setVisible ] = useState ( false ) ;
37+ const [ canVisible , setCanVisible ] = useState ( true ) ;
3138 const fadeAnim = useRef ( new Animated . Value ( 0 ) ) . current ;
3239
33- useMemo ( ( ) => {
40+ const scale = useRef ( new Animated . Value ( 1 ) ) . current ; // 初始缩放比例为 1
41+ const lastScale = useRef ( 1 ) ; // 上一次的缩放比例
42+
43+ const onPinchGestureEvent = ( event : PinchGestureHandlerGestureEvent ) => {
44+ if ( event . nativeEvent . scale ) {
45+ // 更新缩放比例
46+ scale . setValue ( lastScale . current * event . nativeEvent . scale ) ;
47+ }
48+ } ;
49+
50+ const onPinchHandlerStateChange = ( event : HandlerStateChangeEvent < PinchGestureHandlerEventPayload > ) => {
51+ if ( event . nativeEvent . oldState === 4 && event . nativeEvent . state === 5 ) {
52+ // 手势结束后,保存缩放比例
53+ lastScale . current *= event . nativeEvent . scale ;
54+ scale . setValue ( lastScale . current ) ;
55+ }
56+ } ;
57+
58+ useEffect ( ( ) => {
3459 if ( ! visible ) {
3560 fadeAnim . setValue ( 0 ) ;
3661 return ;
@@ -49,45 +74,37 @@ function ImageViewer(props: ImageViewerProps) {
4974 return src ;
5075 } , [ src ] ) ;
5176
52- const onImgClick = ( index : number ) => {
53- setIndex ( index ) ;
54- setVisible ( true ) ;
55- } ;
77+ const PinchGestureHandlerChild = ( url : string ) =>
78+ useMemo (
79+ ( ) => (
80+ < PinchGestureHandler onGestureEvent = { onPinchGestureEvent } onHandlerStateChange = { onPinchHandlerStateChange } >
81+ < Animated . View style = { [ { transform : [ { scale } ] } ] } >
82+ < Image style = { { width : '100%' , height : '100%' , resizeMode : 'contain' } } source = { { uri : url } } />
83+ </ Animated . View >
84+ </ PinchGestureHandler >
85+ ) ,
86+ [ src , scale ] ,
87+ ) ;
5688
5789 return (
5890 < View style = { { } } >
59- < View style = { { flexDirection : 'row' , justifyContent : 'space-between' } } >
60- { Array . isArray ( src ) ? (
61- src . map ( ( item : ImageViewerDataSourceProps , index : number ) => {
62- return (
63- < TransitionImage
64- key = { index }
65- style = { { width : width , height : height , flex : 1 } }
66- onPress = { ( ) => onImgClick ( index ) }
67- source = { { uri : item . url } }
68- PlaceholderContent = { < ActivityIndicator /> }
69- transition = { true }
70- transitionDuration = { 500 }
71- />
72- ) ;
73- } )
74- ) : (
75- < TransitionImage
76- style = { { width : width , height : height } }
77- onPress = { ( ) => setVisible ( true ) }
78- source = { { uri : imgUrl } }
79- PlaceholderContent = { < ActivityIndicator /> }
80- transition = { true }
81- transitionDuration = { 500 }
82- />
83- ) }
84- </ View >
91+ < TransitionImage
92+ style = { { width : width , height : height } }
93+ onPress = { ( ) => canVisible && setVisible ( true ) }
94+ source = { { uri : imgUrl } }
95+ PlaceholderContent = { < ActivityIndicator /> }
96+ transition = { true }
97+ transitionDuration = { 500 }
98+ onError = { ( e ) => {
99+ if ( e ?. nativeEvent ?. error ) setCanVisible ( false ) ;
100+ } }
101+ />
85102 < MaskLayer visible = { visible } onDismiss = { ( ) => setVisible ( false ) } opacity = { 0.9 } >
86103 < Animated . View style = { [ styles . content , { opacity : fadeAnim } ] } >
87104 { Array . isArray ( src ) ? (
88105 < Swiper dataSource = { src } height = { 200 } autoplay = { false } index = { index } />
89106 ) : (
90- < Image style = { { width : '100%' , height : '100%' , resizeMode : 'contain' } } source = { { uri : src } } />
107+ PinchGestureHandlerChild ( imgUrl )
91108 ) }
92109 </ Animated . View >
93110 </ MaskLayer >
0 commit comments