perf: Remove useComponentSize from image and video node render

This commit is contained in:
Tom Moor
2024-06-01 11:13:03 -04:00
parent 009458e435
commit f2e9c0ab23
6 changed files with 87 additions and 46 deletions

View File

@@ -4,39 +4,56 @@ type DragDirection = "left" | "right";
type SizeState = { width: number; height?: number };
/**
* Hook for resizing an element by dragging its sides.
*/
type ReturnValue = {
/** Event handler for pointer down event on the resize handle. */
handlePointerDown: (
dragging: DragDirection
) => (event: React.PointerEvent<HTMLDivElement>) => void;
/** Handler to set the new size of the element from outside. */
setSize: React.Dispatch<React.SetStateAction<SizeState>>;
/** Whether the element is currently being resized. */
dragging: boolean;
/** The current width of the element. */
width: number;
/** The current height of the element. */
height?: number;
};
type Props = {
type Params = {
/** Callback triggered when the image is resized */
onChangeSize?: undefined | ((size: SizeState) => void);
/** The initial width of the element. */
width: number;
/** The initial height of the element. */
height: number;
/** The natural width of the element. */
naturalWidth: number;
/** The natural height of the element. */
naturalHeight: number;
minWidth: number;
maxWidth: number;
gridWidth: number;
/** The percentage of the grid to snap the element to. */
gridSnap: 5;
/** A reference to the element being resized. */
ref: React.RefObject<HTMLDivElement>;
};
export default function useDragResize(props: Props): ReturnValue {
export default function useDragResize(props: Params): ReturnValue {
const [size, setSize] = React.useState<SizeState>({
width: props.width,
height: props.height,
});
const [maxWidth, setMaxWidth] = React.useState(Infinity);
const [offset, setOffset] = React.useState(0);
const [sizeAtDragStart, setSizeAtDragStart] = React.useState(size);
const [dragging, setDragging] = React.useState<DragDirection>();
const isResizable = !!props.onChangeSize;
const constrainWidth = (width: number) =>
Math.round(Math.min(props.maxWidth, Math.max(width, props.minWidth)));
const constrainWidth = (width: number, max: number) => {
const minWidth = Math.min(props.naturalWidth, (props.gridSnap / 100) * max);
return Math.round(Math.min(max, Math.max(width, minWidth)));
};
const handlePointerMove = (event: PointerEvent) => {
event.preventDefault();
@@ -48,10 +65,10 @@ export default function useDragResize(props: Props): ReturnValue {
diff = event.pageX - offset;
}
const gridWidth = (props.gridSnap / 100) * maxWidth;
const newWidth = sizeAtDragStart.width + diff * 2;
const widthOnGrid =
Math.round(newWidth / props.gridWidth) * props.gridWidth;
const constrainedWidth = constrainWidth(widthOnGrid);
const widthOnGrid = Math.round(newWidth / gridWidth) * gridWidth;
const constrainedWidth = constrainWidth(widthOnGrid, maxWidth);
const aspectRatio = props.naturalHeight / props.naturalWidth;
setSize({
@@ -88,8 +105,18 @@ export default function useDragResize(props: Props): ReturnValue {
(event: React.PointerEvent<HTMLDivElement>) => {
event.preventDefault();
event.stopPropagation();
// Calculate constraints once at the start of dragging as it's relatively expensive operation
const max = props.ref.current
? parseInt(
getComputedStyle(props.ref.current).getPropertyValue(
"--document-width"
)
)
: Infinity;
setMaxWidth(max);
setSizeAtDragStart({
width: constrainWidth(size.width),
width: constrainWidth(size.width, max),
height: size.height,
});
setOffset(event.pageX);