diff --git a/packages/vant/src/image-preview/ImagePreview.tsx b/packages/vant/src/image-preview/ImagePreview.tsx index c8a7c8570..4f2b499fa 100644 --- a/packages/vant/src/image-preview/ImagePreview.tsx +++ b/packages/vant/src/image-preview/ImagePreview.tsx @@ -69,6 +69,7 @@ export const imagePreviewProps = { startPosition: makeNumericProp(0), showIndicators: Boolean, closeOnPopstate: truthProp, + closeOnClickOverlay: truthProp, closeIconPosition: makeStringProp('top-right'), teleport: [String, Object] as PropType, }; @@ -173,6 +174,7 @@ export default defineComponent({ rootWidth={state.rootWidth} rootHeight={state.rootHeight} disableZoom={state.disableZoom} + closeOnClickOverlay={props.closeOnClickOverlay} onScale={emitScale} onClose={emitClose} onLongPress={() => emit('longPress', { index })} diff --git a/packages/vant/src/image-preview/ImagePreviewItem.tsx b/packages/vant/src/image-preview/ImagePreviewItem.tsx index 55bcc01e9..a51f2a3cc 100644 --- a/packages/vant/src/image-preview/ImagePreviewItem.tsx +++ b/packages/vant/src/image-preview/ImagePreviewItem.tsx @@ -53,6 +53,7 @@ export default defineComponent({ rootWidth: makeRequiredProp(Number), rootHeight: makeRequiredProp(Number), disableZoom: Boolean, + closeOnClickOverlay: Boolean, }, emits: ['scale', 'close', 'longPress'], @@ -241,7 +242,7 @@ export default defineComponent({ } }; - const checkTap = () => { + const checkTap = (event: TouchEvent) => { if (fingerNum > 1) { return; } @@ -260,6 +261,12 @@ export default defineComponent({ doubleTapTimer = null; toggleScale(); } else { + if ( + !props.closeOnClickOverlay && + event.target === swipeItem.value?.$el + ) { + return; + } doubleTapTimer = setTimeout(() => { emit('close'); doubleTapTimer = null; @@ -314,7 +321,7 @@ export default defineComponent({ // eliminate tap delay on safari preventDefault(event, stopPropagation); - checkTap(); + checkTap(event); touch.reset(); }; diff --git a/packages/vant/src/image-preview/README.md b/packages/vant/src/image-preview/README.md index 1ccacf574..394d78c16 100644 --- a/packages/vant/src/image-preview/README.md +++ b/packages/vant/src/image-preview/README.md @@ -195,6 +195,7 @@ Vant exports following ImagePreview utility functions: | onChange | Emitted when current image changed | _Function_ | - | | onScale | Emitted when scaling current image | _Function_ | - | | closeOnPopstate | Whether to close when popstate | _boolean_ | `true` | +| closeOnClickOverlay `v4.6.4` | Whether to close when overlay is clicked | _boolean_ | `true` | | beforeClose | Callback function before close | _(action) => boolean \| Promise_ | - | | className | Custom className | _string \| Array \| object_ | - | | maxZoom | Max zoom | _number \| string_ | `3` | @@ -220,6 +221,7 @@ Vant exports following ImagePreview utility functions: | loop | Whether to enable loop | _boolean_ | `true` | | before-close | Callback function before close | _(action: number) => boolean \| Promise\_ | - | | close-on-popstate | Whether to close when popstate | _boolean_ | `true` | +| close-on-click-overlay `v4.6.4` | Whether to close when overlay is clicked | _boolean_ | `true` | | class-name | Custom className | _string \| Array \| object_ | - | | max-zoom | Max zoom | _number \| string_ | `3` | | min-zoom | Min zoom | _number \| string_ | `1/3` | diff --git a/packages/vant/src/image-preview/README.zh-CN.md b/packages/vant/src/image-preview/README.zh-CN.md index 8179f4513..e5d2af465 100644 --- a/packages/vant/src/image-preview/README.zh-CN.md +++ b/packages/vant/src/image-preview/README.zh-CN.md @@ -209,6 +209,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数: | onScale | 缩放图片时的回调函数,回调参数为当前索引和当前缩放值组成的对象 | _Function_ | - | | beforeClose | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\_ | - | | closeOnPopstate | 是否在页面回退时自动关闭 | _boolean_ | `true` | +| closeOnClickOverlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` | | className | 自定义类名 | _string \| Array \| object_ | - | | maxZoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` | | minZoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` | @@ -235,6 +236,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数: | loop | 是否开启循环播放 | _boolean_ | `true` | | before-close | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\_ | - | | close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `true` | +| close-on-click-overlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` | | class-name | 自定义类名 | _string \| Array \| object_ | - | | max-zoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` | | min-zoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` | diff --git a/packages/vant/src/image-preview/function-call.tsx b/packages/vant/src/image-preview/function-call.tsx index 1de9c4ee9..4ec6dd59f 100644 --- a/packages/vant/src/image-preview/function-call.tsx +++ b/packages/vant/src/image-preview/function-call.tsx @@ -26,6 +26,7 @@ const defaultConfig: ImagePreviewOptions = { swipeDuration: 300, showIndicators: false, closeOnPopstate: true, + closeOnClickOverlay: true, closeIconPosition: 'top-right', }; diff --git a/packages/vant/src/image-preview/test/index.spec.ts b/packages/vant/src/image-preview/test/index.spec.ts index 58e2fb974..bbe9d6952 100644 --- a/packages/vant/src/image-preview/test/index.spec.ts +++ b/packages/vant/src/image-preview/test/index.spec.ts @@ -6,11 +6,11 @@ import { trigger, } from '../../../test'; import { LONG_PRESS_START_TIME } from '../../utils'; -import ImagePreviewComponent from '../ImagePreview'; +import ImagePreview from '../ImagePreview'; import { images, triggerZoom } from './shared'; test('should swipe to current index after calling the swipeTo method', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, images, @@ -26,7 +26,7 @@ test('should swipe to current index after calling the swipeTo method', async () test('should allow to use the teleport prop', () => { const root = document.createElement('div'); - mount(ImagePreviewComponent, { + mount(ImagePreview, { props: { show: true, teleport: root, @@ -37,7 +37,7 @@ test('should allow to use the teleport prop', () => { }); test('should render cover slot correctly', () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, }, @@ -50,7 +50,7 @@ test('should render cover slot correctly', () => { }); test('should render index slot correctly', () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, }, @@ -63,7 +63,7 @@ test('should render index slot correctly', () => { }); test('should render close icon when using closeable prop', () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, images, @@ -76,7 +76,7 @@ test('should render close icon when using closeable prop', () => { }); test('should change close icon when using close-icon prop', () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, closeable: true, @@ -90,7 +90,7 @@ test('should change close icon when using close-icon prop', () => { }); test('should change close icon position when using close-icon-position prop', () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, closeable: true, @@ -104,7 +104,7 @@ test('should change close icon position when using close-icon-position prop', () }); test('should hide index when show-index prop is false', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, }, @@ -116,7 +116,7 @@ test('should hide index when show-index prop is false', async () => { }); test('should hide ImagePreview after popstate', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, @@ -128,7 +128,7 @@ test('should hide ImagePreview after popstate', async () => { }); test('should not hide ImagePreview after popstate when close-on-popstate is false', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, @@ -141,7 +141,7 @@ test('should not hide ImagePreview after popstate when close-on-popstate is fals }); test('render image', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true }, }); @@ -160,7 +160,7 @@ test('render image', async () => { }); test('before close prop', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, @@ -180,9 +180,46 @@ test('before close prop', async () => { expect(wrapper.emitted('close')![0]).toBeTruthy(); }); +test('should close when overlay is clicked', async () => { + const wrapper = mount(ImagePreview, { + props: { + images, + show: true, + 'onUpdate:show': (show) => { + wrapper.setProps({ show }); + }, + }, + }); + + const swipe = wrapper.find('.van-swipe-item'); + + await triggerDrag(swipe, 0, 0); + await later(300); + expect(wrapper.emitted('close')).toBeTruthy(); +}); + +test('should not close when overlay is clicked and closeOnClickOverlay is false', async () => { + const wrapper = mount(ImagePreview, { + props: { + images, + show: true, + closeOnClickOverlay: false, + 'onUpdate:show': (show) => { + wrapper.setProps({ show }); + }, + }, + }); + + const swipe = wrapper.find('.van-swipe-item'); + + triggerDrag(swipe, 0, 0); + await later(300); + expect(wrapper.emitted('close')).toBeFalsy(); +}); + test('double click', async () => { const onScale = jest.fn(); - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, @@ -211,7 +248,7 @@ test('double click', async () => { test('zoom in and drag image to move', async () => { const restore = mockGetBoundingClientRect({ width: 100, height: 100 }); - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true }, }); @@ -240,7 +277,7 @@ test('zoom out', async () => { const restore = mockGetBoundingClientRect({ width: 100, height: 100 }); const onScale = jest.fn(); - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, @@ -258,7 +295,7 @@ test('zoom out', async () => { }); test('should render image slot correctly', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, images, @@ -274,7 +311,7 @@ test('should render image slot correctly', async () => { }); test('should render image slot correctly 2', async () => { - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { show: true, images, @@ -292,7 +329,7 @@ test('should render image slot correctly 2', async () => { test('should emit long-press event after long press', async () => { const onLongPress = jest.fn(); - const wrapper = mount(ImagePreviewComponent, { + const wrapper = mount(ImagePreview, { props: { images, show: true, diff --git a/packages/vant/src/image-preview/types.ts b/packages/vant/src/image-preview/types.ts index fd44ac43a..75263595e 100644 --- a/packages/vant/src/image-preview/types.ts +++ b/packages/vant/src/image-preview/types.ts @@ -27,6 +27,7 @@ export type ImagePreviewOptions = { showIndicators?: boolean; closeOnPopstate?: boolean; closeIconPosition?: PopupCloseIconPosition; + closeOnClickOverlay?: boolean; onClose?(): void; onScale?(args: { scale: number; index: number }): void; onChange?(index: number): void;