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: () => {
onSelectedToolChange(previousSelectedToolRef.current);
},
onDragStart: () => {
interactionEmitter.emit("dragStart");
},
onDrag: () => {
interactionEmitter.emit("drag");
},
onDragEnd: () => {
interactionEmitter.emit("dragEnd");
onDrag: ({ first, last }) => {
if (first) {
interactionEmitter.emit("dragStart");
} else if (last) {
interactionEmitter.emit("dragEnd");
} else {
interactionEmitter.emit("drag");
}
},
}
);

View File

@ -21,131 +21,141 @@ function useStageInteraction(
const pinchPreviousDistanceRef = useRef();
const pinchPreviousOriginRef = useRef();
const bind = useGesture({
...gesture,
onWheelStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
gesture.onWheelStart && gesture.onWheelStart(props);
},
onWheel: (props) => {
const { event } = props;
event.persist();
const { pixelY } = normalizeWheel(event);
if (preventInteraction || !isInteractingWithCanvas.current) {
return;
}
const newScale = Math.min(
Math.max(
stageScale +
(pixelY * wheelZoomSpeed * stageScale) / window.innerHeight,
minZoom
),
maxZoom
);
const bind = useGesture(
{
...gesture,
onWheelStart: (props) => {
const { event } = props;
isInteractingWithCanvas.current =
event.target === layer.getCanvas()._canvas;
gesture.onWheelStart && gesture.onWheelStart(props);
},
onWheel: (props) => {
const { event } = props;
event.persist();
const { pixelY } = normalizeWheel(event);
if (preventInteraction || !isInteractingWithCanvas.current) {
return;
}
const newScale = Math.min(
Math.max(
stageScale +
(pixelY * wheelZoomSpeed * stageScale) / window.innerHeight,
minZoom
),
maxZoom
);
// Center on pointer
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") {
// Center on pointer
const pointer = stage.getPointerPosition();
const newTranslate = {
x: stageTranslate.x + dx,
y: stageTranslate.y + dy,
x: pointer.x - ((pointer.x - stage.x()) / stageScale) * newScale,
y: pointer.y - ((pointer.y - stage.y()) / stageScale) * newScale,
};
stage.position(newTranslate);
stage.draw();
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;
}