Skip to content

Commit

Permalink
```plaintext
Browse files Browse the repository at this point in the history
feat(canvas): 实现resize handle功能和界面更新

- 添加resizeBounds函数以计算调整大小的边界
- 在Canvas组件中实现resizeSelectedLayer mutation
- SelectionBox组件现在可以显示调整大小的句柄
- 调整SelectionBox的onPointerDown处理以支持调整大小操作
```
  • Loading branch information
YuniqueUnic committed Aug 25, 2024
1 parent 77c9eae commit bd5474c
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 24 deletions.
53 changes: 49 additions & 4 deletions app/board/[boardId]/_components/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ import {
Camera,
CanvasMode,
CanvasState,
LayerType
LayerType,
XYWH,
Side
} from "@/types/canvas";

import { Info } from "./info";
Expand All @@ -32,7 +34,7 @@ import { SelectionBox } from "./selection-box";
import { CursorsPresence } from "./cursors-presence";


import { connectionIdToColor, pointerEventToCanvasEvent } from "@/lib/utils";
import { connectionIdToColor, pointerEventToCanvasEvent, resizeBounds } from "@/lib/utils";


// import { useQuery } from "convex/react";
Expand All @@ -45,6 +47,8 @@ const MAX_LAYERS = 100;
interface CanvasProps {
boardId: string;
}


export const Canvas = ({ boardId }: CanvasProps) => {
const layerIds = useStorage((root) => root.layerIds);

Expand Down Expand Up @@ -98,6 +102,43 @@ export const Canvas = ({ boardId }: CanvasProps) => {
setCanvasState({ mode: CanvasMode.None });
}, [lastUsedColor]);

const resizeSelectedLayer = useMutation((
{ storage, self },
point: Point
) => {
if (canvasState.mode !== CanvasMode.Resizing) {
return;
}

const bounds = resizeBounds(canvasState.initialBounds, canvasState.corner, point);

const liveLayers = storage.get("layers");
const layer = liveLayers.get(self.presence.selection[0]);

if (layer) {
layer.update(bounds);
}

}, [canvasState]);

const onResizeHandlePointerDown = useCallback((
corner: Side,
initialBounds: XYWH,) => {

console.log({
corner,
initialBounds
});

history.pause();

setCanvasState({
mode: CanvasMode.Resizing,
initialBounds,
corner
});

}, [history]);

const onWheel = useCallback((e: React.WheelEvent) => {

Expand All @@ -121,9 +162,13 @@ export const Canvas = ({ boardId }: CanvasProps) => {

const current = pointerEventToCanvasEvent(e, camera);

if (canvasState.mode === CanvasMode.Resizing) {
resizeSelectedLayer(current);
}

setMyPresence({ cursor: current });

}, []);
}, [camera, canvasState, resizeSelectedLayer]);

const onPointerLeave = useMutation((
{ setMyPresence },
Expand Down Expand Up @@ -236,7 +281,7 @@ export const Canvas = ({ boardId }: CanvasProps) => {
onLayerPointerDown={(e) => { return onLayerPointerDown(e, layerId); }}
selectionColor={layerIdToColorSelection[layerId]} />;
})}
<SelectionBox onResizeHandlePointerDown={() => { }} />
<SelectionBox onResizeHandlePointerDown={onResizeHandlePointerDown} />
<CursorsPresence />
</g>
</svg>
Expand Down
37 changes: 22 additions & 15 deletions app/board/[boardId]/_components/selection-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const SelectionBox = memo(({
});

const isShowingHandles = useStorage((root) => {
soleLayerId && root.layers.get("layers")?.type !== LayerType.Path;
return soleLayerId && root.layers.get("layers")?.type !== LayerType.Path;
});

const bounds = useSelectionBounds();
Expand All @@ -37,7 +37,7 @@ export const SelectionBox = memo(({

return <>
<rect
className="fill-transparent stroke-blue-500 stroke-1 pointer-events-none"
className="fill-transparent stroke-blue-500 stroke-2 pointer-events-none"
style={{
transform: `translate(${bounds.x}px, ${bounds.y}px)`,
}}
Expand All @@ -48,7 +48,7 @@ export const SelectionBox = memo(({
/>
{isShowingHandles && (
<>
{/* Top-Right-Corner */}
{/* Top-Left-Corner */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -63,7 +63,8 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler

onResizeHandlePointerDown(Side.Top + Side.Left, bounds);
}}
/>
{/* Top-Center */}
Expand All @@ -81,10 +82,11 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Top, bounds);

}}
/>
{/* Top-Left-Corner */}
{/* Top-Right-Corner */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -99,10 +101,11 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Top + Side.Right, bounds);

}}
/>
{/* Left-Center */}
{/* Right-Center */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -117,10 +120,11 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Right, bounds);

}}
/>
{/* Bottom-Left-Corner */}
{/* Bottom-Right-Corner */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -135,7 +139,8 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Bottom + Side.Right, bounds);

}}
/>
{/* Bottom-Center */}
Expand All @@ -153,10 +158,11 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Bottom, bounds);

}}
/>
{/* Bottom-Right-Corner */}
{/* Bottom-Left-Corner */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -171,10 +177,10 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
// TODO:Add Resize handler
onResizeHandlePointerDown(Side.Bottom + Side.Left, bounds);
}}
/>
{/* Right-Corner */}
{/* Left-Corner */}
<rect
className="fill-white stroke-1 stroke-blue-500"
x={0}
Expand All @@ -189,6 +195,7 @@ export const SelectionBox = memo(({
}}
onPointerDown={(e) => {
e.stopPropagation();
onResizeHandlePointerDown(Side.Left, bounds);
// TODO:Add Resize handler
}}
/>
Expand Down
43 changes: 38 additions & 5 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react";

import { Camera, Color } from "@/types/canvas";
import { Camera, Color, Point, Side, XYWH } from "@/types/canvas";

import { twMerge } from "tailwind-merge";
import { type ClassValue, clsx } from "clsx";
Expand All @@ -22,6 +22,12 @@ export function connectionIdToColor(connectionId: number) {
return COLORS[connectionId % COLORS.length];
}

export function colorToCss(color: Color) {
return `#${color.r.toString(16).padStart(2, "0")
}${color.g.toString(16).padStart(2, "0")
}${color.b.toString(16).padStart(2, "0")}`;
}

export function pointerEventToCanvasEvent(
e: React.PointerEvent,
camera: Camera
Expand All @@ -32,8 +38,35 @@ export function pointerEventToCanvasEvent(
};
}

export function colorToCss(color: Color) {
return `#${color.r.toString(16).padStart(2, "0")
}${color.g.toString(16).padStart(2, "0")
}${color.b.toString(16).padStart(2, "0")}`;

export function resizeBounds(bounds: XYWH, corner: Side, point: Point) {
const result = {
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height
};

if ((corner & Side.Left) === Side.Left) {
result.x = Math.min(point.x, bounds.x + bounds.width);
result.width = Math.abs(bounds.x + bounds.width - point.x);
}

if ((corner & Side.Right) === Side.Right) {
result.x = Math.min(point.x, bounds.x);
result.width = Math.abs(point.x - bounds.x);
}

if ((corner & Side.Top) === Side.Top) {
result.y = Math.min(point.y, bounds.y + bounds.height);
result.height = Math.abs(bounds.y + bounds.height - point.y);
}

if ((corner & Side.Bottom) === Side.Bottom) {
result.y = Math.min(point.y, bounds.y);
result.height = Math.abs(point.y - bounds.y);
}

return result;

}

0 comments on commit bd5474c

Please sign in to comment.