Fix issue with drag start using old positions on touch devices

This commit is contained in:
Mitchell McCaffrey 2020-11-19 14:53:07 +11:00
parent edab7c85f1
commit b6b68014ed
2 changed files with 137 additions and 127 deletions

View File

@ -91,14 +91,14 @@ function MapInteraction({
onPinchEnd: () => { onPinchEnd: () => {
onSelectedToolChange(previousSelectedToolRef.current); onSelectedToolChange(previousSelectedToolRef.current);
}, },
onDragStart: () => { onDrag: ({ first, last }) => {
interactionEmitter.emit("dragStart"); if (first) {
}, interactionEmitter.emit("dragStart");
onDrag: () => { } else if (last) {
interactionEmitter.emit("drag"); interactionEmitter.emit("dragEnd");
}, } else {
onDragEnd: () => { interactionEmitter.emit("drag");
interactionEmitter.emit("dragEnd"); }
}, },
} }
); );

View File

@ -21,131 +21,141 @@ function useStageInteraction(
const pinchPreviousDistanceRef = useRef(); const pinchPreviousDistanceRef = useRef();
const pinchPreviousOriginRef = useRef(); const pinchPreviousOriginRef = useRef();
const bind = useGesture({ const bind = useGesture(
...gesture, {
onWheelStart: (props) => { ...gesture,
const { event } = props; onWheelStart: (props) => {
isInteractingWithCanvas.current = const { event } = props;
event.target === layer.getCanvas()._canvas; isInteractingWithCanvas.current =
gesture.onWheelStart && gesture.onWheelStart(props); event.target === layer.getCanvas()._canvas;
}, gesture.onWheelStart && gesture.onWheelStart(props);
onWheel: (props) => { },
const { event } = props; onWheel: (props) => {
event.persist(); const { event } = props;
const { pixelY } = normalizeWheel(event); event.persist();
if (preventInteraction || !isInteractingWithCanvas.current) { const { pixelY } = normalizeWheel(event);
return; if (preventInteraction || !isInteractingWithCanvas.current) {
} return;
const newScale = Math.min( }
Math.max( const newScale = Math.min(
stageScale + Math.max(
(pixelY * wheelZoomSpeed * stageScale) / window.innerHeight, stageScale +
minZoom (pixelY * wheelZoomSpeed * stageScale) / window.innerHeight,
), minZoom
maxZoom ),
); maxZoom
);
// Center on pointer // Center on pointer
const pointer = stage.getPointerPosition(); const pointer = stage.getPointerPosition();
const newTranslate = {
x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
};
stage.position(newTranslate);
stageTranslateRef.current = newTranslate;
onStageScaleChange(newScale);
gesture.onWheel && gesture.onWheel(props);
},
onPinchStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
const { da, origin } = props;
const [distance] = da;
const [originX, originY] = origin;
pinchPreviousDistanceRef.current = distance;
pinchPreviousOriginRef.current = { x: originX, y: originY };
gesture.onPinchStart && gesture.onPinchStart(props);
},
onPinch: (props) => {
if (preventInteraction || !isInteractingWithCanvas.current) {
return;
}
const { da, origin } = props;
const [distance] = da;
const [originX, originY] = origin;
// Apply scale
const distanceDelta = distance - pinchPreviousDistanceRef.current;
const originXDelta = originX - pinchPreviousOriginRef.current.x;
const originYDelta = originY - pinchPreviousOriginRef.current.y;
const newScale = Math.min(
Math.max(
stageScale + distanceDelta * touchZoomSpeed * stageScale,
minZoom
),
maxZoom
);
const canvasRect = layer.getCanvas()._canvas.getBoundingClientRect();
const relativeOrigin = {
x: originX - canvasRect.left,
y: originY - canvasRect.top,
};
// Center on pinch origin
const centeredTranslate = {
x:
relativeOrigin.x -
((relativeOrigin.x - stage.x()) / stageScale) * newScale,
y:
relativeOrigin.y -
((relativeOrigin.y - stage.y()) / stageScale) * newScale,
};
// Add pinch movement
const newTranslate = {
x: centeredTranslate.x + originXDelta,
y: centeredTranslate.y + originYDelta,
};
stage.position(newTranslate);
stageTranslateRef.current = newTranslate;
onStageScaleChange(newScale);
pinchPreviousDistanceRef.current = distance;
pinchPreviousOriginRef.current = { x: originX, y: originY };
gesture.onPinch && gesture.onPinch(props);
},
onDragStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
gesture.onDragStart && gesture.onDragStart(props);
},
onDrag: (props) => {
const { delta, pinching } = props;
if (preventInteraction || pinching || !isInteractingWithCanvas.current) {
return;
}
const [dx, dy] = delta;
const stageTranslate = stageTranslateRef.current;
if (tool === "pan") {
const newTranslate = { const newTranslate = {
x: stageTranslate.x + dx, x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
y: stageTranslate.y + dy, y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
}; };
stage.position(newTranslate); stage.position(newTranslate);
stage.draw();
stageTranslateRef.current = newTranslate; stageTranslateRef.current = newTranslate;
}
gesture.onDrag && gesture.onDrag(props); onStageScaleChange(newScale);
gesture.onWheel && gesture.onWheel(props);
},
onPinchStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
const { da, origin } = props;
const [distance] = da;
const [originX, originY] = origin;
pinchPreviousDistanceRef.current = distance;
pinchPreviousOriginRef.current = { x: originX, y: originY };
gesture.onPinchStart && gesture.onPinchStart(props);
},
onPinch: (props) => {
if (preventInteraction || !isInteractingWithCanvas.current) {
return;
}
const { da, origin } = props;
const [distance] = da;
const [originX, originY] = origin;
// Apply scale
const distanceDelta = distance - pinchPreviousDistanceRef.current;
const originXDelta = originX - pinchPreviousOriginRef.current.x;
const originYDelta = originY - pinchPreviousOriginRef.current.y;
const newScale = Math.min(
Math.max(
stageScale + distanceDelta * touchZoomSpeed * stageScale,
minZoom
),
maxZoom
);
const canvasRect = layer.getCanvas()._canvas.getBoundingClientRect();
const relativeOrigin = {
x: originX - canvasRect.left,
y: originY - canvasRect.top,
};
// Center on pinch origin
const centeredTranslate = {
x:
relativeOrigin.x -
((relativeOrigin.x - stage.x()) / stageScale) * newScale,
y:
relativeOrigin.y -
((relativeOrigin.y - stage.y()) / stageScale) * newScale,
};
// Add pinch movement
const newTranslate = {
x: centeredTranslate.x + originXDelta,
y: centeredTranslate.y + originYDelta,
};
stage.position(newTranslate);
stageTranslateRef.current = newTranslate;
onStageScaleChange(newScale);
pinchPreviousDistanceRef.current = distance;
pinchPreviousOriginRef.current = { x: originX, y: originY };
gesture.onPinch && gesture.onPinch(props);
},
onDragStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
gesture.onDragStart && gesture.onDragStart(props);
},
onDrag: (props) => {
const { delta, pinching } = props;
if (
preventInteraction ||
pinching ||
!isInteractingWithCanvas.current
) {
return;
}
const [dx, dy] = delta;
const stageTranslate = stageTranslateRef.current;
if (tool === "pan") {
const newTranslate = {
x: stageTranslate.x + dx,
y: stageTranslate.y + dy,
};
stage.position(newTranslate);
stage.draw();
stageTranslateRef.current = newTranslate;
}
gesture.onDrag && gesture.onDrag(props);
},
}, },
}); {
// Fix drawing using old pointer end position on touch devices when drawing new shapes
drag: { delay: 300 },
}
);
return bind; return bind;
} }