mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat(ImagePreview): use the touched point as the center of zooming (#11848)
This commit is contained in:
parent
127b5b28d3
commit
d05972d229
@ -20,7 +20,7 @@ import {
|
|||||||
|
|
||||||
// Composables
|
// Composables
|
||||||
import { useTouch } from '../composables/use-touch';
|
import { useTouch } from '../composables/use-touch';
|
||||||
import { useEventListener } from '@vant/use';
|
import { useEventListener, useRect } from '@vant/use';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { Image } from '../image';
|
import { Image } from '../image';
|
||||||
@ -33,6 +33,11 @@ const getDistance = (touches: TouchList) =>
|
|||||||
(touches[0].clientY - touches[1].clientY) ** 2
|
(touches[0].clientY - touches[1].clientY) ** 2
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getCenter = (touches: TouchList) => ({
|
||||||
|
x: (touches[0].clientX + touches[1].clientX) / 2,
|
||||||
|
y: (touches[0].clientY + touches[1].clientY) / 2,
|
||||||
|
});
|
||||||
|
|
||||||
const bem = createNamespace('image-preview')[1];
|
const bem = createNamespace('image-preview')[1];
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@ -62,6 +67,7 @@ export default defineComponent({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const touch = useTouch();
|
const touch = useTouch();
|
||||||
|
const imageRef = ref<ComponentInstance>();
|
||||||
const swipeItem = ref<ComponentInstance>();
|
const swipeItem = ref<ComponentInstance>();
|
||||||
|
|
||||||
const vertical = computed(() => {
|
const vertical = computed(() => {
|
||||||
@ -77,9 +83,8 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (scale !== 1) {
|
if (scale !== 1) {
|
||||||
const offsetX = moveX / scale;
|
// use matrix to solve the problem of elements not rendering due to safari optimization
|
||||||
const offsetY = moveY / scale;
|
style.transform = `matrix(${scale}, 0, 0, ${scale}, ${moveX}, ${moveY})`;
|
||||||
style.transform = `scale(${scale}, ${scale}) translate(${offsetX}px, ${offsetY}px)`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
@ -111,11 +116,31 @@ export default defineComponent({
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const setScale = (scale: number) => {
|
const setScale = (scale: number, center?: { x: number; y: number }) => {
|
||||||
scale = clamp(scale, +props.minZoom, +props.maxZoom + 1);
|
scale = clamp(scale, +props.minZoom, +props.maxZoom + 1);
|
||||||
|
|
||||||
if (scale !== state.scale) {
|
if (scale !== state.scale) {
|
||||||
|
const ratio = scale / state.scale;
|
||||||
state.scale = scale;
|
state.scale = scale;
|
||||||
|
|
||||||
|
if (center) {
|
||||||
|
const imageRect = useRect(imageRef.value?.$el);
|
||||||
|
const origin = {
|
||||||
|
x: imageRect.width * 0.5,
|
||||||
|
y: imageRect.height * 0.5,
|
||||||
|
};
|
||||||
|
const moveX =
|
||||||
|
state.moveX - (center.x - imageRect.left - origin.x) * (ratio - 1);
|
||||||
|
const moveY =
|
||||||
|
state.moveY - (center.y - imageRect.top - origin.y) * (ratio - 1);
|
||||||
|
|
||||||
|
state.moveX = clamp(moveX, -maxMoveX.value, maxMoveX.value);
|
||||||
|
state.moveY = clamp(moveY, -maxMoveY.value, maxMoveY.value);
|
||||||
|
} else {
|
||||||
|
state.moveX = 0;
|
||||||
|
state.moveY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
emit('scale', {
|
emit('scale', {
|
||||||
scale,
|
scale,
|
||||||
index: props.active,
|
index: props.active,
|
||||||
@ -125,16 +150,17 @@ export default defineComponent({
|
|||||||
|
|
||||||
const resetScale = () => {
|
const resetScale = () => {
|
||||||
setScale(1);
|
setScale(1);
|
||||||
state.moveX = 0;
|
|
||||||
state.moveY = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleScale = () => {
|
const toggleScale = () => {
|
||||||
const scale = state.scale > 1 ? 1 : 2;
|
const scale = state.scale > 1 ? 1 : 2;
|
||||||
|
|
||||||
setScale(scale);
|
setScale(
|
||||||
state.moveX = 0;
|
scale,
|
||||||
state.moveY = 0;
|
scale === 2
|
||||||
|
? { x: touch.startX.value, y: touch.startY.value }
|
||||||
|
: undefined
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let fingerNum: number;
|
let fingerNum: number;
|
||||||
@ -142,6 +168,7 @@ export default defineComponent({
|
|||||||
let startMoveY: number;
|
let startMoveY: number;
|
||||||
let startScale: number;
|
let startScale: number;
|
||||||
let startDistance: number;
|
let startDistance: number;
|
||||||
|
let lastCenter: { x: number; y: number };
|
||||||
let doubleTapTimer: ReturnType<typeof setTimeout> | null;
|
let doubleTapTimer: ReturnType<typeof setTimeout> | null;
|
||||||
let touchStartTime: number;
|
let touchStartTime: number;
|
||||||
let isImageMoved = false;
|
let isImageMoved = false;
|
||||||
@ -170,7 +197,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
if (state.zooming) {
|
if (state.zooming) {
|
||||||
startScale = state.scale;
|
startScale = state.scale;
|
||||||
startDistance = getDistance(event.touches);
|
startDistance = getDistance(touches);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -207,8 +234,8 @@ export default defineComponent({
|
|||||||
if (touches.length === 2) {
|
if (touches.length === 2) {
|
||||||
const distance = getDistance(touches);
|
const distance = getDistance(touches);
|
||||||
const scale = (startScale * distance) / startDistance;
|
const scale = (startScale * distance) / startDistance;
|
||||||
|
lastCenter = getCenter(touches);
|
||||||
setScale(scale);
|
setScale(scale, lastCenter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -279,7 +306,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
const maxZoom = +props.maxZoom;
|
const maxZoom = +props.maxZoom;
|
||||||
if (state.scale > maxZoom) {
|
if (state.scale > maxZoom) {
|
||||||
state.scale = maxZoom;
|
setScale(maxZoom, lastCenter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -331,6 +358,7 @@ export default defineComponent({
|
|||||||
) : (
|
) : (
|
||||||
<Image
|
<Image
|
||||||
v-slots={imageSlots}
|
v-slots={imageSlots}
|
||||||
|
ref={imageRef}
|
||||||
src={props.src}
|
src={props.src}
|
||||||
fit="contain"
|
fit="contain"
|
||||||
class={bem('image', { vertical: vertical.value })}
|
class={bem('image', { vertical: vertical.value })}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user