feat(ImagePreview): add closeOnClickOverlay option (#12153)

This commit is contained in:
neverland 2023-08-05 22:45:36 +08:00 committed by GitHub
parent afeef70429
commit d988df7ba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 21 deletions

View File

@ -69,6 +69,7 @@ export const imagePreviewProps = {
startPosition: makeNumericProp(0), startPosition: makeNumericProp(0),
showIndicators: Boolean, showIndicators: Boolean,
closeOnPopstate: truthProp, closeOnPopstate: truthProp,
closeOnClickOverlay: truthProp,
closeIconPosition: makeStringProp<PopupCloseIconPosition>('top-right'), closeIconPosition: makeStringProp<PopupCloseIconPosition>('top-right'),
teleport: [String, Object] as PropType<TeleportProps['to']>, teleport: [String, Object] as PropType<TeleportProps['to']>,
}; };
@ -173,6 +174,7 @@ export default defineComponent({
rootWidth={state.rootWidth} rootWidth={state.rootWidth}
rootHeight={state.rootHeight} rootHeight={state.rootHeight}
disableZoom={state.disableZoom} disableZoom={state.disableZoom}
closeOnClickOverlay={props.closeOnClickOverlay}
onScale={emitScale} onScale={emitScale}
onClose={emitClose} onClose={emitClose}
onLongPress={() => emit('longPress', { index })} onLongPress={() => emit('longPress', { index })}

View File

@ -53,6 +53,7 @@ export default defineComponent({
rootWidth: makeRequiredProp(Number), rootWidth: makeRequiredProp(Number),
rootHeight: makeRequiredProp(Number), rootHeight: makeRequiredProp(Number),
disableZoom: Boolean, disableZoom: Boolean,
closeOnClickOverlay: Boolean,
}, },
emits: ['scale', 'close', 'longPress'], emits: ['scale', 'close', 'longPress'],
@ -241,7 +242,7 @@ export default defineComponent({
} }
}; };
const checkTap = () => { const checkTap = (event: TouchEvent) => {
if (fingerNum > 1) { if (fingerNum > 1) {
return; return;
} }
@ -260,6 +261,12 @@ export default defineComponent({
doubleTapTimer = null; doubleTapTimer = null;
toggleScale(); toggleScale();
} else { } else {
if (
!props.closeOnClickOverlay &&
event.target === swipeItem.value?.$el
) {
return;
}
doubleTapTimer = setTimeout(() => { doubleTapTimer = setTimeout(() => {
emit('close'); emit('close');
doubleTapTimer = null; doubleTapTimer = null;
@ -314,7 +321,7 @@ export default defineComponent({
// eliminate tap delay on safari // eliminate tap delay on safari
preventDefault(event, stopPropagation); preventDefault(event, stopPropagation);
checkTap(); checkTap(event);
touch.reset(); touch.reset();
}; };

View File

@ -195,6 +195,7 @@ Vant exports following ImagePreview utility functions:
| onChange | Emitted when current image changed | _Function_ | - | | onChange | Emitted when current image changed | _Function_ | - |
| onScale | Emitted when scaling current image | _Function_ | - | | onScale | Emitted when scaling current image | _Function_ | - |
| closeOnPopstate | Whether to close when popstate | _boolean_ | `true` | | 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_ | - | | beforeClose | Callback function before close | _(action) => boolean \| Promise_ | - |
| className | Custom className | _string \| Array \| object_ | - | | className | Custom className | _string \| Array \| object_ | - |
| maxZoom | Max zoom | _number \| string_ | `3` | | maxZoom | Max zoom | _number \| string_ | `3` |
@ -220,6 +221,7 @@ Vant exports following ImagePreview utility functions:
| loop | Whether to enable loop | _boolean_ | `true` | | loop | Whether to enable loop | _boolean_ | `true` |
| before-close | Callback function before close | _(action: number) => boolean \| Promise\<boolean\>_ | - | | before-close | Callback function before close | _(action: number) => boolean \| Promise\<boolean\>_ | - |
| close-on-popstate | Whether to close when popstate | _boolean_ | `true` | | 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_ | - | | class-name | Custom className | _string \| Array \| object_ | - |
| max-zoom | Max zoom | _number \| string_ | `3` | | max-zoom | Max zoom | _number \| string_ | `3` |
| min-zoom | Min zoom | _number \| string_ | `1/3` | | min-zoom | Min zoom | _number \| string_ | `1/3` |

View File

@ -209,6 +209,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数:
| onScale | 缩放图片时的回调函数,回调参数为当前索引和当前缩放值组成的对象 | _Function_ | - | | onScale | 缩放图片时的回调函数,回调参数为当前索引和当前缩放值组成的对象 | _Function_ | - |
| beforeClose | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - | | beforeClose | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - |
| closeOnPopstate | 是否在页面回退时自动关闭 | _boolean_ | `true` | | closeOnPopstate | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| closeOnClickOverlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` |
| className | 自定义类名 | _string \| Array \| object_ | - | | className | 自定义类名 | _string \| Array \| object_ | - |
| maxZoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` | | maxZoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` |
| minZoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` | | minZoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` |
@ -235,6 +236,7 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数:
| loop | 是否开启循环播放 | _boolean_ | `true` | | loop | 是否开启循环播放 | _boolean_ | `true` |
| before-close | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - | | before-close | 关闭前的回调函数,返回 `false` 可阻止关闭,支持返回 Promise | _(active: number) => boolean \| Promise\<boolean\>_ | - |
| close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `true` | | close-on-popstate | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| close-on-click-overlay `v4.6.4` | 是否在点击遮罩层后关闭图片预览 | _boolean_ | `true` |
| class-name | 自定义类名 | _string \| Array \| object_ | - | | class-name | 自定义类名 | _string \| Array \| object_ | - |
| max-zoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` | | max-zoom | 手势缩放时,最大缩放比例 | _number \| string_ | `3` |
| min-zoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` | | min-zoom | 手势缩放时,最小缩放比例 | _number \| string_ | `1/3` |

View File

@ -26,6 +26,7 @@ const defaultConfig: ImagePreviewOptions = {
swipeDuration: 300, swipeDuration: 300,
showIndicators: false, showIndicators: false,
closeOnPopstate: true, closeOnPopstate: true,
closeOnClickOverlay: true,
closeIconPosition: 'top-right', closeIconPosition: 'top-right',
}; };

View File

@ -6,11 +6,11 @@ import {
trigger, trigger,
} from '../../../test'; } from '../../../test';
import { LONG_PRESS_START_TIME } from '../../utils'; import { LONG_PRESS_START_TIME } from '../../utils';
import ImagePreviewComponent from '../ImagePreview'; import ImagePreview from '../ImagePreview';
import { images, triggerZoom } from './shared'; import { images, triggerZoom } from './shared';
test('should swipe to current index after calling the swipeTo method', async () => { test('should swipe to current index after calling the swipeTo method', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
images, 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', () => { test('should allow to use the teleport prop', () => {
const root = document.createElement('div'); const root = document.createElement('div');
mount(ImagePreviewComponent, { mount(ImagePreview, {
props: { props: {
show: true, show: true,
teleport: root, teleport: root,
@ -37,7 +37,7 @@ test('should allow to use the teleport prop', () => {
}); });
test('should render cover slot correctly', () => { test('should render cover slot correctly', () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
}, },
@ -50,7 +50,7 @@ test('should render cover slot correctly', () => {
}); });
test('should render index slot correctly', () => { test('should render index slot correctly', () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
}, },
@ -63,7 +63,7 @@ test('should render index slot correctly', () => {
}); });
test('should render close icon when using closeable prop', () => { test('should render close icon when using closeable prop', () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
images, images,
@ -76,7 +76,7 @@ test('should render close icon when using closeable prop', () => {
}); });
test('should change close icon when using close-icon prop', () => { test('should change close icon when using close-icon prop', () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
closeable: 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', () => { test('should change close icon position when using close-icon-position prop', () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
closeable: 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 () => { test('should hide index when show-index prop is false', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
}, },
@ -116,7 +116,7 @@ test('should hide index when show-index prop is false', async () => {
}); });
test('should hide ImagePreview after popstate', async () => { test('should hide ImagePreview after popstate', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, 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 () => { test('should not hide ImagePreview after popstate when close-on-popstate is false', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, show: true,
@ -141,7 +141,7 @@ test('should not hide ImagePreview after popstate when close-on-popstate is fals
}); });
test('render image', async () => { test('render image', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { images, show: true }, props: { images, show: true },
}); });
@ -160,7 +160,7 @@ test('render image', async () => {
}); });
test('before close prop', async () => { test('before close prop', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, show: true,
@ -180,9 +180,46 @@ test('before close prop', async () => {
expect(wrapper.emitted('close')![0]).toBeTruthy(); 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 () => { test('double click', async () => {
const onScale = jest.fn(); const onScale = jest.fn();
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, show: true,
@ -211,7 +248,7 @@ test('double click', async () => {
test('zoom in and drag image to move', async () => { test('zoom in and drag image to move', async () => {
const restore = mockGetBoundingClientRect({ width: 100, height: 100 }); const restore = mockGetBoundingClientRect({ width: 100, height: 100 });
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { images, show: true }, props: { images, show: true },
}); });
@ -240,7 +277,7 @@ test('zoom out', async () => {
const restore = mockGetBoundingClientRect({ width: 100, height: 100 }); const restore = mockGetBoundingClientRect({ width: 100, height: 100 });
const onScale = jest.fn(); const onScale = jest.fn();
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, show: true,
@ -258,7 +295,7 @@ test('zoom out', async () => {
}); });
test('should render image slot correctly', async () => { test('should render image slot correctly', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
images, images,
@ -274,7 +311,7 @@ test('should render image slot correctly', async () => {
}); });
test('should render image slot correctly 2', async () => { test('should render image slot correctly 2', async () => {
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
show: true, show: true,
images, images,
@ -292,7 +329,7 @@ test('should render image slot correctly 2', async () => {
test('should emit long-press event after long press', async () => { test('should emit long-press event after long press', async () => {
const onLongPress = jest.fn(); const onLongPress = jest.fn();
const wrapper = mount(ImagePreviewComponent, { const wrapper = mount(ImagePreview, {
props: { props: {
images, images,
show: true, show: true,

View File

@ -27,6 +27,7 @@ export type ImagePreviewOptions = {
showIndicators?: boolean; showIndicators?: boolean;
closeOnPopstate?: boolean; closeOnPopstate?: boolean;
closeIconPosition?: PopupCloseIconPosition; closeIconPosition?: PopupCloseIconPosition;
closeOnClickOverlay?: boolean;
onClose?(): void; onClose?(): void;
onScale?(args: { scale: number; index: number }): void; onScale?(args: { scale: number; index: number }): void;
onChange?(index: number): void; onChange?(index: number): void;