feat(ImagePreview): add long-press event (#11252)

* feat(ImagePreview): add long-press event

* chore: upd

* chore: upd

* chore: upd
This commit is contained in:
neverland 2022-11-13 11:42:26 +08:00 committed by GitHub
parent 839bcd8928
commit 093db7b37e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 28 deletions

View File

@ -77,7 +77,7 @@ export default defineComponent({
props: imagePreviewProps,
emits: ['scale', 'close', 'closed', 'change', 'update:show'],
emits: ['scale', 'close', 'closed', 'change', 'longPress', 'update:show'],
setup(props, { emit, slots }) {
const swipeRef = ref<SwipeInstance>();
@ -146,7 +146,7 @@ export default defineComponent({
indicatorColor="white"
onChange={setActive}
>
{props.images.map((image) => (
{props.images.map((image, index) => (
<ImagePreviewItem
src={image}
show={props.show}
@ -157,6 +157,7 @@ export default defineComponent({
rootHeight={state.rootHeight}
onScale={emitScale}
onClose={emitClose}
onLongPress={() => emit('longPress', { index })}
v-slots={{
image: slots.image,
}}

View File

@ -14,6 +14,7 @@ import {
preventDefault,
createNamespace,
makeRequiredProp,
LONG_PRESS_START_TIME,
type ComponentInstance,
} from '../utils';
@ -45,7 +46,7 @@ export default defineComponent({
rootHeight: makeRequiredProp(Number),
},
emits: ['scale', 'close'],
emits: ['scale', 'close', 'longPress'],
setup(props, { emit, slots }) {
const state = reactive({
@ -200,20 +201,23 @@ export default defineComponent({
const TAP_TIME = 250;
const TAP_OFFSET = 5;
if (
offsetX.value < TAP_OFFSET &&
offsetY.value < TAP_OFFSET &&
deltaTime < TAP_TIME
) {
if (doubleTapTimer) {
clearTimeout(doubleTapTimer);
doubleTapTimer = null;
toggleScale();
} else {
doubleTapTimer = setTimeout(() => {
emit('close');
if (offsetX.value < TAP_OFFSET && offsetY.value < TAP_OFFSET) {
// tap or double tap
if (deltaTime < TAP_TIME) {
if (doubleTapTimer) {
clearTimeout(doubleTapTimer);
doubleTapTimer = null;
}, TAP_TIME);
toggleScale();
} else {
doubleTapTimer = setTimeout(() => {
emit('close');
doubleTapTimer = null;
}, TAP_TIME);
}
}
// long press
else if (deltaTime > LONG_PRESS_START_TIME) {
emit('longPress');
}
}
};

View File

@ -235,11 +235,11 @@ Vant exports following ImagePreview utility functions:
| Event | Description | Arguments |
| --- | --- | --- |
| close | Emitted when closing ImagePreview | _value: { index, url }_ |
| close | Emitted when closing ImagePreview | _{ index: number, url: string }_ |
| closed | Emitted when ImagePreview is closed | - |
| change | Emitted when current image changed | _index: number_ |
| scale | Emitted when scaling current image | _value: ImagePreviewScaleEventParams_ |
| scale | Emitted when scaling current image | _value: ImagePreviewScaleEventParams_ |
| scale | Emitted when scaling current image | _{ index: number, scale: number }_ |
| long-press | Emitted when long press current image | _{ index: number }_ |
### Methods

View File

@ -250,12 +250,13 @@ Vant 中导出了以下 ImagePreview 相关的辅助函数:
通过组件调用 `ImagePreview` 时,支持以下事件:
| 事件 | 说明 | 回调参数 |
| --- | --- | --- |
| close | 关闭时触发 | { index: 索引, url: 图片链接 } |
| closed | 关闭且且动画结束后触发 | - |
| change | 切换当前图片时触发 | index: 当前图片的索引 |
| scale | 缩放当前图片时触发 | { index: 当前图片的索引, scale: 当前缩放的值 } |
| 事件 | 说明 | 回调参数 |
| ---------- | ---------------------- | ---------------------------------- |
| close | 关闭时触发 | _{ index: number, url: string }_ |
| closed | 关闭且且动画结束后触发 | - |
| change | 切换当前图片时触发 | _index: number_ |
| scale | 缩放当前图片时触发 | _{ index: number, scale: number }_ |
| long-press | 长按当前图片时触发 | _{ index: number }_ |
### 方法

View File

@ -3,7 +3,9 @@ import {
later,
triggerDrag,
mockGetBoundingClientRect,
trigger,
} from '../../../test';
import { LONG_PRESS_START_TIME } from '../../utils';
import ImagePreviewComponent from '../ImagePreview';
import { images, triggerZoom } from './shared';
@ -287,3 +289,24 @@ test('should render image slot correctly 2', async () => {
expect(wrapper.html().includes('video')).toBeTruthy();
});
test('should emit long-press event after long press', async () => {
const onLongPress = jest.fn();
const wrapper = mount(ImagePreviewComponent, {
props: {
images,
show: true,
onLongPress,
},
});
await later();
const swipe = wrapper.find('.van-swipe-item');
trigger(swipe, 'touchstart', 0, 0, { x: 0, y: 0 });
await later(LONG_PRESS_START_TIME + 100);
trigger(swipe, 'touchend', 0, 0, { touchList: [] });
expect(onLongPress).toHaveBeenLastCalledWith({
index: 0,
});
});

View File

@ -24,6 +24,7 @@ import {
callInterceptor,
makeNumericProp,
HAPTICS_FEEDBACK,
LONG_PRESS_START_TIME,
type Numeric,
} from '../utils';
@ -33,7 +34,6 @@ import { useCustomFieldValue } from '@vant/use';
const [name, bem] = createNamespace('stepper');
const LONG_PRESS_INTERVAL = 200;
const LONG_PRESS_START_TIME = 600;
const isEqual = (value1?: Numeric, value2?: Numeric) =>
String(value1) === String(value2);

View File

@ -1,6 +1,7 @@
import { nextTick } from 'vue';
import { Stepper } from '..';
import { mount, later } from '../../../test';
import { LONG_PRESS_START_TIME } from '../../utils';
test('should disable buttons and input when using disabled prop', () => {
const wrapper = mount(Stepper, {
@ -126,9 +127,9 @@ test('should update value after long pressing', async () => {
expect(wrapper.emitted('update:modelValue')![0]).toEqual([2]);
await plus.trigger('touchstart');
await later(1000);
await later(LONG_PRESS_START_TIME + 500);
await plus.trigger('touchend');
expect(wrapper.emitted('update:modelValue')).toEqual([[2], [3], [4]]);
expect(wrapper.emitted('update:modelValue')).toEqual([[2], [3], [4], [5]]);
});
test('should allow to disable long press', async () => {

View File

@ -12,3 +12,7 @@ export const BORDER_UNSET_TOP_BOTTOM = `${BORDER}-unset--top-bottom`;
export const HAPTICS_FEEDBACK = 'van-haptics-feedback';
export const FORM_KEY: InjectionKey<FormProvide> = Symbol('van-form');
// Same as the default value of iOS long press time
// https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer/1616423-minimumpressduration
export const LONG_PRESS_START_TIME = 500;