Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/frontend/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default tseslint.config(
"no-shadow": "off",
"import/no-absolute-path": "warn",
"import/no-unresolved": "warn",
"no-unused-vars": "off",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/src/components/space/SpaceView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import useDragNode from "@/hooks/useDragNode";
import useMoveNode from "@/hooks/useMoveNode";
import useSpaceSelection from "@/hooks/useSpaceSelection";
import useYjsSpace from "@/hooks/useYjsSpace";
import { useZoomSpace } from "@/hooks/useZoomSpace";
import { useZoomSpace } from "@/hooks/useZoomSpaceNew";

import PointerLayer from "../PointerLayer";
import GooeyNode from "./GooeyNode";
Expand Down
83 changes: 83 additions & 0 deletions packages/frontend/src/hooks/useZoomSpaceNew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useRef } from "react";

import Konva from "konva";
import { KonvaEventObject } from "konva/lib/Node";

import {
ctrlWheelZoomStrategy,
defaultMoveViewStrategy,
} from "@/utils/zoomStrategies";

interface UseZoomSpaceProps {
stageRef: React.RefObject<Konva.Stage>;
scaleBy?: number;
minScale?: number;
maxScale?: number;
}

export function useZoomSpace({
stageRef,
scaleBy = 1.018,
minScale = 0.5,
maxScale = 3,
}: UseZoomSpaceProps) {
const lastDistRef = useRef<number | null>(null);

const zoomSpace = (event: KonvaEventObject<WheelEvent>) => {
if (stageRef.current !== null) {
const stage = stageRef.current;

if (event.evt.ctrlKey) {
ctrlWheelZoomStrategy(event, stage, scaleBy, minScale, maxScale);
} else {
defaultMoveViewStrategy(event, stage);
}
}
};

const handleTouchMove = (event: KonvaEventObject<TouchEvent>) => {
if (stageRef.current !== null && event.evt.touches.length === 2) {
const stage = stageRef.current;
const touch1 = event.evt.touches[0];
const touch2 = event.evt.touches[1];

const dist = Math.sqrt(
(touch1.clientX - touch2.clientX) ** 2 +
(touch1.clientY - touch2.clientY) ** 2,
);

if (lastDistRef.current !== null) {
const oldScale = stage.scaleX();
const pointer = stage.getPointerPosition();

if (!pointer) return;

const mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
};

const scaleChange = lastDistRef.current / dist;
let newScale = oldScale * scaleChange;
newScale = Math.max(minScale, Math.min(maxScale, newScale));

if (newScale !== oldScale) {
const newPosition = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
stage.scale({ x: newScale, y: newScale });
stage.position(newPosition);
}
}

lastDistRef.current = dist;
}
};

const handleTouchEnd = () => {
lastDistRef.current = null;
};

return { zoomSpace, handleTouchMove, handleTouchEnd };
}
54 changes: 54 additions & 0 deletions packages/frontend/src/utils/zoomStrategies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import Konva from "konva";
import { KonvaEventObject } from "konva/lib/Node";

export type ZoomStrategy = (
event: KonvaEventObject<WheelEvent>,
stage: Konva.Stage,
scaleBy: number,
minScale: number,
maxScale: number,
) => void;

export const ctrlWheelZoomStrategy: ZoomStrategy = (
event,
stage,
scaleBy,
minScale,
maxScale,
) => {
event.evt.preventDefault();

const oldScale = stage.scaleX();
const pointer = stage.getPointerPosition();

if (!pointer) return;

const mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
};

const direction = event.evt.deltaY > 0 ? -1 : 1;
let newScale = direction > 0 ? oldScale * scaleBy : oldScale / scaleBy;
newScale = Math.max(minScale, Math.min(maxScale, newScale));

if (newScale === oldScale) return;

const newPosition = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
stage.scale({ x: newScale, y: newScale });
stage.position(newPosition);
};

export const defaultMoveViewStrategy = (
event: KonvaEventObject<WheelEvent>,
stage: Konva.Stage,
) => {
const currentScale = stage.scaleX();
stage.position({
x: stage.x() - event.evt.deltaX / currentScale,
y: stage.y() - event.evt.deltaY / currentScale,
});
};
Loading