mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
9cdf8a99ed
* Facebook -> Meta in copyright rg --files | xargs sed -i 's#Copyright (c) Facebook, Inc. and its affiliates.#Copyright (c) Meta Platforms, Inc. and affiliates.#g' * Manual tweaks
255 lines
6.2 KiB
JavaScript
255 lines
6.2 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {NormalizedWheelDelta} from './utils/normalizeWheel';
|
|
import type {Point} from './geometry';
|
|
|
|
import {useEffect, useRef} from 'react';
|
|
import {normalizeWheel} from './utils/normalizeWheel';
|
|
|
|
export type ClickInteraction = {
|
|
type: 'click',
|
|
payload: {
|
|
event: MouseEvent,
|
|
location: Point,
|
|
},
|
|
};
|
|
export type DoubleClickInteraction = {
|
|
type: 'double-click',
|
|
payload: {
|
|
event: MouseEvent,
|
|
location: Point,
|
|
},
|
|
};
|
|
export type MouseDownInteraction = {
|
|
type: 'mousedown',
|
|
payload: {
|
|
event: MouseEvent,
|
|
location: Point,
|
|
},
|
|
};
|
|
export type MouseMoveInteraction = {
|
|
type: 'mousemove',
|
|
payload: {
|
|
event: MouseEvent,
|
|
location: Point,
|
|
},
|
|
};
|
|
export type MouseUpInteraction = {
|
|
type: 'mouseup',
|
|
payload: {
|
|
event: MouseEvent,
|
|
location: Point,
|
|
},
|
|
};
|
|
export type WheelPlainInteraction = {
|
|
type: 'wheel-plain',
|
|
payload: {
|
|
event: WheelEvent,
|
|
location: Point,
|
|
delta: NormalizedWheelDelta,
|
|
},
|
|
};
|
|
export type WheelWithShiftInteraction = {
|
|
type: 'wheel-shift',
|
|
payload: {
|
|
event: WheelEvent,
|
|
location: Point,
|
|
delta: NormalizedWheelDelta,
|
|
},
|
|
};
|
|
export type WheelWithControlInteraction = {
|
|
type: 'wheel-control',
|
|
payload: {
|
|
event: WheelEvent,
|
|
location: Point,
|
|
delta: NormalizedWheelDelta,
|
|
},
|
|
};
|
|
export type WheelWithMetaInteraction = {
|
|
type: 'wheel-meta',
|
|
payload: {
|
|
event: WheelEvent,
|
|
location: Point,
|
|
delta: NormalizedWheelDelta,
|
|
},
|
|
};
|
|
|
|
export type Interaction =
|
|
| ClickInteraction
|
|
| DoubleClickInteraction
|
|
| MouseDownInteraction
|
|
| MouseMoveInteraction
|
|
| MouseUpInteraction
|
|
| WheelPlainInteraction
|
|
| WheelWithShiftInteraction
|
|
| WheelWithControlInteraction
|
|
| WheelWithMetaInteraction;
|
|
|
|
let canvasBoundingRectCache = null;
|
|
function cacheFirstGetCanvasBoundingRect(
|
|
canvas: HTMLCanvasElement,
|
|
): ClientRect {
|
|
if (
|
|
canvasBoundingRectCache &&
|
|
canvas.width === canvasBoundingRectCache.width &&
|
|
canvas.height === canvasBoundingRectCache.height
|
|
) {
|
|
return canvasBoundingRectCache.rect;
|
|
}
|
|
canvasBoundingRectCache = {
|
|
width: canvas.width,
|
|
height: canvas.height,
|
|
rect: canvas.getBoundingClientRect(),
|
|
};
|
|
return canvasBoundingRectCache.rect;
|
|
}
|
|
|
|
export function useCanvasInteraction(
|
|
canvasRef: {current: HTMLCanvasElement | null},
|
|
interactor: (interaction: Interaction) => void,
|
|
) {
|
|
const isMouseDownRef = useRef<boolean>(false);
|
|
const didMouseMoveWhileDownRef = useRef<boolean>(false);
|
|
|
|
useEffect(() => {
|
|
const canvas = canvasRef.current;
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
|
|
function localToCanvasCoordinates(localCoordinates: Point): Point {
|
|
// $FlowFixMe[incompatible-call] found when upgrading Flow
|
|
const canvasRect = cacheFirstGetCanvasBoundingRect(canvas);
|
|
return {
|
|
x: localCoordinates.x - canvasRect.left,
|
|
y: localCoordinates.y - canvasRect.top,
|
|
};
|
|
}
|
|
|
|
const onCanvasClick: MouseEventHandler = event => {
|
|
if (didMouseMoveWhileDownRef.current) {
|
|
return;
|
|
}
|
|
|
|
interactor({
|
|
type: 'click',
|
|
payload: {
|
|
event,
|
|
location: localToCanvasCoordinates({x: event.x, y: event.y}),
|
|
},
|
|
});
|
|
};
|
|
|
|
const onCanvasDoubleClick: MouseEventHandler = event => {
|
|
if (didMouseMoveWhileDownRef.current) {
|
|
return;
|
|
}
|
|
|
|
interactor({
|
|
type: 'double-click',
|
|
payload: {
|
|
event,
|
|
location: localToCanvasCoordinates({x: event.x, y: event.y}),
|
|
},
|
|
});
|
|
};
|
|
|
|
const onCanvasMouseDown: MouseEventHandler = event => {
|
|
didMouseMoveWhileDownRef.current = false;
|
|
isMouseDownRef.current = true;
|
|
|
|
interactor({
|
|
type: 'mousedown',
|
|
payload: {
|
|
event,
|
|
location: localToCanvasCoordinates({x: event.x, y: event.y}),
|
|
},
|
|
});
|
|
};
|
|
|
|
const onDocumentMouseMove: MouseEventHandler = event => {
|
|
if (isMouseDownRef.current) {
|
|
didMouseMoveWhileDownRef.current = true;
|
|
}
|
|
|
|
interactor({
|
|
type: 'mousemove',
|
|
payload: {
|
|
event,
|
|
location: localToCanvasCoordinates({x: event.x, y: event.y}),
|
|
},
|
|
});
|
|
};
|
|
|
|
const onDocumentMouseUp: MouseEventHandler = event => {
|
|
isMouseDownRef.current = false;
|
|
|
|
interactor({
|
|
type: 'mouseup',
|
|
payload: {
|
|
event,
|
|
location: localToCanvasCoordinates({x: event.x, y: event.y}),
|
|
},
|
|
});
|
|
};
|
|
|
|
const onCanvasWheel: WheelEventHandler = event => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
const location = localToCanvasCoordinates({x: event.x, y: event.y});
|
|
const delta = normalizeWheel(event);
|
|
|
|
if (event.shiftKey) {
|
|
interactor({
|
|
type: 'wheel-shift',
|
|
payload: {event, location, delta},
|
|
});
|
|
} else if (event.ctrlKey) {
|
|
interactor({
|
|
type: 'wheel-control',
|
|
payload: {event, location, delta},
|
|
});
|
|
} else if (event.metaKey) {
|
|
interactor({
|
|
type: 'wheel-meta',
|
|
payload: {event, location, delta},
|
|
});
|
|
} else {
|
|
interactor({
|
|
type: 'wheel-plain',
|
|
payload: {event, location, delta},
|
|
});
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const ownerDocument = canvas.ownerDocument;
|
|
ownerDocument.addEventListener('mousemove', onDocumentMouseMove);
|
|
ownerDocument.addEventListener('mouseup', onDocumentMouseUp);
|
|
|
|
canvas.addEventListener('click', onCanvasClick);
|
|
canvas.addEventListener('dblclick', onCanvasDoubleClick);
|
|
canvas.addEventListener('mousedown', onCanvasMouseDown);
|
|
canvas.addEventListener('wheel', onCanvasWheel);
|
|
|
|
return () => {
|
|
ownerDocument.removeEventListener('mousemove', onDocumentMouseMove);
|
|
ownerDocument.removeEventListener('mouseup', onDocumentMouseUp);
|
|
|
|
canvas.removeEventListener('click', onCanvasClick);
|
|
canvas.removeEventListener('dblclick', onCanvasDoubleClick);
|
|
canvas.removeEventListener('mousedown', onCanvasMouseDown);
|
|
canvas.removeEventListener('wheel', onCanvasWheel);
|
|
};
|
|
}, [canvasRef, interactor]);
|
|
}
|