Skip to content

Commit 5b269a9

Browse files
committed
feat: 给taro版本增加缩放功能,优化代码
1 parent 51a1c8c commit 5b269a9

File tree

2 files changed

+62
-126
lines changed

2 files changed

+62
-126
lines changed

src/packages/imagepreview/imagepreview.taro.tsx

Lines changed: 60 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -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
8282
export 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
}

src/packages/imagepreview/imagepreview.tsx

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
138138
store1.oriDistance = getDistance(events, events2)
139139
}
140140
// 取到开始两指操作时的放大(缩小比例),store.scale 存储的是当前的放缩比(相对于标准大小 scale 为 1 的情况的放大缩小比)
141-
store1.originScale = store1.scale || 1
141+
store1.originScale = store1.scale
142142
}
143143

144144
const onTouchMove = (event: TouchEvent) => {
@@ -152,20 +152,14 @@ export const ImagePreview: FunctionComponent<Partial<ImagePreviewProps>> = (
152152

153153
// 双指移动
154154
if (events2) {
155-
// 获得当前两点间的距离
156155
const curDistance = getDistance(events, events2)
157-
158156
/** 此处计算倍数,距离放大(缩小) k 倍则 scale 也 扩大(缩小) k 倍。距离放大(缩小)倍数 = 结束时两点距离 除以 开始时两点距离
159157
* 注意此处的 scale 变化是基于 store.scale 的。
160158
* store.scale 是一个暂存值,比如第一次放大 2 倍,则 store.scale 为 2。
161159
* 再次两指触碰的时候,store.originScale 就为 store.scale 的值,基于此时的 store.scale 继续放大缩小。 * */
162160
const curScale = curDistance / store1.oriDistance
163-
store1.scale = store1.originScale * curScale
164-
165161
// 最大放大 3 倍,缩小后松手要弹回原比例
166-
if (store1.scale > 3) {
167-
store1.scale = 3
168-
}
162+
store1.scale = Math.min(store1.originScale * curScale, 3)
169163
scaleNow()
170164
}
171165
}

0 commit comments

Comments
 (0)