feat(canvas): 多角度贴节点、宫格切分菜单与多选虚线框跟随
把多角度改成贴在图片节点下方的 3D 编辑器并跟随拖动;宫格切分并入图片工具,自定义面板不关菜单;切分后子图按网格排开;多选虚线框随拖动预览平移。
This commit is contained in:
1 parent
9727656b2b
commit
8e82e67cb3
26 files changed
+1431
-153
No files matched your search
@@ -5,6 +5,7 @@ import { activeConnectionPath, canvasConnectionPath } from "@/components/canvas/
|
||||
import type { CanvasBatchConnectionPreview } from "@/lib/canvas/canvas-batch-connection";
|
||||
import { subscribeCanvasGraphicsViewportPreview, subscribeCanvasNodeDragPreview, subscribeCanvasSelectionPreview, type CanvasNodeDragPreview } from "@/lib/canvas/canvas-live-viewport";
|
||||
import { calculateCanvasPreviewTransform, sameCanvasViewport, shouldRebaseCanvasRaster } from "@/lib/canvas/canvas-leafer-viewport";
|
||||
import { offsetSelectedNodeBounds } from "@/lib/canvas/canvas-selection";
|
||||
import type { CanvasTheme } from "@/lib/canvas-theme";
|
||||
import type { CanvasDisplayConnection, CanvasNodeData, ConnectionHandle, Position, SelectionBox, ViewportTransform } from "@/types/canvas";
|
||||
|
||||
@@ -57,6 +58,7 @@ type OverlayScene = LeaferScene & {
|
||||
guides: Path;
|
||||
draft: Path;
|
||||
batchDrafts: Group;
|
||||
dragPreview: CanvasNodeDragPreview | null;
|
||||
};
|
||||
|
||||
export function CanvasLeaferGraphicsLayer(props: CanvasLeaferGraphicsLayerProps) {
|
||||
@@ -118,6 +120,8 @@ export function CanvasLeaferGraphicsLayer(props: CanvasLeaferGraphicsLayerProps)
|
||||
});
|
||||
const unsubscribeNodeDrag = subscribeCanvasNodeDragPreview(container, (preview) => {
|
||||
applyConnectionDragPreview(underlay, propsRef.current, preview);
|
||||
overlay.dragPreview = preview;
|
||||
syncLiveSelectionBounds(overlay, propsRef.current, viewportRef.current.k);
|
||||
});
|
||||
resize();
|
||||
|
||||
@@ -206,7 +210,7 @@ function createOverlayScene(host: HTMLDivElement): OverlayScene {
|
||||
world.add(draft);
|
||||
world.add(batchDrafts);
|
||||
leafer.add(world);
|
||||
return { leafer, world, host, selection, selectionBounds, guides, draft, batchDrafts };
|
||||
return { leafer, world, host, selection, selectionBounds, guides, draft, batchDrafts, dragPreview: null };
|
||||
}
|
||||
|
||||
function rebuildConnections(scene: UnderlayScene, props: CanvasLeaferGraphicsLayerProps) {
|
||||
@@ -323,7 +327,7 @@ function syncOverlayContent(scene: OverlayScene, props: CanvasLeaferGraphicsLaye
|
||||
const bounds = props.selectedNodeBounds;
|
||||
scene.selectionBounds.visible = Boolean(bounds && !selection);
|
||||
if (bounds && !selection) {
|
||||
syncSelectionBounds(scene.selectionBounds, bounds, viewportScale);
|
||||
syncSelectionBounds(scene.selectionBounds, liveSelectedNodeBounds(bounds, scene.dragPreview), viewportScale);
|
||||
scene.selectionBounds.stroke = props.theme.node.label;
|
||||
}
|
||||
|
||||
@@ -388,7 +392,7 @@ function syncViewport(viewport: ViewportTransform, width: number, height: number
|
||||
cornerRadius: 2 / scale,
|
||||
dashPattern: [4 / scale, 4 / scale],
|
||||
});
|
||||
if (props.selectedNodeBounds) syncSelectionBounds(overlay.selectionBounds, props.selectedNodeBounds, scale);
|
||||
if (props.selectedNodeBounds) syncSelectionBounds(overlay.selectionBounds, liveSelectedNodeBounds(props.selectedNodeBounds, overlay.dragPreview), scale);
|
||||
overlay.selectionBounds.set({
|
||||
strokeWidth: 1 / scale,
|
||||
cornerRadius: 2 / scale,
|
||||
@@ -438,6 +442,19 @@ function forceSceneRender(...scenes: LeaferScene[]) {
|
||||
for (const scene of scenes) scene.leafer.forceRender(undefined, true);
|
||||
}
|
||||
|
||||
function liveSelectedNodeBounds(bounds: NonNullable<NodeBounds>, preview: CanvasNodeDragPreview | null) {
|
||||
return offsetSelectedNodeBounds(bounds, preview);
|
||||
}
|
||||
|
||||
function syncLiveSelectionBounds(scene: OverlayScene, props: CanvasLeaferGraphicsLayerProps, viewportScale: number) {
|
||||
const bounds = props.selectedNodeBounds;
|
||||
const selection = props.selectionBox;
|
||||
scene.selectionBounds.visible = Boolean(bounds && !selection);
|
||||
if (!bounds || selection) return;
|
||||
syncSelectionBounds(scene.selectionBounds, liveSelectedNodeBounds(bounds, scene.dragPreview), viewportScale);
|
||||
scene.selectionBounds.stroke = props.theme.node.label;
|
||||
}
|
||||
|
||||
function syncSelectionBounds(rect: Rect, bounds: NonNullable<NodeBounds>, viewportScale: number) {
|
||||
const padding = 12 / Math.max(viewportScale, 0.05);
|
||||
rect.set({
|
||||
|
||||
Reference in new issue
Block a user