feat(canvas): 框选 - 新增工具栏「框选」工具,空白处拖动框选后自动回到移动与选择 (#78)
Co-authored-by: sikongyue <3155157671@qq.com>
This commit is contained in:
@@ -218,6 +218,7 @@ export function CanvasTopBar({
|
||||
<Shortcut keys={["滚轮"]} value="缩放画布" />
|
||||
<Shortcut keys={["缩放滑杆"]} value="精确调整缩放" />
|
||||
<Shortcut keys={["Shift / Ctrl / Cmd + 左键拖动"]} value="框选多个节点" />
|
||||
<Shortcut keys={["工具栏「框选」", "左键拖动"]} value="框选多个节点,完成后自动回到「移动与选择」" />
|
||||
<Shortcut keys={["Shift / Ctrl / Cmd", "点击"]} value="追加选择节点" />
|
||||
<Shortcut keys={["Alt", "点击 / 框选"]} value="移除选择节点" />
|
||||
<Shortcut keys={["Ctrl / Cmd", "1 / 2 / 3"]} value="100% / 适应全部 / 适应选择" />
|
||||
|
||||
@@ -106,6 +106,7 @@ import {
|
||||
type StoryboardShotDuration,
|
||||
type CanvasWorkflowKind,
|
||||
type CanvasWorkspaceMode,
|
||||
type CanvasToolMode,
|
||||
type ContextMenuState,
|
||||
type Position,
|
||||
type ViewportTransform,
|
||||
@@ -204,6 +205,7 @@ function InfiniteCanvasPage() {
|
||||
const [isMiniMapOpen, setIsMiniMapOpen] = useState(false);
|
||||
const [backgroundMode, setBackgroundMode] = useState<CanvasBackgroundMode>("dots");
|
||||
const [showImageInfo, setShowImageInfo] = useState(false);
|
||||
const [canvasTool, setCanvasTool] = useState<CanvasToolMode>("move");
|
||||
const [mediaPerformanceMode, setMediaPerformanceMode] = useState<CanvasMediaPerformanceMode>(readCanvasMediaPerformanceMode);
|
||||
const [projectLoaded, setProjectLoaded] = useState(false);
|
||||
const [workspaceMode, setWorkspaceMode] = useState<CanvasWorkspaceMode>(readCanvasWorkspaceMode);
|
||||
@@ -700,6 +702,7 @@ function InfiniteCanvasPage() {
|
||||
onNodeInteractionStart: handleNodeInteractionStart,
|
||||
onNodeClick: handleSelectedNodeClick,
|
||||
onDeselect: handleCanvasDeselect,
|
||||
onSelectionBoxEnd: () => setCanvasTool((tool) => (tool === "box-select" ? "move" : tool)),
|
||||
});
|
||||
|
||||
const keepNodeToolbar = useCallback(
|
||||
@@ -1430,6 +1433,7 @@ function InfiniteCanvasPage() {
|
||||
onViewportChange={handleViewportChange}
|
||||
onViewportPreviewChange={handleViewportPreviewChange}
|
||||
onCanvasMouseDown={handleCanvasMouseDown}
|
||||
boxSelectEnabled={canvasTool === "box-select"}
|
||||
onCanvasDoubleClick={handleCanvasDoubleClick}
|
||||
onCanvasDeselect={deselectCanvas}
|
||||
onContextMenu={handleCanvasContextMenu}
|
||||
@@ -1568,6 +1572,8 @@ function InfiniteCanvasPage() {
|
||||
<CanvasToolbar
|
||||
selectedCount={selectedNodeIds.size}
|
||||
workspaceMode={workspaceMode}
|
||||
canvasTool={canvasTool}
|
||||
onToolChange={setCanvasTool}
|
||||
isProjectLinked={Boolean(currentProject?.projectId)}
|
||||
canUndo={historyState.canUndo}
|
||||
canRedo={historyState.canRedo}
|
||||
|
||||
@@ -20,6 +20,7 @@ type UseCanvasSelectionControllerOptions = {
|
||||
onNodeInteractionStart: (selectionModifier: boolean) => void;
|
||||
onNodeClick: (node: CanvasNodeData) => void;
|
||||
onDeselect: () => void;
|
||||
onSelectionBoxEnd?: () => void;
|
||||
};
|
||||
|
||||
type DragState = {
|
||||
@@ -57,6 +58,7 @@ export function useCanvasSelectionController({
|
||||
onNodeInteractionStart,
|
||||
onNodeClick,
|
||||
onDeselect,
|
||||
onSelectionBoxEnd,
|
||||
}: UseCanvasSelectionControllerOptions) {
|
||||
const dragFrameRef = useRef<number | null>(null);
|
||||
const pendingNodeDragRef = useRef<Position>({ x: 0, y: 0 });
|
||||
@@ -296,7 +298,8 @@ export function useCanvasSelectionController({
|
||||
const wasSelection = selectionActivatedRef.current;
|
||||
cancelSelectionBox();
|
||||
if (hadPendingSelection && !wasSelection) deselectCanvas();
|
||||
}, [cancelSelectionBox, deselectCanvas]);
|
||||
if (hadPendingSelection) onSelectionBoxEnd?.();
|
||||
}, [cancelSelectionBox, deselectCanvas, onSelectionBoxEnd]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseUp = (event: MouseEvent) => {
|
||||
|
||||
Reference in New Issue
Block a user