@@ -76,7 +76,7 @@ const defaultProps = {
7676 closeIcon : false ,
7777 closeIconPosition : 'top-right' ,
7878 showMenuByLongpress : false ,
79- onChange : ( value : number ) => { } ,
79+ onChange : ( ) => { } ,
8080 onClose : ( ) => { } ,
8181} as ImagePreviewProps
8282export const ImagePreview : FunctionComponent < Partial < ImagePreviewProps > > = (
@@ -102,24 +102,22 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
102102 onChange,
103103 } = { ...defaultProps , ...props }
104104 const classPrefix = 'nut-imagepreview'
105- const ref = useRef ( null )
105+ const ref = useRef < HTMLDivElement | null > ( null )
106106 const [ innerNo , setInnerNo ] = usePropsValue < number > ( {
107107 value,
108108 defaultValue,
109109 finalValue : defaultValue ,
110- onChange : ( val : number ) => {
111- onChange ?.( val )
112- } ,
110+ onChange,
113111 } )
114112
115113 const [ showPop , setShowPop ] = useState ( visible )
116114 const [ active , setActive ] = useState ( 0 )
117- const [ maxNo , setMaxNo ] = useState (
118- images ?. length || 0 + ( videos ?. length || 0 )
119- )
120- const [ store , setStore ] = useState ( {
115+ const [ maxNo , setMaxNo ] = useState ( images . length + videos . length )
116+ const [ store , setStore ] = useState < Store > ( {
121117 scale : 1 ,
122118 moveable : false ,
119+ oriDistance : 0 ,
120+ originScale : 1 ,
123121 } )
124122 const [ lastTouchEndTime , setLastTouchEndTime ] = useState ( 0 ) // 用来辅助监听双击
125123 const onTouchStart = ( event : TouchEvent ) => {
@@ -131,76 +129,46 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
131129 const curTouchTime = Date . now ( )
132130 if ( curTouchTime - lastTouchEndTime < 300 ) {
133131 const store1 = store
134- if ( store1 . scale > 1 ) {
135- store1 . scale = 1
136- } else if ( store1 . scale === 1 ) {
137- store1 . scale = 2
138- }
132+ store1 . scale = store1 . scale === 1 ? 2 : 1
139133 scaleNow ( )
140134 }
141135
142- const store1 = store as Store
136+ const store1 = store
143137 store1 . moveable = true
144138
145139 if ( events2 ) {
146140 // 如果开始两指操作,记录初始时刻两指间的距离
147- store1 . oriDistance = getDistance (
148- {
149- x : events . pageX ,
150- y : events . pageY ,
151- } ,
152- {
153- x : events2 . pageX ,
154- y : events2 . pageY ,
155- }
156- )
141+ store1 . oriDistance = getDistance ( events , events2 )
157142 }
158143 // 取到开始两指操作时的放大(缩小比例),store.scale 存储的是当前的放缩比(相对于标准大小 scale 为 1 的情况的放大缩小比)
159- store1 . originScale = store1 . scale || 1
144+ store1 . originScale = store1 . scale
160145 }
161146
162147 const onTouchMove = ( event : TouchEvent ) => {
148+ if ( ! store . moveable ) return
163149 const touches = event . touches
164150 const events = touches [ 0 ]
165151 const events2 = touches [ 1 ]
166152
167- if ( ! store . moveable ) {
168- return
169- }
170- const store1 = store as Store
153+ const store1 = store
171154
172155 // 双指移动
173156 if ( events2 ) {
174- // 获得当前两点间的距离
175- const curDistance = getDistance (
176- {
177- x : events . pageX ,
178- y : events . pageY ,
179- } ,
180- {
181- x : events2 . pageX ,
182- y : events2 . pageY ,
183- }
184- )
185-
157+ const curDistance = getDistance ( events , events2 )
186158 /** 此处计算倍数,距离放大(缩小) k 倍则 scale 也 扩大(缩小) k 倍。距离放大(缩小)倍数 = 结束时两点距离 除以 开始时两点距离
187159 * 注意此处的 scale 变化是基于 store.scale 的。
188160 * store.scale 是一个暂存值,比如第一次放大 2 倍,则 store.scale 为 2。
189161 * 再次两指触碰的时候,store.originScale 就为 store.scale 的值,基于此时的 store.scale 继续放大缩小。 * */
190162 const curScale = curDistance / store1 . oriDistance
191- store1 . scale = store1 . originScale * curScale
192-
193163 // 最大放大 3 倍,缩小后松手要弹回原比例
194- if ( store1 . scale > 3 ) {
195- store1 . scale = 3
196- }
164+ store1 . scale = Math . min ( store1 . originScale * curScale , 3 )
197165 scaleNow ( )
198166 }
199167 }
200168
201169 const onTouchEnd = ( ) => {
202170 setLastTouchEndTime ( Date . now ( ) )
203- const store1 = store as Store
171+ const store1 = store
204172 store1 . moveable = false
205173 if ( ( store1 . scale < 1.1 && store1 . scale > 1 ) || store1 . scale < 1 ) {
206174 store1 . scale = 1
@@ -229,28 +197,25 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
229197 } , [ innerNo ] )
230198
231199 useEffect ( ( ) => {
232- setMaxNo ( images ? .length || 0 + ( videos ? .length || 0 ) )
200+ setMaxNo ( images . length + videos . length )
233201 } , [ images , videos ] )
234202
235203 const scaleNow = ( ) => {
236- if ( ref . current as any ) {
237- ; ( ref . current as any ) . style . transform = `scale(${ store . scale } )`
204+ if ( ref . current ) {
205+ ref . current . style . transform = `scale(${ store . scale } )`
238206 }
239207 }
240208
241- // 计算两个点的距离
242209 const getDistance = ( first : any , second : any ) => {
243- // 计算两个点起始时刻的距离和终止时刻的距离,终止时刻距离变大了则放大,变小了则缩小
244- // 放大 k 倍则 scale 也 扩大 k 倍
245210 return Math . hypot (
246- Math . abs ( second . x - first . x ) ,
247- Math . abs ( second . y - first . y )
211+ Math . abs ( second . pageX - first . pageX ) ,
212+ Math . abs ( second . pageY - first . pageY )
248213 )
249214 }
250215
251216 const slideChangeEnd = ( page : number ) => {
252217 setActive ( page + 1 )
253- onChange ?. ( page + 1 )
218+ onChange && onChange ( page + 1 )
254219 }
255220 const onCloseInner = ( e : ITouchEvent | React . MouseEvent ) => {
256221 e . stopPropagation ( )
@@ -264,25 +229,25 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
264229 } )
265230 }
266231 const closeOnImg = ( e : ITouchEvent | React . MouseEvent ) => {
267- // 点击内容区域的图片是否可以关闭弹层(视频区域由于nut-video做了限制,无法关闭弹层)
268232 e . stopPropagation ( )
269- if ( closeOnContentClick ) {
270- onCloseInner ( e )
271- }
233+ // 点击内容区域的图片是否可以关闭弹层(视频区域由于nut-video做了限制,无法关闭弹层)
234+ if ( closeOnContentClick ) onCloseInner ( e )
272235 }
273236
274237 return (
275238 < Popup
276239 visible = { showPop }
277240 className = { `${ classPrefix } -pop` }
278- // style={{ width: '100%' }}
279241 onClick = { onCloseInner }
280242 >
281243 < View
282244 className = { classNames ( classPrefix , className ) }
283245 style = { style }
284246 ref = { ref }
285247 onTouchStart = { onTouchStart as any }
248+ onTouchMove = { onTouchMove as any }
249+ onTouchEnd = { onTouchEnd }
250+ onTouchCancel = { onTouchEnd }
286251 >
287252 < Swiper
288253 autoPlay = { autoPlay }
@@ -300,73 +265,50 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
300265 defaultValue = { innerNo && ( innerNo > maxNo ? maxNo - 1 : innerNo - 1 ) }
301266 indicator = { indicator }
302267 >
303- { ( videos ?? [ ] )
304- . map (
305- ( item ) =>
306- ( { type : 'video' , data : item } ) as {
307- type : 'video' | 'image'
308- data : ImageOption | VideoOption
309- }
310- )
311- . concat (
312- ( images ?? [ ] ) . map ( ( item ) => ( { type : 'image' , data : item } ) )
313- )
268+ { [
269+ ...videos . map ( ( item ) => ( { type : 'video' , data : item } ) ) ,
270+ ...images . map ( ( item ) => ( { type : 'image' , data : item } ) ) ,
271+ ]
314272 . sort ( ( a , b ) => ( a . data ?. index ?? 0 ) - ( b . data ?. index ?? 0 ) )
315- . map ( ( item , index ) => {
316- if ( item . type === 'video' ) {
317- const { source, options } = item . data as VideoOption
318- return (
319- < SwiperItem
320- key = { index }
321- className = "nut-imagepreview-swiper-item"
322- >
323- < TaroVideo
324- src = { source . src }
325- onClick = { closeOnImg }
326- controls = { options . controls }
327- autoplay = { false }
328- loop = { false }
329- muted = { options . muted }
330- />
331- </ SwiperItem >
332- )
333- }
334- if ( item . type === 'image' ) {
335- const { src } = item . data as ImageOption
336- return (
337- < SwiperItem
338- key = { index }
339- className = "nut-imagepreview-swiper-item"
340- >
341- < Image
342- src = { src }
343- mode = "widthFix"
344- onClick = { closeOnImg }
345- style = { { width : '100%' } }
346- { ...( Taro . getEnv ( ) !== 'WEB' && {
347- showMenuByLongpress,
348- } ) }
349- />
350- </ SwiperItem >
351- )
352- }
353- return null
354- } ) }
273+ . map ( ( item , index ) => (
274+ < SwiperItem key = { index } className = "nut-imagepreview-swiper-item" >
275+ { item . type === 'video' ? (
276+ < TaroVideo
277+ src = { ( item . data as VideoOption ) . source . src }
278+ onClick = { closeOnImg }
279+ controls = { ( item . data as VideoOption ) . options . controls }
280+ autoplay = { false }
281+ loop = { false }
282+ muted = { ( item . data as VideoOption ) . options . muted }
283+ />
284+ ) : (
285+ < Image
286+ src = { ( item . data as ImageOption ) . src }
287+ mode = "widthFix"
288+ onClick = { closeOnImg }
289+ style = { { width : '100%' } }
290+ { ...( Taro . getEnv ( ) !== 'WEB' && {
291+ showMenuByLongpress,
292+ } ) }
293+ />
294+ ) }
295+ </ SwiperItem >
296+ ) ) }
355297 </ Swiper >
356298 </ View >
357- { pagination ? (
299+ { pagination && (
358300 < View className = { `${ classPrefix } -index` } >
359- { active } /{ ( images ? images . length : 0 ) + ( videos ? videos . length : 0 ) }
301+ { active } /{ maxNo }
360302 </ View >
361- ) : null }
362- { closeIcon !== false ? (
303+ ) }
304+ { closeIcon !== false && (
363305 < View
364306 className = { `${ classPrefix } -close ${ closeIconPosition } ` }
365307 onClick = { onCloseInner }
366308 >
367309 { closeIcon === true ? < Close /> : closeIcon }
368310 </ View >
369- ) : null }
311+ ) }
370312 </ Popup >
371313 )
372314}
0 commit comments