@8btc/whiteboard 0.0.12 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +0 -4
- package/dist/index.d.ts +19 -1
- package/dist/index.js +192 -86
- package/dist/index.umd.js +192 -90
- package/package.json +1 -1
package/dist/index.css
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -73,6 +73,14 @@ export declare class CanvasApi extends CanvasCore {
|
|
|
73
73
|
* @returns 被删除的节点数据数组
|
|
74
74
|
*/
|
|
75
75
|
deleteNodes(nodeIds: string[]): INode[];
|
|
76
|
+
/**
|
|
77
|
+
* 将节点移动到最上层
|
|
78
|
+
*/
|
|
79
|
+
moveNodesToTop(nodeIds: string[]): void;
|
|
80
|
+
/**
|
|
81
|
+
* 将节点移动到最下层
|
|
82
|
+
*/
|
|
83
|
+
moveNodesToBottom(nodeIds: string[]): void;
|
|
76
84
|
/**
|
|
77
85
|
* 滚动到内容区域
|
|
78
86
|
* - 如果提供了 nodeIds,将指定的节点居中显示
|
|
@@ -157,6 +165,8 @@ declare class CanvasCore extends CanvasState_2 {
|
|
|
157
165
|
/* Excluded from this release type: updateDraftNode */
|
|
158
166
|
/* Excluded from this release type: finalizeDraftNode */
|
|
159
167
|
/* Excluded from this release type: selectNode */
|
|
168
|
+
/* Excluded from this release type: deleteNodes */
|
|
169
|
+
/* Excluded from this release type: deleteSelectedNodes */
|
|
160
170
|
/**
|
|
161
171
|
* 销毁 canvas
|
|
162
172
|
*/
|
|
@@ -367,7 +377,13 @@ declare interface IViewport {
|
|
|
367
377
|
scale: number;
|
|
368
378
|
}
|
|
369
379
|
|
|
370
|
-
export declare const
|
|
380
|
+
export declare const NODE_NAMES: {
|
|
381
|
+
nodeRoot: string;
|
|
382
|
+
selectable: string;
|
|
383
|
+
rect: string;
|
|
384
|
+
image: string;
|
|
385
|
+
imageMarker: string;
|
|
386
|
+
};
|
|
371
387
|
|
|
372
388
|
export declare type NodeType = "rectangle" | "image" | "image-marker";
|
|
373
389
|
|
|
@@ -394,10 +410,12 @@ declare type StateEvents = {
|
|
|
394
410
|
"state:redo": ICoreState;
|
|
395
411
|
"state:reset": ICoreState;
|
|
396
412
|
"viewport:change": ICoreState["viewport"];
|
|
413
|
+
"viewport:scale:change": number;
|
|
397
414
|
"transformer:positionChange": TransformerPosition | null;
|
|
398
415
|
"toolType:change": ICoreState["toolType"];
|
|
399
416
|
"nodes:created": ICoreState["nodes"];
|
|
400
417
|
"nodes:deleted": INode[];
|
|
418
|
+
"nodes:selected": string[];
|
|
401
419
|
};
|
|
402
420
|
|
|
403
421
|
export declare type ToolType = "select" | "hand" | "rectangle" | "image-marker";
|
package/dist/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
|
|
|
9
9
|
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
10
10
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
11
11
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
12
|
-
var _core, _stage, _viewport, _handleWheel, _handlePointerDown, _handlePointerMove, _handlePointerUp, _handleDragStart, _handleDragMove, _handleDragEnd, _CanvasStage_instances, setupEventListeners_fn, _core2, _transformer, _handleTransformStart, _handleTransform, _handleTransformEnd, _handleDragStart2, _handleDragMove2, _handleDragEnd2, _CanvasTransformer_instances, setupEventListeners_fn2, _toolTypeChangeHandler, _RectNode_instances, setupEventHandlers_fn, _ImageNode_instances, loadImage_fn, _toolTypeChangeHandler2, setupEventHandlers_fn2, syncImageMarkers_fn, syncImageMarkersToState_fn, _rect, _markerGroup, _circle, _text, _ImageMarkerNode_instances, setupEventHandlers_fn3, _canvasStage, _mainLayer, _canvasTransformer, _draftNode;
|
|
12
|
+
var _core, _stage, _viewport, _handleWheel, _handlePointerDown, _handlePointerMove, _handlePointerUp, _handleDragStart, _handleDragMove, _handleDragEnd, _CanvasStage_instances, setupEventListeners_fn, _core2, _transformer, _handleTransformStart, _handleTransform, _handleTransformEnd, _handleDragStart2, _handleDragMove2, _handleDragEnd2, _CanvasTransformer_instances, setupEventListeners_fn2, _toolTypeChangeHandler, _RectNode_instances, setupEventHandlers_fn, _ImageNode_instances, loadImage_fn, _toolTypeChangeHandler2, setupEventHandlers_fn2, syncImageMarkers_fn, syncImageMarkersToState_fn, _rect, _markerGroup, _circle, _text, _handleViewportChange, _handleNodesSelected, _ImageMarkerNode_instances, updateMarkerScale_fn, setupEventHandlers_fn3, _canvasStage, _mainLayer, _canvasTransformer, _draftNode, _container, _handleKeyDown, _CanvasCore_instances, setupKeyboardEvents_fn;
|
|
13
13
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
14
14
|
import { useState, useEffect, useRef } from "react";
|
|
15
15
|
import Konva from "konva";
|
|
@@ -509,7 +509,13 @@ class CanvasState {
|
|
|
509
509
|
this._emitter.all.clear();
|
|
510
510
|
}
|
|
511
511
|
}
|
|
512
|
-
const
|
|
512
|
+
const NODE_NAMES = {
|
|
513
|
+
nodeRoot: "nodeRoot_intrinsic",
|
|
514
|
+
selectable: "selectable_intrinsic",
|
|
515
|
+
rect: "rect_intrinsic",
|
|
516
|
+
image: "image_intrinsic",
|
|
517
|
+
imageMarker: "image_marker_intrinsic"
|
|
518
|
+
};
|
|
513
519
|
const RECT = {
|
|
514
520
|
CORNER_RADIUS: 6,
|
|
515
521
|
MIN_SIZE: 10
|
|
@@ -573,7 +579,7 @@ class RectNode extends BaseCanvasNode {
|
|
|
573
579
|
width,
|
|
574
580
|
height,
|
|
575
581
|
cornerRadius: RECT.CORNER_RADIUS,
|
|
576
|
-
name:
|
|
582
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.selectable} ${NODE_NAMES.rect}`,
|
|
577
583
|
draggable: true,
|
|
578
584
|
stroke: "black",
|
|
579
585
|
strokeWidth: 2
|
|
@@ -692,7 +698,7 @@ class ImageNode extends BaseCanvasNode {
|
|
|
692
698
|
id: this.node.id,
|
|
693
699
|
x: this.node.props.x,
|
|
694
700
|
y: this.node.props.y,
|
|
695
|
-
name:
|
|
701
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.selectable} ${NODE_NAMES.image}`,
|
|
696
702
|
draggable: true,
|
|
697
703
|
image: placeholder
|
|
698
704
|
});
|
|
@@ -889,12 +895,23 @@ class ImageMarkerNode extends BaseCanvasNode {
|
|
|
889
895
|
__privateAdd(this, _markerGroup);
|
|
890
896
|
__privateAdd(this, _circle);
|
|
891
897
|
__privateAdd(this, _text);
|
|
898
|
+
__privateAdd(this, _handleViewportChange);
|
|
899
|
+
__privateAdd(this, _handleNodesSelected);
|
|
892
900
|
const group = this.getElement();
|
|
893
901
|
__privateSet(this, _rect, group.findOne(".rect"));
|
|
894
902
|
__privateSet(this, _markerGroup, group.findOne(".marker-group"));
|
|
895
903
|
__privateSet(this, _circle, __privateGet(this, _markerGroup).findOne("Circle"));
|
|
896
904
|
__privateSet(this, _text, __privateGet(this, _markerGroup).findOne("Text"));
|
|
897
905
|
__privateMethod(this, _ImageMarkerNode_instances, setupEventHandlers_fn3).call(this);
|
|
906
|
+
__privateSet(this, _handleViewportChange, () => {
|
|
907
|
+
__privateMethod(this, _ImageMarkerNode_instances, updateMarkerScale_fn).call(this);
|
|
908
|
+
});
|
|
909
|
+
this.core.on("viewport:scale:change", __privateGet(this, _handleViewportChange));
|
|
910
|
+
__privateSet(this, _handleNodesSelected, (selectedIds) => {
|
|
911
|
+
const isSelected = selectedIds.includes(this.node.id);
|
|
912
|
+
this.setFocusState(isSelected);
|
|
913
|
+
});
|
|
914
|
+
this.core.on("nodes:selected", __privateGet(this, _handleNodesSelected));
|
|
898
915
|
}
|
|
899
916
|
createElement() {
|
|
900
917
|
const width = Math.max(
|
|
@@ -907,23 +924,27 @@ class ImageMarkerNode extends BaseCanvasNode {
|
|
|
907
924
|
);
|
|
908
925
|
const group = new Konva.Group({
|
|
909
926
|
id: this.node.id,
|
|
910
|
-
name:
|
|
927
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.imageMarker} ${this.node.meta.parent}`,
|
|
911
928
|
x: this.node.props.x,
|
|
912
929
|
y: this.node.props.y,
|
|
913
930
|
width,
|
|
914
931
|
height
|
|
915
932
|
});
|
|
933
|
+
const stageScale = this.core.getStageScale();
|
|
934
|
+
const rectStrokeWidth = 2 / stageScale;
|
|
935
|
+
const rectDash = [5 / stageScale, 5 / stageScale];
|
|
936
|
+
const rectCornerRadius = RECT.CORNER_RADIUS / stageScale;
|
|
916
937
|
const rect = new Konva.Rect({
|
|
917
938
|
name: "rect",
|
|
918
939
|
x: 0,
|
|
919
940
|
y: 0,
|
|
920
941
|
width,
|
|
921
942
|
height,
|
|
922
|
-
stroke:
|
|
923
|
-
strokeWidth:
|
|
924
|
-
dash:
|
|
943
|
+
stroke: "#3B82F6",
|
|
944
|
+
strokeWidth: rectStrokeWidth,
|
|
945
|
+
dash: rectDash,
|
|
925
946
|
fill: "transparent",
|
|
926
|
-
cornerRadius:
|
|
947
|
+
cornerRadius: rectCornerRadius,
|
|
927
948
|
listening: false
|
|
928
949
|
});
|
|
929
950
|
const markerGroup = new Konva.Group({
|
|
@@ -932,13 +953,14 @@ class ImageMarkerNode extends BaseCanvasNode {
|
|
|
932
953
|
y: height,
|
|
933
954
|
visible: this.isDraft ? false : true
|
|
934
955
|
});
|
|
935
|
-
const
|
|
936
|
-
const
|
|
956
|
+
const radius = 14 / stageScale;
|
|
957
|
+
const strokeWidth = 3 / stageScale;
|
|
958
|
+
const fontSize = 16 / stageScale;
|
|
937
959
|
const circle = new Konva.Circle({
|
|
938
960
|
radius,
|
|
939
|
-
fill: "
|
|
940
|
-
stroke: "
|
|
941
|
-
strokeWidth
|
|
961
|
+
fill: "#3B82F6",
|
|
962
|
+
stroke: "white",
|
|
963
|
+
strokeWidth
|
|
942
964
|
});
|
|
943
965
|
const text = new Konva.Text({
|
|
944
966
|
x: -radius,
|
|
@@ -948,7 +970,7 @@ class ImageMarkerNode extends BaseCanvasNode {
|
|
|
948
970
|
text: String(this.node.meta.markerNumber || ""),
|
|
949
971
|
align: "center",
|
|
950
972
|
verticalAlign: "middle",
|
|
951
|
-
fontSize
|
|
973
|
+
fontSize,
|
|
952
974
|
fill: "white"
|
|
953
975
|
});
|
|
954
976
|
markerGroup.add(circle);
|
|
@@ -1011,13 +1033,16 @@ class ImageMarkerNode extends BaseCanvasNode {
|
|
|
1011
1033
|
* 销毁
|
|
1012
1034
|
*/
|
|
1013
1035
|
destroy() {
|
|
1036
|
+
this.core.off("viewport:scale:change", __privateGet(this, _handleViewportChange));
|
|
1037
|
+
this.core.off("nodes:selected", __privateGet(this, _handleNodesSelected));
|
|
1014
1038
|
this.element.destroy();
|
|
1015
1039
|
}
|
|
1016
1040
|
/**
|
|
1017
1041
|
* 更新焦点状态(hover 或 selected)
|
|
1018
1042
|
*/
|
|
1019
1043
|
setFocusState(isFocus) {
|
|
1020
|
-
const
|
|
1044
|
+
const stageScale = this.core.getStageScale();
|
|
1045
|
+
const strokeWidth = isFocus ? 4 / stageScale : 3 / stageScale;
|
|
1021
1046
|
const scale = isFocus ? 1.2 : 1;
|
|
1022
1047
|
__privateGet(this, _rect).strokeWidth(strokeWidth);
|
|
1023
1048
|
__privateGet(this, _circle).strokeWidth(strokeWidth);
|
|
@@ -1029,7 +1054,31 @@ _rect = new WeakMap();
|
|
|
1029
1054
|
_markerGroup = new WeakMap();
|
|
1030
1055
|
_circle = new WeakMap();
|
|
1031
1056
|
_text = new WeakMap();
|
|
1057
|
+
_handleViewportChange = new WeakMap();
|
|
1058
|
+
_handleNodesSelected = new WeakMap();
|
|
1032
1059
|
_ImageMarkerNode_instances = new WeakSet();
|
|
1060
|
+
/**
|
|
1061
|
+
* 更新标记点的缩放以保持视觉大小不变
|
|
1062
|
+
*/
|
|
1063
|
+
updateMarkerScale_fn = function() {
|
|
1064
|
+
const stageScale = this.core.getStageScale();
|
|
1065
|
+
const radius = 14 / stageScale;
|
|
1066
|
+
const strokeWidth = 3 / stageScale;
|
|
1067
|
+
const fontSize = 16 / stageScale;
|
|
1068
|
+
const rectStrokeWidth = 3 / stageScale;
|
|
1069
|
+
const rectDash = [5 / stageScale, 5 / stageScale];
|
|
1070
|
+
const rectCornerRadius = RECT.CORNER_RADIUS / stageScale;
|
|
1071
|
+
__privateGet(this, _rect).strokeWidth(rectStrokeWidth);
|
|
1072
|
+
__privateGet(this, _rect).dash(rectDash);
|
|
1073
|
+
__privateGet(this, _rect).cornerRadius(rectCornerRadius);
|
|
1074
|
+
__privateGet(this, _circle).radius(radius);
|
|
1075
|
+
__privateGet(this, _circle).strokeWidth(strokeWidth);
|
|
1076
|
+
__privateGet(this, _text).x(-radius);
|
|
1077
|
+
__privateGet(this, _text).y(-radius);
|
|
1078
|
+
__privateGet(this, _text).width(radius * 2);
|
|
1079
|
+
__privateGet(this, _text).height(radius * 2);
|
|
1080
|
+
__privateGet(this, _text).fontSize(fontSize);
|
|
1081
|
+
};
|
|
1033
1082
|
/**
|
|
1034
1083
|
* 设置事件处理器
|
|
1035
1084
|
*/
|
|
@@ -1091,8 +1140,7 @@ const createNodeByType = (type, position, style, meta) => {
|
|
|
1091
1140
|
...baseNode,
|
|
1092
1141
|
style: {
|
|
1093
1142
|
...baseNode.style,
|
|
1094
|
-
color: "#
|
|
1095
|
-
line: "dashed"
|
|
1143
|
+
color: "#3B82F6"
|
|
1096
1144
|
}
|
|
1097
1145
|
};
|
|
1098
1146
|
}
|
|
@@ -1161,10 +1209,19 @@ class CanvasCore extends CanvasState {
|
|
|
1161
1209
|
toolType: "select",
|
|
1162
1210
|
nodes: []
|
|
1163
1211
|
});
|
|
1212
|
+
__privateAdd(this, _CanvasCore_instances);
|
|
1164
1213
|
__privateAdd(this, _canvasStage);
|
|
1165
1214
|
__privateAdd(this, _mainLayer);
|
|
1166
1215
|
__privateAdd(this, _canvasTransformer);
|
|
1167
1216
|
__privateAdd(this, _draftNode, null);
|
|
1217
|
+
__privateAdd(this, _container);
|
|
1218
|
+
__privateAdd(this, _handleKeyDown, (e) => {
|
|
1219
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
1220
|
+
e.preventDefault();
|
|
1221
|
+
this.deleteSelectedNodes();
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
__privateSet(this, _container, el);
|
|
1168
1225
|
__privateSet(this, _canvasStage, new CanvasStage(this, {
|
|
1169
1226
|
container: el,
|
|
1170
1227
|
width: el.clientWidth,
|
|
@@ -1177,6 +1234,7 @@ class CanvasCore extends CanvasState {
|
|
|
1177
1234
|
__privateGet(this, _canvasStage).getStage().add(__privateGet(this, _mainLayer));
|
|
1178
1235
|
__privateGet(this, _mainLayer).add(__privateGet(this, _canvasTransformer).getTransformer());
|
|
1179
1236
|
this.updateViewport(this.getState().viewport, false);
|
|
1237
|
+
__privateMethod(this, _CanvasCore_instances, setupKeyboardEvents_fn).call(this);
|
|
1180
1238
|
}
|
|
1181
1239
|
/**
|
|
1182
1240
|
* 获取 CanvasStage 实例
|
|
@@ -1272,8 +1330,9 @@ class CanvasCore extends CanvasState {
|
|
|
1272
1330
|
*/
|
|
1273
1331
|
updateViewport(viewport, addToHistory = false) {
|
|
1274
1332
|
__privateGet(this, _canvasStage).setViewport(viewport);
|
|
1333
|
+
const oldViewport = this.getState().viewport;
|
|
1275
1334
|
const newViewport = {
|
|
1276
|
-
...
|
|
1335
|
+
...oldViewport,
|
|
1277
1336
|
...viewport
|
|
1278
1337
|
};
|
|
1279
1338
|
this._updateState(
|
|
@@ -1283,6 +1342,9 @@ class CanvasCore extends CanvasState {
|
|
|
1283
1342
|
addToHistory
|
|
1284
1343
|
);
|
|
1285
1344
|
this.emit("viewport:change", newViewport);
|
|
1345
|
+
if (oldViewport.scale !== newViewport.scale) {
|
|
1346
|
+
this.emit("viewport:scale:change", newViewport.scale);
|
|
1347
|
+
}
|
|
1286
1348
|
__privateGet(this, _canvasTransformer).emitPositionChange();
|
|
1287
1349
|
}
|
|
1288
1350
|
createNodes(nodes) {
|
|
@@ -1330,7 +1392,7 @@ class CanvasCore extends CanvasState {
|
|
|
1330
1392
|
visible: true
|
|
1331
1393
|
},
|
|
1332
1394
|
style: {
|
|
1333
|
-
color: "#
|
|
1395
|
+
color: "#3B82F6",
|
|
1334
1396
|
line: "dashed",
|
|
1335
1397
|
size: "medium",
|
|
1336
1398
|
opacity: 1
|
|
@@ -1478,41 +1540,79 @@ class CanvasCore extends CanvasState {
|
|
|
1478
1540
|
*/
|
|
1479
1541
|
selectNode(nodeId, multiSelect = false) {
|
|
1480
1542
|
const curSelectedNodeIds = this.getState().selectedNodeIds ?? [];
|
|
1481
|
-
|
|
1543
|
+
let selectedNodeIds = [];
|
|
1544
|
+
if (nodeId) {
|
|
1545
|
+
if (multiSelect && curSelectedNodeIds.length > 0) {
|
|
1546
|
+
selectedNodeIds = [...curSelectedNodeIds, nodeId];
|
|
1547
|
+
} else {
|
|
1548
|
+
selectedNodeIds = [nodeId];
|
|
1549
|
+
}
|
|
1550
|
+
} else if (curSelectedNodeIds.length === 0) {
|
|
1482
1551
|
return;
|
|
1483
1552
|
}
|
|
1484
|
-
if (
|
|
1553
|
+
if (selectedNodeIds.length === 0) {
|
|
1485
1554
|
__privateGet(this, _canvasTransformer).clearNodes();
|
|
1486
|
-
this._updateState(
|
|
1487
|
-
{
|
|
1488
|
-
selectedNodeIds: []
|
|
1489
|
-
},
|
|
1490
|
-
false
|
|
1491
|
-
);
|
|
1492
|
-
return;
|
|
1493
|
-
}
|
|
1494
|
-
let selectedNodeIds = [];
|
|
1495
|
-
if (multiSelect) {
|
|
1496
|
-
selectedNodeIds = curSelectedNodeIds.length ? [...curSelectedNodeIds, nodeId] : [nodeId];
|
|
1497
1555
|
} else {
|
|
1498
|
-
|
|
1556
|
+
const nodes = this.getStage().find(`.${NODE_NAMES.selectable}`).filter((node) => selectedNodeIds.includes(node.id()));
|
|
1557
|
+
__privateGet(this, _canvasTransformer).setNodes(nodes);
|
|
1499
1558
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1559
|
+
this._updateState({ selectedNodeIds }, false);
|
|
1560
|
+
this.emit("nodes:selected", selectedNodeIds);
|
|
1561
|
+
}
|
|
1562
|
+
/**
|
|
1563
|
+
* 删除指定的节点(内部使用)
|
|
1564
|
+
* 如果删除的是 image 节点,会同步删除所有关联的 image-marker
|
|
1565
|
+
* @internal 仅供内部使用,外部请使用 CanvasApi
|
|
1566
|
+
* @param nodeIds - 要删除的节点 ID 数组
|
|
1567
|
+
* @returns 被删除的节点数据数组
|
|
1568
|
+
*/
|
|
1569
|
+
deleteNodes(nodeIds) {
|
|
1570
|
+
if (nodeIds.length === 0) return [];
|
|
1571
|
+
const nodes = this.getState().nodes || [];
|
|
1572
|
+
const idsToDelete = new Set(nodeIds);
|
|
1573
|
+
nodeIds.forEach((id) => {
|
|
1574
|
+
const node = nodes.find((n) => n.id === id);
|
|
1575
|
+
if (node?.type === "image") {
|
|
1576
|
+
nodes.forEach((n) => {
|
|
1577
|
+
if (n.type === "image-marker" && n.meta.parent === id) {
|
|
1578
|
+
idsToDelete.add(n.id);
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1503
1582
|
});
|
|
1504
|
-
|
|
1583
|
+
const deletedNodes = nodes.filter((n) => idsToDelete.has(n.id));
|
|
1584
|
+
idsToDelete.forEach((id) => {
|
|
1585
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
1586
|
+
if (shape) {
|
|
1587
|
+
shape.destroy();
|
|
1588
|
+
}
|
|
1589
|
+
});
|
|
1590
|
+
const newNodes = nodes.filter((n) => !idsToDelete.has(n.id));
|
|
1591
|
+
__privateGet(this, _canvasTransformer).clearNodes();
|
|
1505
1592
|
this._updateState(
|
|
1506
1593
|
{
|
|
1507
|
-
|
|
1594
|
+
nodes: newNodes,
|
|
1595
|
+
selectedNodeIds: []
|
|
1508
1596
|
},
|
|
1509
|
-
|
|
1597
|
+
true
|
|
1510
1598
|
);
|
|
1599
|
+
this.emit("nodes:deleted", deletedNodes);
|
|
1600
|
+
return deletedNodes;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* 删除选中的节点(内部使用)
|
|
1604
|
+
* @internal 仅供内部使用,外部请使用 CanvasApi
|
|
1605
|
+
*/
|
|
1606
|
+
deleteSelectedNodes() {
|
|
1607
|
+
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
1608
|
+
if (selectedNodeIds.length === 0) return;
|
|
1609
|
+
this.deleteNodes(selectedNodeIds);
|
|
1511
1610
|
}
|
|
1512
1611
|
/**
|
|
1513
1612
|
* 销毁 canvas
|
|
1514
1613
|
*/
|
|
1515
1614
|
dispose() {
|
|
1615
|
+
__privateGet(this, _container).removeEventListener("keydown", __privateGet(this, _handleKeyDown));
|
|
1516
1616
|
this.getCanvasTransformer().destroy();
|
|
1517
1617
|
this.getMainLayer().destroy();
|
|
1518
1618
|
this.getCanvasStage().destroy();
|
|
@@ -1568,6 +1668,16 @@ _canvasStage = new WeakMap();
|
|
|
1568
1668
|
_mainLayer = new WeakMap();
|
|
1569
1669
|
_canvasTransformer = new WeakMap();
|
|
1570
1670
|
_draftNode = new WeakMap();
|
|
1671
|
+
_container = new WeakMap();
|
|
1672
|
+
_handleKeyDown = new WeakMap();
|
|
1673
|
+
_CanvasCore_instances = new WeakSet();
|
|
1674
|
+
/**
|
|
1675
|
+
* 设置键盘事件监听
|
|
1676
|
+
*/
|
|
1677
|
+
setupKeyboardEvents_fn = function() {
|
|
1678
|
+
__privateGet(this, _container).tabIndex = 0;
|
|
1679
|
+
__privateGet(this, _container).addEventListener("keydown", __privateGet(this, _handleKeyDown));
|
|
1680
|
+
};
|
|
1571
1681
|
class CanvasApi extends CanvasCore {
|
|
1572
1682
|
/**
|
|
1573
1683
|
* 获取所有可用的工具类型
|
|
@@ -1656,29 +1766,32 @@ class CanvasApi extends CanvasCore {
|
|
|
1656
1766
|
* @returns DataURL 格式的图片数据,如果没有选区则返回 null
|
|
1657
1767
|
*/
|
|
1658
1768
|
exportSelectionAsImage(options) {
|
|
1659
|
-
const
|
|
1660
|
-
if (
|
|
1769
|
+
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
1770
|
+
if (selectedNodeIds.length === 0) {
|
|
1661
1771
|
console.warn("No selection to export");
|
|
1662
1772
|
return null;
|
|
1663
1773
|
}
|
|
1664
|
-
const stage = this.getStage();
|
|
1665
1774
|
const padding = options?.padding ?? 0;
|
|
1666
|
-
const
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1775
|
+
const tempGroup = new Konva.Group();
|
|
1776
|
+
selectedNodeIds.forEach((id) => {
|
|
1777
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
1778
|
+
if (shape) {
|
|
1779
|
+
const clone = shape.clone();
|
|
1780
|
+
tempGroup.add(clone);
|
|
1781
|
+
}
|
|
1782
|
+
});
|
|
1783
|
+
const box = tempGroup.getClientRect();
|
|
1784
|
+
const dataURL = tempGroup.toDataURL({
|
|
1785
|
+
x: box.x - padding,
|
|
1786
|
+
y: box.y - padding,
|
|
1787
|
+
width: box.width + padding * 2,
|
|
1788
|
+
height: box.height + padding * 2,
|
|
1789
|
+
pixelRatio: options?.pixelRatio ?? 2,
|
|
1790
|
+
mimeType: options?.mimeType ?? "image/png",
|
|
1791
|
+
quality: options?.quality ?? 1
|
|
1792
|
+
});
|
|
1793
|
+
tempGroup.destroy();
|
|
1794
|
+
return dataURL;
|
|
1682
1795
|
}
|
|
1683
1796
|
/**
|
|
1684
1797
|
* 导出带有标注的图片节点为图片
|
|
@@ -1722,7 +1835,6 @@ class CanvasApi extends CanvasCore {
|
|
|
1722
1835
|
*/
|
|
1723
1836
|
deleteSelectedNodes() {
|
|
1724
1837
|
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
1725
|
-
if (selectedNodeIds.length === 0) return;
|
|
1726
1838
|
this.deleteNodes(selectedNodeIds);
|
|
1727
1839
|
}
|
|
1728
1840
|
/**
|
|
@@ -1732,37 +1844,31 @@ class CanvasApi extends CanvasCore {
|
|
|
1732
1844
|
* @returns 被删除的节点数据数组
|
|
1733
1845
|
*/
|
|
1734
1846
|
deleteNodes(nodeIds) {
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1847
|
+
return super.deleteNodes(nodeIds);
|
|
1848
|
+
}
|
|
1849
|
+
/**
|
|
1850
|
+
* 将节点移动到最上层
|
|
1851
|
+
*/
|
|
1852
|
+
moveNodesToTop(nodeIds) {
|
|
1853
|
+
if (nodeIds.length === 0) return;
|
|
1738
1854
|
nodeIds.forEach((id) => {
|
|
1739
|
-
const
|
|
1740
|
-
if (
|
|
1741
|
-
|
|
1742
|
-
if (n.type === "image-marker" && n.meta.parent === id) {
|
|
1743
|
-
idsToDelete.add(n.id);
|
|
1744
|
-
}
|
|
1745
|
-
});
|
|
1855
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
1856
|
+
if (shape) {
|
|
1857
|
+
shape.moveToTop();
|
|
1746
1858
|
}
|
|
1747
1859
|
});
|
|
1748
|
-
|
|
1749
|
-
|
|
1860
|
+
}
|
|
1861
|
+
/**
|
|
1862
|
+
* 将节点移动到最下层
|
|
1863
|
+
*/
|
|
1864
|
+
moveNodesToBottom(nodeIds) {
|
|
1865
|
+
if (nodeIds.length === 0) return;
|
|
1866
|
+
nodeIds.forEach((id) => {
|
|
1750
1867
|
const shape = this.getStage().findOne(`#${id}`);
|
|
1751
1868
|
if (shape) {
|
|
1752
|
-
shape.
|
|
1869
|
+
shape.moveToBottom();
|
|
1753
1870
|
}
|
|
1754
1871
|
});
|
|
1755
|
-
const newNodes = nodes.filter((n) => !idsToDelete.has(n.id));
|
|
1756
|
-
this.getCanvasTransformer().clearNodes();
|
|
1757
|
-
this._updateState(
|
|
1758
|
-
{
|
|
1759
|
-
nodes: newNodes,
|
|
1760
|
-
selectedNodeIds: []
|
|
1761
|
-
},
|
|
1762
|
-
true
|
|
1763
|
-
);
|
|
1764
|
-
this.emit("nodes:deleted", deletedNodes);
|
|
1765
|
-
return deletedNodes;
|
|
1766
1872
|
}
|
|
1767
1873
|
/**
|
|
1768
1874
|
* 滚动到内容区域
|
|
@@ -1790,7 +1896,7 @@ class CanvasApi extends CanvasCore {
|
|
|
1790
1896
|
const hasSelection = !hasTargetIds && selectedNodeIds.length > 0;
|
|
1791
1897
|
const idsToShow = hasTargetIds ? targetNodeIds : hasSelection ? selectedNodeIds : null;
|
|
1792
1898
|
mainLayer.children.forEach((child) => {
|
|
1793
|
-
if (child.visible() && child.getClassName() !== "Transformer" && child.hasName(
|
|
1899
|
+
if (child.visible() && child.getClassName() !== "Transformer" && child.hasName(NODE_NAMES.selectable)) {
|
|
1794
1900
|
if (idsToShow) {
|
|
1795
1901
|
const id = child.id();
|
|
1796
1902
|
if (!idsToShow.includes(id)) return;
|
|
@@ -2108,6 +2214,6 @@ function PureCanvas({ setApi }) {
|
|
|
2108
2214
|
}
|
|
2109
2215
|
export {
|
|
2110
2216
|
CanvasApi,
|
|
2111
|
-
|
|
2217
|
+
NODE_NAMES,
|
|
2112
2218
|
PureCanvas
|
|
2113
2219
|
};
|
package/dist/index.umd.js
CHANGED
|
@@ -13,7 +13,7 @@ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot
|
|
|
13
13
|
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
14
14
|
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
15
15
|
|
|
16
|
-
var _core, _stage, _viewport, _handleWheel, _handlePointerDown, _handlePointerMove, _handlePointerUp, _handleDragStart, _handleDragMove, _handleDragEnd, _CanvasStage_instances, setupEventListeners_fn, _core2, _transformer, _handleTransformStart, _handleTransform, _handleTransformEnd, _handleDragStart2, _handleDragMove2, _handleDragEnd2, _CanvasTransformer_instances, setupEventListeners_fn2, _toolTypeChangeHandler, _RectNode_instances, setupEventHandlers_fn, _ImageNode_instances, loadImage_fn, _toolTypeChangeHandler2, setupEventHandlers_fn2, syncImageMarkers_fn, syncImageMarkersToState_fn, _rect, _markerGroup, _circle, _text, _ImageMarkerNode_instances, setupEventHandlers_fn3, _canvasStage, _mainLayer, _canvasTransformer, _draftNode;
|
|
16
|
+
var _core, _stage, _viewport, _handleWheel, _handlePointerDown, _handlePointerMove, _handlePointerUp, _handleDragStart, _handleDragMove, _handleDragEnd, _CanvasStage_instances, setupEventListeners_fn, _core2, _transformer, _handleTransformStart, _handleTransform, _handleTransformEnd, _handleDragStart2, _handleDragMove2, _handleDragEnd2, _CanvasTransformer_instances, setupEventListeners_fn2, _toolTypeChangeHandler, _RectNode_instances, setupEventHandlers_fn, _ImageNode_instances, loadImage_fn, _toolTypeChangeHandler2, setupEventHandlers_fn2, syncImageMarkers_fn, syncImageMarkersToState_fn, _rect, _markerGroup, _circle, _text, _handleViewportChange, _handleNodesSelected, _ImageMarkerNode_instances, updateMarkerScale_fn, setupEventHandlers_fn3, _canvasStage, _mainLayer, _canvasTransformer, _draftNode, _container, _handleKeyDown, _CanvasCore_instances, setupKeyboardEvents_fn;
|
|
17
17
|
var __vite_style__ = document.createElement("style");
|
|
18
18
|
__vite_style__.textContent = `/*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */
|
|
19
19
|
@layer properties {
|
|
@@ -389,10 +389,6 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
389
389
|
position: relative;
|
|
390
390
|
}
|
|
391
391
|
|
|
392
|
-
.static {
|
|
393
|
-
position: static;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
392
|
.top-0 {
|
|
397
393
|
top: calc(var(--spacing) * 0);
|
|
398
394
|
}
|
|
@@ -1830,7 +1826,13 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
1830
1826
|
this._emitter.all.clear();
|
|
1831
1827
|
}
|
|
1832
1828
|
}
|
|
1833
|
-
const
|
|
1829
|
+
const NODE_NAMES = {
|
|
1830
|
+
nodeRoot: "nodeRoot_intrinsic",
|
|
1831
|
+
selectable: "selectable_intrinsic",
|
|
1832
|
+
rect: "rect_intrinsic",
|
|
1833
|
+
image: "image_intrinsic",
|
|
1834
|
+
imageMarker: "image_marker_intrinsic"
|
|
1835
|
+
};
|
|
1834
1836
|
const RECT = {
|
|
1835
1837
|
CORNER_RADIUS: 6,
|
|
1836
1838
|
MIN_SIZE: 10
|
|
@@ -1894,7 +1896,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
1894
1896
|
width,
|
|
1895
1897
|
height,
|
|
1896
1898
|
cornerRadius: RECT.CORNER_RADIUS,
|
|
1897
|
-
name:
|
|
1899
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.selectable} ${NODE_NAMES.rect}`,
|
|
1898
1900
|
draggable: true,
|
|
1899
1901
|
stroke: "black",
|
|
1900
1902
|
strokeWidth: 2
|
|
@@ -2013,7 +2015,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2013
2015
|
id: this.node.id,
|
|
2014
2016
|
x: this.node.props.x,
|
|
2015
2017
|
y: this.node.props.y,
|
|
2016
|
-
name:
|
|
2018
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.selectable} ${NODE_NAMES.image}`,
|
|
2017
2019
|
draggable: true,
|
|
2018
2020
|
image: placeholder
|
|
2019
2021
|
});
|
|
@@ -2210,12 +2212,23 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2210
2212
|
__privateAdd(this, _markerGroup);
|
|
2211
2213
|
__privateAdd(this, _circle);
|
|
2212
2214
|
__privateAdd(this, _text);
|
|
2215
|
+
__privateAdd(this, _handleViewportChange);
|
|
2216
|
+
__privateAdd(this, _handleNodesSelected);
|
|
2213
2217
|
const group = this.getElement();
|
|
2214
2218
|
__privateSet(this, _rect, group.findOne(".rect"));
|
|
2215
2219
|
__privateSet(this, _markerGroup, group.findOne(".marker-group"));
|
|
2216
2220
|
__privateSet(this, _circle, __privateGet(this, _markerGroup).findOne("Circle"));
|
|
2217
2221
|
__privateSet(this, _text, __privateGet(this, _markerGroup).findOne("Text"));
|
|
2218
2222
|
__privateMethod(this, _ImageMarkerNode_instances, setupEventHandlers_fn3).call(this);
|
|
2223
|
+
__privateSet(this, _handleViewportChange, () => {
|
|
2224
|
+
__privateMethod(this, _ImageMarkerNode_instances, updateMarkerScale_fn).call(this);
|
|
2225
|
+
});
|
|
2226
|
+
this.core.on("viewport:scale:change", __privateGet(this, _handleViewportChange));
|
|
2227
|
+
__privateSet(this, _handleNodesSelected, (selectedIds) => {
|
|
2228
|
+
const isSelected = selectedIds.includes(this.node.id);
|
|
2229
|
+
this.setFocusState(isSelected);
|
|
2230
|
+
});
|
|
2231
|
+
this.core.on("nodes:selected", __privateGet(this, _handleNodesSelected));
|
|
2219
2232
|
}
|
|
2220
2233
|
createElement() {
|
|
2221
2234
|
const width = Math.max(
|
|
@@ -2228,23 +2241,27 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2228
2241
|
);
|
|
2229
2242
|
const group = new Konva.Group({
|
|
2230
2243
|
id: this.node.id,
|
|
2231
|
-
name:
|
|
2244
|
+
name: `${NODE_NAMES.nodeRoot} ${NODE_NAMES.imageMarker} ${this.node.meta.parent}`,
|
|
2232
2245
|
x: this.node.props.x,
|
|
2233
2246
|
y: this.node.props.y,
|
|
2234
2247
|
width,
|
|
2235
2248
|
height
|
|
2236
2249
|
});
|
|
2250
|
+
const stageScale = this.core.getStageScale();
|
|
2251
|
+
const rectStrokeWidth = 2 / stageScale;
|
|
2252
|
+
const rectDash = [5 / stageScale, 5 / stageScale];
|
|
2253
|
+
const rectCornerRadius = RECT.CORNER_RADIUS / stageScale;
|
|
2237
2254
|
const rect = new Konva.Rect({
|
|
2238
2255
|
name: "rect",
|
|
2239
2256
|
x: 0,
|
|
2240
2257
|
y: 0,
|
|
2241
2258
|
width,
|
|
2242
2259
|
height,
|
|
2243
|
-
stroke:
|
|
2244
|
-
strokeWidth:
|
|
2245
|
-
dash:
|
|
2260
|
+
stroke: "#3B82F6",
|
|
2261
|
+
strokeWidth: rectStrokeWidth,
|
|
2262
|
+
dash: rectDash,
|
|
2246
2263
|
fill: "transparent",
|
|
2247
|
-
cornerRadius:
|
|
2264
|
+
cornerRadius: rectCornerRadius,
|
|
2248
2265
|
listening: false
|
|
2249
2266
|
});
|
|
2250
2267
|
const markerGroup = new Konva.Group({
|
|
@@ -2253,13 +2270,14 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2253
2270
|
y: height,
|
|
2254
2271
|
visible: this.isDraft ? false : true
|
|
2255
2272
|
});
|
|
2256
|
-
const
|
|
2257
|
-
const
|
|
2273
|
+
const radius = 14 / stageScale;
|
|
2274
|
+
const strokeWidth = 3 / stageScale;
|
|
2275
|
+
const fontSize = 16 / stageScale;
|
|
2258
2276
|
const circle = new Konva.Circle({
|
|
2259
2277
|
radius,
|
|
2260
|
-
fill: "
|
|
2261
|
-
stroke: "
|
|
2262
|
-
strokeWidth
|
|
2278
|
+
fill: "#3B82F6",
|
|
2279
|
+
stroke: "white",
|
|
2280
|
+
strokeWidth
|
|
2263
2281
|
});
|
|
2264
2282
|
const text = new Konva.Text({
|
|
2265
2283
|
x: -radius,
|
|
@@ -2269,7 +2287,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2269
2287
|
text: String(this.node.meta.markerNumber || ""),
|
|
2270
2288
|
align: "center",
|
|
2271
2289
|
verticalAlign: "middle",
|
|
2272
|
-
fontSize
|
|
2290
|
+
fontSize,
|
|
2273
2291
|
fill: "white"
|
|
2274
2292
|
});
|
|
2275
2293
|
markerGroup.add(circle);
|
|
@@ -2332,13 +2350,16 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2332
2350
|
* 销毁
|
|
2333
2351
|
*/
|
|
2334
2352
|
destroy() {
|
|
2353
|
+
this.core.off("viewport:scale:change", __privateGet(this, _handleViewportChange));
|
|
2354
|
+
this.core.off("nodes:selected", __privateGet(this, _handleNodesSelected));
|
|
2335
2355
|
this.element.destroy();
|
|
2336
2356
|
}
|
|
2337
2357
|
/**
|
|
2338
2358
|
* 更新焦点状态(hover 或 selected)
|
|
2339
2359
|
*/
|
|
2340
2360
|
setFocusState(isFocus) {
|
|
2341
|
-
const
|
|
2361
|
+
const stageScale = this.core.getStageScale();
|
|
2362
|
+
const strokeWidth = isFocus ? 4 / stageScale : 3 / stageScale;
|
|
2342
2363
|
const scale = isFocus ? 1.2 : 1;
|
|
2343
2364
|
__privateGet(this, _rect).strokeWidth(strokeWidth);
|
|
2344
2365
|
__privateGet(this, _circle).strokeWidth(strokeWidth);
|
|
@@ -2350,7 +2371,31 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2350
2371
|
_markerGroup = new WeakMap();
|
|
2351
2372
|
_circle = new WeakMap();
|
|
2352
2373
|
_text = new WeakMap();
|
|
2374
|
+
_handleViewportChange = new WeakMap();
|
|
2375
|
+
_handleNodesSelected = new WeakMap();
|
|
2353
2376
|
_ImageMarkerNode_instances = new WeakSet();
|
|
2377
|
+
/**
|
|
2378
|
+
* 更新标记点的缩放以保持视觉大小不变
|
|
2379
|
+
*/
|
|
2380
|
+
updateMarkerScale_fn = function() {
|
|
2381
|
+
const stageScale = this.core.getStageScale();
|
|
2382
|
+
const radius = 14 / stageScale;
|
|
2383
|
+
const strokeWidth = 3 / stageScale;
|
|
2384
|
+
const fontSize = 16 / stageScale;
|
|
2385
|
+
const rectStrokeWidth = 3 / stageScale;
|
|
2386
|
+
const rectDash = [5 / stageScale, 5 / stageScale];
|
|
2387
|
+
const rectCornerRadius = RECT.CORNER_RADIUS / stageScale;
|
|
2388
|
+
__privateGet(this, _rect).strokeWidth(rectStrokeWidth);
|
|
2389
|
+
__privateGet(this, _rect).dash(rectDash);
|
|
2390
|
+
__privateGet(this, _rect).cornerRadius(rectCornerRadius);
|
|
2391
|
+
__privateGet(this, _circle).radius(radius);
|
|
2392
|
+
__privateGet(this, _circle).strokeWidth(strokeWidth);
|
|
2393
|
+
__privateGet(this, _text).x(-radius);
|
|
2394
|
+
__privateGet(this, _text).y(-radius);
|
|
2395
|
+
__privateGet(this, _text).width(radius * 2);
|
|
2396
|
+
__privateGet(this, _text).height(radius * 2);
|
|
2397
|
+
__privateGet(this, _text).fontSize(fontSize);
|
|
2398
|
+
};
|
|
2354
2399
|
/**
|
|
2355
2400
|
* 设置事件处理器
|
|
2356
2401
|
*/
|
|
@@ -2412,8 +2457,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2412
2457
|
...baseNode,
|
|
2413
2458
|
style: {
|
|
2414
2459
|
...baseNode.style,
|
|
2415
|
-
color: "#
|
|
2416
|
-
line: "dashed"
|
|
2460
|
+
color: "#3B82F6"
|
|
2417
2461
|
}
|
|
2418
2462
|
};
|
|
2419
2463
|
}
|
|
@@ -2482,10 +2526,19 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2482
2526
|
toolType: "select",
|
|
2483
2527
|
nodes: []
|
|
2484
2528
|
});
|
|
2529
|
+
__privateAdd(this, _CanvasCore_instances);
|
|
2485
2530
|
__privateAdd(this, _canvasStage);
|
|
2486
2531
|
__privateAdd(this, _mainLayer);
|
|
2487
2532
|
__privateAdd(this, _canvasTransformer);
|
|
2488
2533
|
__privateAdd(this, _draftNode, null);
|
|
2534
|
+
__privateAdd(this, _container);
|
|
2535
|
+
__privateAdd(this, _handleKeyDown, (e) => {
|
|
2536
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
2537
|
+
e.preventDefault();
|
|
2538
|
+
this.deleteSelectedNodes();
|
|
2539
|
+
}
|
|
2540
|
+
});
|
|
2541
|
+
__privateSet(this, _container, el);
|
|
2489
2542
|
__privateSet(this, _canvasStage, new CanvasStage(this, {
|
|
2490
2543
|
container: el,
|
|
2491
2544
|
width: el.clientWidth,
|
|
@@ -2498,6 +2551,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2498
2551
|
__privateGet(this, _canvasStage).getStage().add(__privateGet(this, _mainLayer));
|
|
2499
2552
|
__privateGet(this, _mainLayer).add(__privateGet(this, _canvasTransformer).getTransformer());
|
|
2500
2553
|
this.updateViewport(this.getState().viewport, false);
|
|
2554
|
+
__privateMethod(this, _CanvasCore_instances, setupKeyboardEvents_fn).call(this);
|
|
2501
2555
|
}
|
|
2502
2556
|
/**
|
|
2503
2557
|
* 获取 CanvasStage 实例
|
|
@@ -2593,8 +2647,9 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2593
2647
|
*/
|
|
2594
2648
|
updateViewport(viewport, addToHistory = false) {
|
|
2595
2649
|
__privateGet(this, _canvasStage).setViewport(viewport);
|
|
2650
|
+
const oldViewport = this.getState().viewport;
|
|
2596
2651
|
const newViewport = {
|
|
2597
|
-
...
|
|
2652
|
+
...oldViewport,
|
|
2598
2653
|
...viewport
|
|
2599
2654
|
};
|
|
2600
2655
|
this._updateState(
|
|
@@ -2604,6 +2659,9 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2604
2659
|
addToHistory
|
|
2605
2660
|
);
|
|
2606
2661
|
this.emit("viewport:change", newViewport);
|
|
2662
|
+
if (oldViewport.scale !== newViewport.scale) {
|
|
2663
|
+
this.emit("viewport:scale:change", newViewport.scale);
|
|
2664
|
+
}
|
|
2607
2665
|
__privateGet(this, _canvasTransformer).emitPositionChange();
|
|
2608
2666
|
}
|
|
2609
2667
|
createNodes(nodes) {
|
|
@@ -2651,7 +2709,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2651
2709
|
visible: true
|
|
2652
2710
|
},
|
|
2653
2711
|
style: {
|
|
2654
|
-
color: "#
|
|
2712
|
+
color: "#3B82F6",
|
|
2655
2713
|
line: "dashed",
|
|
2656
2714
|
size: "medium",
|
|
2657
2715
|
opacity: 1
|
|
@@ -2799,41 +2857,79 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2799
2857
|
*/
|
|
2800
2858
|
selectNode(nodeId, multiSelect = false) {
|
|
2801
2859
|
const curSelectedNodeIds = this.getState().selectedNodeIds ?? [];
|
|
2802
|
-
|
|
2860
|
+
let selectedNodeIds = [];
|
|
2861
|
+
if (nodeId) {
|
|
2862
|
+
if (multiSelect && curSelectedNodeIds.length > 0) {
|
|
2863
|
+
selectedNodeIds = [...curSelectedNodeIds, nodeId];
|
|
2864
|
+
} else {
|
|
2865
|
+
selectedNodeIds = [nodeId];
|
|
2866
|
+
}
|
|
2867
|
+
} else if (curSelectedNodeIds.length === 0) {
|
|
2803
2868
|
return;
|
|
2804
2869
|
}
|
|
2805
|
-
if (
|
|
2870
|
+
if (selectedNodeIds.length === 0) {
|
|
2806
2871
|
__privateGet(this, _canvasTransformer).clearNodes();
|
|
2807
|
-
this._updateState(
|
|
2808
|
-
{
|
|
2809
|
-
selectedNodeIds: []
|
|
2810
|
-
},
|
|
2811
|
-
false
|
|
2812
|
-
);
|
|
2813
|
-
return;
|
|
2814
|
-
}
|
|
2815
|
-
let selectedNodeIds = [];
|
|
2816
|
-
if (multiSelect) {
|
|
2817
|
-
selectedNodeIds = curSelectedNodeIds.length ? [...curSelectedNodeIds, nodeId] : [nodeId];
|
|
2818
2872
|
} else {
|
|
2819
|
-
|
|
2873
|
+
const nodes = this.getStage().find(`.${NODE_NAMES.selectable}`).filter((node) => selectedNodeIds.includes(node.id()));
|
|
2874
|
+
__privateGet(this, _canvasTransformer).setNodes(nodes);
|
|
2820
2875
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2876
|
+
this._updateState({ selectedNodeIds }, false);
|
|
2877
|
+
this.emit("nodes:selected", selectedNodeIds);
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* 删除指定的节点(内部使用)
|
|
2881
|
+
* 如果删除的是 image 节点,会同步删除所有关联的 image-marker
|
|
2882
|
+
* @internal 仅供内部使用,外部请使用 CanvasApi
|
|
2883
|
+
* @param nodeIds - 要删除的节点 ID 数组
|
|
2884
|
+
* @returns 被删除的节点数据数组
|
|
2885
|
+
*/
|
|
2886
|
+
deleteNodes(nodeIds) {
|
|
2887
|
+
if (nodeIds.length === 0) return [];
|
|
2888
|
+
const nodes = this.getState().nodes || [];
|
|
2889
|
+
const idsToDelete = new Set(nodeIds);
|
|
2890
|
+
nodeIds.forEach((id) => {
|
|
2891
|
+
const node = nodes.find((n) => n.id === id);
|
|
2892
|
+
if (node?.type === "image") {
|
|
2893
|
+
nodes.forEach((n) => {
|
|
2894
|
+
if (n.type === "image-marker" && n.meta.parent === id) {
|
|
2895
|
+
idsToDelete.add(n.id);
|
|
2896
|
+
}
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2824
2899
|
});
|
|
2825
|
-
|
|
2900
|
+
const deletedNodes = nodes.filter((n) => idsToDelete.has(n.id));
|
|
2901
|
+
idsToDelete.forEach((id) => {
|
|
2902
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
2903
|
+
if (shape) {
|
|
2904
|
+
shape.destroy();
|
|
2905
|
+
}
|
|
2906
|
+
});
|
|
2907
|
+
const newNodes = nodes.filter((n) => !idsToDelete.has(n.id));
|
|
2908
|
+
__privateGet(this, _canvasTransformer).clearNodes();
|
|
2826
2909
|
this._updateState(
|
|
2827
2910
|
{
|
|
2828
|
-
|
|
2911
|
+
nodes: newNodes,
|
|
2912
|
+
selectedNodeIds: []
|
|
2829
2913
|
},
|
|
2830
|
-
|
|
2914
|
+
true
|
|
2831
2915
|
);
|
|
2916
|
+
this.emit("nodes:deleted", deletedNodes);
|
|
2917
|
+
return deletedNodes;
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* 删除选中的节点(内部使用)
|
|
2921
|
+
* @internal 仅供内部使用,外部请使用 CanvasApi
|
|
2922
|
+
*/
|
|
2923
|
+
deleteSelectedNodes() {
|
|
2924
|
+
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
2925
|
+
if (selectedNodeIds.length === 0) return;
|
|
2926
|
+
this.deleteNodes(selectedNodeIds);
|
|
2832
2927
|
}
|
|
2833
2928
|
/**
|
|
2834
2929
|
* 销毁 canvas
|
|
2835
2930
|
*/
|
|
2836
2931
|
dispose() {
|
|
2932
|
+
__privateGet(this, _container).removeEventListener("keydown", __privateGet(this, _handleKeyDown));
|
|
2837
2933
|
this.getCanvasTransformer().destroy();
|
|
2838
2934
|
this.getMainLayer().destroy();
|
|
2839
2935
|
this.getCanvasStage().destroy();
|
|
@@ -2889,6 +2985,16 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2889
2985
|
_mainLayer = new WeakMap();
|
|
2890
2986
|
_canvasTransformer = new WeakMap();
|
|
2891
2987
|
_draftNode = new WeakMap();
|
|
2988
|
+
_container = new WeakMap();
|
|
2989
|
+
_handleKeyDown = new WeakMap();
|
|
2990
|
+
_CanvasCore_instances = new WeakSet();
|
|
2991
|
+
/**
|
|
2992
|
+
* 设置键盘事件监听
|
|
2993
|
+
*/
|
|
2994
|
+
setupKeyboardEvents_fn = function() {
|
|
2995
|
+
__privateGet(this, _container).tabIndex = 0;
|
|
2996
|
+
__privateGet(this, _container).addEventListener("keydown", __privateGet(this, _handleKeyDown));
|
|
2997
|
+
};
|
|
2892
2998
|
class CanvasApi extends CanvasCore {
|
|
2893
2999
|
/**
|
|
2894
3000
|
* 获取所有可用的工具类型
|
|
@@ -2977,29 +3083,32 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
2977
3083
|
* @returns DataURL 格式的图片数据,如果没有选区则返回 null
|
|
2978
3084
|
*/
|
|
2979
3085
|
exportSelectionAsImage(options) {
|
|
2980
|
-
const
|
|
2981
|
-
if (
|
|
3086
|
+
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
3087
|
+
if (selectedNodeIds.length === 0) {
|
|
2982
3088
|
console.warn("No selection to export");
|
|
2983
3089
|
return null;
|
|
2984
3090
|
}
|
|
2985
|
-
const stage = this.getStage();
|
|
2986
3091
|
const padding = options?.padding ?? 0;
|
|
2987
|
-
const
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3092
|
+
const tempGroup = new Konva.Group();
|
|
3093
|
+
selectedNodeIds.forEach((id) => {
|
|
3094
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
3095
|
+
if (shape) {
|
|
3096
|
+
const clone = shape.clone();
|
|
3097
|
+
tempGroup.add(clone);
|
|
3098
|
+
}
|
|
3099
|
+
});
|
|
3100
|
+
const box = tempGroup.getClientRect();
|
|
3101
|
+
const dataURL = tempGroup.toDataURL({
|
|
3102
|
+
x: box.x - padding,
|
|
3103
|
+
y: box.y - padding,
|
|
3104
|
+
width: box.width + padding * 2,
|
|
3105
|
+
height: box.height + padding * 2,
|
|
3106
|
+
pixelRatio: options?.pixelRatio ?? 2,
|
|
3107
|
+
mimeType: options?.mimeType ?? "image/png",
|
|
3108
|
+
quality: options?.quality ?? 1
|
|
3109
|
+
});
|
|
3110
|
+
tempGroup.destroy();
|
|
3111
|
+
return dataURL;
|
|
3003
3112
|
}
|
|
3004
3113
|
/**
|
|
3005
3114
|
* 导出带有标注的图片节点为图片
|
|
@@ -3043,7 +3152,6 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
3043
3152
|
*/
|
|
3044
3153
|
deleteSelectedNodes() {
|
|
3045
3154
|
const selectedNodeIds = this.getState().selectedNodeIds || [];
|
|
3046
|
-
if (selectedNodeIds.length === 0) return;
|
|
3047
3155
|
this.deleteNodes(selectedNodeIds);
|
|
3048
3156
|
}
|
|
3049
3157
|
/**
|
|
@@ -3053,37 +3161,31 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
3053
3161
|
* @returns 被删除的节点数据数组
|
|
3054
3162
|
*/
|
|
3055
3163
|
deleteNodes(nodeIds) {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3164
|
+
return super.deleteNodes(nodeIds);
|
|
3165
|
+
}
|
|
3166
|
+
/**
|
|
3167
|
+
* 将节点移动到最上层
|
|
3168
|
+
*/
|
|
3169
|
+
moveNodesToTop(nodeIds) {
|
|
3170
|
+
if (nodeIds.length === 0) return;
|
|
3059
3171
|
nodeIds.forEach((id) => {
|
|
3060
|
-
const
|
|
3061
|
-
if (
|
|
3062
|
-
|
|
3063
|
-
if (n.type === "image-marker" && n.meta.parent === id) {
|
|
3064
|
-
idsToDelete.add(n.id);
|
|
3065
|
-
}
|
|
3066
|
-
});
|
|
3172
|
+
const shape = this.getStage().findOne(`#${id}`);
|
|
3173
|
+
if (shape) {
|
|
3174
|
+
shape.moveToTop();
|
|
3067
3175
|
}
|
|
3068
3176
|
});
|
|
3069
|
-
|
|
3070
|
-
|
|
3177
|
+
}
|
|
3178
|
+
/**
|
|
3179
|
+
* 将节点移动到最下层
|
|
3180
|
+
*/
|
|
3181
|
+
moveNodesToBottom(nodeIds) {
|
|
3182
|
+
if (nodeIds.length === 0) return;
|
|
3183
|
+
nodeIds.forEach((id) => {
|
|
3071
3184
|
const shape = this.getStage().findOne(`#${id}`);
|
|
3072
3185
|
if (shape) {
|
|
3073
|
-
shape.
|
|
3186
|
+
shape.moveToBottom();
|
|
3074
3187
|
}
|
|
3075
3188
|
});
|
|
3076
|
-
const newNodes = nodes.filter((n) => !idsToDelete.has(n.id));
|
|
3077
|
-
this.getCanvasTransformer().clearNodes();
|
|
3078
|
-
this._updateState(
|
|
3079
|
-
{
|
|
3080
|
-
nodes: newNodes,
|
|
3081
|
-
selectedNodeIds: []
|
|
3082
|
-
},
|
|
3083
|
-
true
|
|
3084
|
-
);
|
|
3085
|
-
this.emit("nodes:deleted", deletedNodes);
|
|
3086
|
-
return deletedNodes;
|
|
3087
3189
|
}
|
|
3088
3190
|
/**
|
|
3089
3191
|
* 滚动到内容区域
|
|
@@ -3111,7 +3213,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
3111
3213
|
const hasSelection = !hasTargetIds && selectedNodeIds.length > 0;
|
|
3112
3214
|
const idsToShow = hasTargetIds ? targetNodeIds : hasSelection ? selectedNodeIds : null;
|
|
3113
3215
|
mainLayer.children.forEach((child) => {
|
|
3114
|
-
if (child.visible() && child.getClassName() !== "Transformer" && child.hasName(
|
|
3216
|
+
if (child.visible() && child.getClassName() !== "Transformer" && child.hasName(NODE_NAMES.selectable)) {
|
|
3115
3217
|
if (idsToShow) {
|
|
3116
3218
|
const id = child.id();
|
|
3117
3219
|
if (!idsToShow.includes(id)) return;
|
|
@@ -3428,7 +3530,7 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
|
|
|
3428
3530
|
] });
|
|
3429
3531
|
}
|
|
3430
3532
|
exports2.CanvasApi = CanvasApi;
|
|
3431
|
-
exports2.
|
|
3533
|
+
exports2.NODE_NAMES = NODE_NAMES;
|
|
3432
3534
|
exports2.PureCanvas = PureCanvas;
|
|
3433
3535
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
3434
3536
|
}));
|