I am making an editor on React. Canvas elements are generated using html. I added a function for moving around the canvas using transform translate. Now I can’t figure out how to add a limitation if the user has scrolled to the canvas border, now he can scroll endlessly.
The size of the screen in which the canvas is located can be different depending on the screen size. The size of the canvas itself is always 1920 by 1080 (almost always larger than the parent div).
Here is the code for moving and my attempts to add a limitation + jsx markup:
// drag
const [space, setSpace] = useState(false);
const [origin, setOrigin] = useState({ x: 0, y: 0 });
const [translate, setTranslate] = useState({ x: 0, y: 0 });
useEffect(() => {
const scrollDown = (event: KeyboardEvent): void => {
if (event.code === 'Space') {
setSpace(true);
}
};
const scrollUp = (event: KeyboardEvent): void => {
if (event.code === 'Space') {
setSpace(false);
}
};
window.addEventListener('keydown', scrollDown);
window.addEventListener('keyup', scrollUp);
return () => {
window.removeEventListener('keydown', scrollDown);
window.removeEventListener('keyup', scrollUp);
};
}, []);
const clampTranslate =
( x: number, y: number ):
{ x: number; y: number } | false => {
const wrapper = scrollWrapperRef.current;
const canvasWrapper = canvas.current;
if (!wrapper && !canvasWrapper) return { x, y };
const rect = wrapper.getBoundingClientRect();
const canvasRect = canvasWrapper.getBoundingClientRect();
const rectMaxX = rect.left + 50; // left
const rectMinX = rect.right + 50; // right
const rectMaxY = rect.top + 50; // top
const rectMinY = rect.bottom + 50; // bottom
console.log(rectMaxX, rectMinX, rectMaxY, rectMinY, x, y, canvasRect);
if (canvasRect.right < rectMinX) {
console.log('You have gone beyond the right edge');
}
if (canvasRect.left > rectMaxX) {
console.log('You have gone beyond the left edge');
}
if (canvasRect.top > rectMaxY) {
console.log('You have gone beyond the top edge.');
}
if (canvasRect.bottom < rectMinY) {
console.log('You have gone beyond the bottom edge');
}
console.log(maxX, minX, maxY, minY);
return {
x: x,
y: y,
};
};
// trackpad
const handleWheel = (event: React.WheelEvent): void => {
setIsDragging(true);
setTranslate((prev) => {
const next = { x: prev.x - event.deltaX, y: prev.y - event.deltaY };
return clampTranslate(next.x, next.y);
});
};
const handleMouseDown = (event: React.MouseEvent): void => {
if (!space) return;
setIsDragging(true);
setOrigin({ x: event.clientX - translate.x, y: event.clientY - translate.y });
event.preventDefault();
};
const handleMouseMove = (event: React.MouseEvent): void => {
if (!space) return;
if (!isDragging) return;
const newX = event.clientX - origin.x;
const newY = event.clientY - origin.y;
setTranslate(clampTranslate(newX, newY));
};
const handleMouseUp = (): void => {
setIsDragging(false);
};
<StyledEditorPageContent>
<StyledEditorWrapper
ref={scrollWrapperRef}
onWheel={handleWheel}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
>
<StyledEditorCanvasWrapper
key={artBoard?.id}
id={canvasId}
style={{
transform: `translate(${translate.x}px, ${translate.y}px)`,
}}
>
<StyledEditorCanvas
ref={canvas}
id={"canvas-content"}
isHorizontal={currentOrientationCanvas === orientationsIds.horizontal}
cursor={getCursorCanvas()}
onClick={handleCanvasClick}
>
{(draft || [])?.map((item, index) => {
return (
<EditorLayer
key={`${item.id}-${index}`}
{...item}
onClick={(): void => {
handleLayerClick(item.id);
}}
index={index}
disableDragging={isDragging}
/>
);
})}
</StyledEditorCanvas>
</StyledEditorCanvasWrapper>
</StyledEditorWrapper>
</StyledEditorPageContent>;
In the clampTranslate function I can use the coordinates of the parent div and canvas to find out if the user has crossed the border, but I can’t figure out how to restrict the movement. If I add return false, the movement will be blocked.