Я делаю редактор на реакт. Элементы холста генерируются с помощью html без канваса. Добавил функцию перемещения по холсту с помощью transform translate. Теперь не могу понять как добавить ограничение если пользователь докрутил до границы холста, сейчас он может бесконечно крутить.
Размеры экрана в котором находится холст может быть разным в зависимости от размера экрана. Размеры самого холста всегда 1920 на 1080 (почти всегда больше чем родительский див).
Вот код перемещения и мои попытки добавить ограничение + jsx разметка:
// 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);
};
}, []);
// ограничитель drag
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; // слева
const rectMinX = rect.right + 50; // справа
const rectMaxY = rect.top + 50; // сверху
const rectMinY = rect.bottom + 50; // вниз
console.log(rectMaxX, rectMinX, rectMaxY, rectMinY, x, y, canvasRect);
if (canvasRect.right < rectMinX) {
console.log('Вы вышли за пределы правого края');
}
if (canvasRect.left > rectMaxX) {
console.log('Вы вышли за пределы левого края');
}
if (canvasRect.top > rectMaxY) {
console.log('Вы вышли за пределы верхнего края', event);
}
if (canvasRect.bottom < rectMinY) {
console.log('Вы вышли за пределы нижнего края');
}
console.log(maxX, minX, maxY, minY);
return {
x: x,
y: y,
};
};
// трекпад
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>;
В функции clampTranslate я могу с помощью координат родительского дива и холста узнать пересек ли пользователь границу, но не могу понять как мне ограничить движение. Если добавлю return false, то перемещение заблокируется