@markup-canvas/core 1.1.7 → 1.2.1
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.d.ts +1 -1
- package/dist/lib/MarkupCanvas.d.ts +9 -12
- package/dist/lib/actions/config/getConfig.d.ts +2 -0
- package/dist/lib/actions/index.d.ts +3 -0
- package/dist/lib/actions/pan/centerContent.d.ts +2 -0
- package/dist/lib/actions/pan/index.d.ts +6 -0
- package/dist/lib/actions/pan/panDown.d.ts +2 -0
- package/dist/lib/actions/pan/panLeft.d.ts +2 -0
- package/dist/lib/actions/pan/panRight.d.ts +2 -0
- package/dist/lib/actions/pan/panUp.d.ts +2 -0
- package/dist/lib/actions/pan/scrollToPoint.d.ts +2 -0
- package/dist/lib/actions/transform/index.d.ts +2 -0
- package/dist/lib/actions/transform/resetTransform.d.ts +2 -0
- package/dist/lib/actions/transform/updateTransform.d.ts +2 -0
- package/dist/lib/actions/ui/grid/hideGrid.d.ts +2 -0
- package/dist/lib/actions/ui/grid/index.d.ts +4 -0
- package/dist/lib/actions/ui/grid/isGridVisible.d.ts +2 -0
- package/dist/lib/actions/ui/grid/showGrid.d.ts +2 -0
- package/dist/lib/actions/ui/grid/toggleGrid.d.ts +2 -0
- package/dist/lib/actions/ui/index.d.ts +4 -0
- package/dist/lib/actions/ui/rulers/areRulersVisible.d.ts +2 -0
- package/dist/lib/actions/ui/rulers/hideRulers.d.ts +2 -0
- package/dist/lib/actions/ui/rulers/index.d.ts +4 -0
- package/dist/lib/actions/ui/rulers/showRulers.d.ts +2 -0
- package/dist/lib/actions/ui/rulers/toggleRulers.d.ts +2 -0
- package/dist/lib/actions/ui/toggleTransition.d.ts +1 -0
- package/dist/lib/actions/ui/updateThemeMode.d.ts +2 -0
- package/dist/lib/actions/zoom/index.d.ts +6 -0
- package/dist/lib/actions/zoom/resetView.d.ts +2 -0
- package/dist/lib/actions/zoom/resetViewToCenter.d.ts +3 -0
- package/dist/lib/actions/zoom/setZoom.d.ts +3 -0
- package/dist/lib/actions/zoom/zoomIn.d.ts +3 -0
- package/dist/lib/actions/zoom/zoomOut.d.ts +3 -0
- package/dist/lib/actions/zoom/zoomToPoint.d.ts +2 -0
- package/dist/lib/canvas/createCanvas.d.ts +2 -2
- package/dist/lib/canvas/fitToScreen.d.ts +2 -0
- package/dist/lib/canvas/getCanvasBounds.d.ts +2 -2
- package/dist/lib/canvas/index.d.ts +1 -1
- package/dist/lib/events/emitTransformEvents.d.ts +3 -0
- package/dist/lib/events/keyboard/handleKeyDown.d.ts +3 -2
- package/dist/lib/events/keyboard/handleKeyUp.d.ts +3 -2
- package/dist/lib/events/keyboard/setupKeyboardEvents.d.ts +3 -2
- package/dist/lib/events/mouse/createMouseDragControls.d.ts +7 -0
- package/dist/lib/events/mouse/handleClickToZoom.d.ts +3 -2
- package/dist/lib/events/mouse/handleMouseDown.d.ts +3 -2
- package/dist/lib/events/mouse/handleMouseLeave.d.ts +3 -2
- package/dist/lib/events/mouse/handleMouseMove.d.ts +3 -2
- package/dist/lib/events/mouse/handleMouseUp.d.ts +3 -2
- package/dist/lib/events/mouse/setupMouseDrag.d.ts +4 -3
- package/dist/lib/events/mouse/setupMouseEvents.d.ts +4 -3
- package/dist/lib/events/touch/handleTouchMove.d.ts +3 -2
- package/dist/lib/events/touch/setupTouchEvents.d.ts +2 -2
- package/dist/lib/events/trackpad/createTrackpadPanHandler.d.ts +2 -2
- package/dist/lib/events/utils/getAdaptiveZoomSpeed.d.ts +2 -2
- package/dist/lib/events/utils/getViewportCenter.d.ts +2 -2
- package/dist/lib/events/utils/resetDragState.d.ts +3 -2
- package/dist/lib/events/utils/updateCursor.d.ts +3 -2
- package/dist/lib/events/wheel/handleWheel.d.ts +3 -2
- package/dist/lib/events/wheel/setupWheelEvents.d.ts +3 -2
- package/dist/lib/events/wheel/setupWheelHandler.d.ts +3 -2
- package/dist/lib/helpers/getVisibleArea.d.ts +7 -0
- package/dist/lib/helpers/index.d.ts +2 -0
- package/dist/lib/helpers/isPointVisible.d.ts +2 -0
- package/dist/lib/transform/applyZoomToCanvas.d.ts +2 -2
- package/dist/lib/window/bindCanvasToWindow.d.ts +3 -0
- package/dist/lib/window/broadcastEvent.d.ts +2 -0
- package/dist/lib/window/cleanupWindowBinding.d.ts +2 -0
- package/dist/lib/window/index.d.ts +3 -0
- package/dist/markup-canvas.cjs.js +689 -529
- package/dist/markup-canvas.esm.js +689 -529
- package/dist/markup-canvas.umd.js +687 -524
- package/dist/markup-canvas.umd.min.js +1 -1
- package/dist/types/canvas.d.ts +1 -48
- package/dist/types/config.d.ts +0 -3
- package/dist/types/events.d.ts +4 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/window.d.ts +84 -0
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/src/lib/MarkupCanvas.ts +139 -315
- package/src/lib/actions/config/getConfig.ts +5 -0
- package/src/lib/actions/index.ts +6 -0
- package/src/lib/actions/pan/centerContent.ts +21 -0
- package/src/lib/actions/pan/index.ts +6 -0
- package/src/lib/actions/pan/panDown.ts +13 -0
- package/src/lib/actions/pan/panLeft.ts +13 -0
- package/src/lib/actions/pan/panRight.ts +13 -0
- package/src/lib/actions/pan/panUp.ts +13 -0
- package/src/lib/actions/pan/scrollToPoint.ts +27 -0
- package/src/lib/actions/transform/index.ts +2 -0
- package/src/lib/actions/transform/resetTransform.ts +11 -0
- package/src/lib/actions/transform/updateTransform.ts +9 -0
- package/src/lib/actions/ui/grid/hideGrid.ts +9 -0
- package/src/lib/actions/ui/grid/index.ts +4 -0
- package/src/lib/actions/ui/grid/isGridVisible.ts +8 -0
- package/src/lib/actions/ui/grid/showGrid.ts +9 -0
- package/src/lib/actions/ui/grid/toggleGrid.ts +9 -0
- package/src/lib/actions/ui/index.ts +4 -0
- package/src/lib/actions/ui/rulers/areRulersVisible.ts +8 -0
- package/src/lib/actions/ui/rulers/hideRulers.ts +9 -0
- package/src/lib/actions/ui/rulers/index.ts +4 -0
- package/src/lib/actions/ui/rulers/showRulers.ts +9 -0
- package/src/lib/actions/ui/rulers/toggleRulers.ts +14 -0
- package/src/lib/actions/ui/toggleTransition.ts +3 -0
- package/src/lib/actions/ui/updateThemeMode.ts +25 -0
- package/src/lib/actions/zoom/index.ts +6 -0
- package/src/lib/actions/zoom/resetView.ts +17 -0
- package/src/lib/actions/zoom/resetViewToCenter.ts +21 -0
- package/src/lib/actions/zoom/setZoom.ts +22 -0
- package/src/lib/actions/zoom/zoomIn.ts +21 -0
- package/src/lib/actions/zoom/zoomOut.ts +21 -0
- package/src/lib/actions/zoom/zoomToPoint.ts +18 -0
- package/src/lib/canvas/createCanvas.ts +6 -14
- package/src/lib/canvas/fitToScreen.ts +27 -0
- package/src/lib/canvas/getCanvasBounds.ts +3 -4
- package/src/lib/canvas/index.ts +1 -1
- package/src/lib/config/constants.ts +2 -6
- package/src/lib/config/presets/editor-preset.ts +2 -6
- package/src/lib/events/emitTransformEvents.ts +9 -0
- package/src/lib/events/keyboard/handleKeyDown.ts +3 -2
- package/src/lib/events/keyboard/handleKeyUp.ts +3 -2
- package/src/lib/events/keyboard/setupKeyboardEvents.ts +3 -2
- package/src/lib/events/mouse/createMouseDragControls.ts +21 -0
- package/src/lib/events/mouse/handleClickToZoom.ts +3 -2
- package/src/lib/events/mouse/handleMouseDown.ts +3 -2
- package/src/lib/events/mouse/handleMouseLeave.ts +3 -2
- package/src/lib/events/mouse/handleMouseMove.ts +3 -2
- package/src/lib/events/mouse/handleMouseUp.ts +3 -2
- package/src/lib/events/mouse/setupMouseDrag.ts +5 -4
- package/src/lib/events/mouse/setupMouseEvents.ts +5 -4
- package/src/lib/events/postMessage/setupPostMessageEvents.ts +10 -0
- package/src/lib/events/touch/handleTouchMove.ts +3 -2
- package/src/lib/events/touch/setupTouchEvents.ts +3 -2
- package/src/lib/events/trackpad/createTrackpadPanHandler.ts +3 -2
- package/src/lib/events/utils/getAdaptiveZoomSpeed.ts +2 -2
- package/src/lib/events/utils/getViewportCenter.ts +2 -2
- package/src/lib/events/utils/resetDragState.ts +3 -2
- package/src/lib/events/utils/updateCursor.ts +3 -2
- package/src/lib/events/wheel/handleWheel.ts +3 -2
- package/src/lib/events/wheel/setupWheelEvents.ts +3 -2
- package/src/lib/events/wheel/setupWheelHandler.ts +3 -2
- package/src/lib/helpers/getVisibleArea.ts +6 -0
- package/src/lib/helpers/index.ts +2 -0
- package/src/lib/helpers/isPointVisible.ts +7 -0
- package/src/lib/rulers/createRulers.ts +0 -1
- package/src/lib/transform/applyZoomToCanvas.ts +2 -2
- package/src/lib/window/bindCanvasToWindow.ts +128 -0
- package/src/lib/window/broadcastEvent.ts +38 -0
- package/src/lib/window/cleanupWindowBinding.ts +15 -0
- package/src/lib/window/index.ts +3 -0
- package/src/types/canvas.ts +1 -48
- package/src/types/config.ts +1 -7
- package/src/types/events.ts +7 -1
- package/src/types/index.ts +4 -2
- package/src/types/window.ts +77 -0
- package/dist/lib/canvas/config.d.ts +0 -2
- package/dist/lib/canvas/getCanvasMethods.d.ts +0 -12
- package/src/lib/canvas/config.ts +0 -29
- package/src/lib/canvas/getCanvasMethods.ts +0 -102
- /package/dist/lib/canvas/{calcVisibleArea.d.ts → calculateVisibleArea.d.ts} +0 -0
- /package/src/lib/canvas/{calcVisibleArea.ts → calculateVisibleArea.ts} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 1.1
|
|
4
|
+
* @version 1.2.1
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -12,8 +12,7 @@ const EDITOR_PRESET = {
|
|
|
12
12
|
width: 4000,
|
|
13
13
|
height: 4000,
|
|
14
14
|
enableAcceleration: true,
|
|
15
|
-
// Global
|
|
16
|
-
bindToWindow: true,
|
|
15
|
+
// Global Instance Access
|
|
17
16
|
name: "canvas",
|
|
18
17
|
enablePostMessageAPI: true,
|
|
19
18
|
// Interaction controls
|
|
@@ -43,7 +42,7 @@ const EDITOR_PRESET = {
|
|
|
43
42
|
requireOptionForClickZoom: true,
|
|
44
43
|
// Visual elements
|
|
45
44
|
enableRulers: true,
|
|
46
|
-
enableGrid:
|
|
45
|
+
enableGrid: true,
|
|
47
46
|
showRulers: true,
|
|
48
47
|
showGrid: false,
|
|
49
48
|
rulerFontSize: 9,
|
|
@@ -67,72 +66,8 @@ const EDITOR_PRESET = {
|
|
|
67
66
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
68
67
|
// Theme
|
|
69
68
|
themeMode: "light",
|
|
70
|
-
// Callbacks
|
|
71
|
-
onTransformUpdate: () => { },
|
|
72
69
|
};
|
|
73
70
|
|
|
74
|
-
// Default transform values
|
|
75
|
-
const DEFAULT_ZOOM = 1.0;
|
|
76
|
-
// Validation thresholds
|
|
77
|
-
const ZOOM_CHANGE_THRESHOLD = 0.001;
|
|
78
|
-
// CSS transition values
|
|
79
|
-
const FALLBACK_TRANSITION_DURATION = 0.2;
|
|
80
|
-
// Zoom to fit padding factor
|
|
81
|
-
const ZOOM_FIT_PADDING = 0.9;
|
|
82
|
-
// CSS class names
|
|
83
|
-
const CANVAS_CONTAINER_CLASS = "canvas-container";
|
|
84
|
-
const TRANSFORM_LAYER_CLASS = "transform-layer";
|
|
85
|
-
const CONTENT_LAYER_CLASS = "content-layer";
|
|
86
|
-
|
|
87
|
-
function moveExistingContent(existingContent, contentLayer, transformLayer) {
|
|
88
|
-
existingContent.forEach((child) => {
|
|
89
|
-
if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
|
|
90
|
-
contentLayer.appendChild(child);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function setupContentLayer(contentLayer) {
|
|
96
|
-
contentLayer.style.position = "relative";
|
|
97
|
-
contentLayer.style.width = "100%";
|
|
98
|
-
contentLayer.style.height = "100%";
|
|
99
|
-
contentLayer.style.pointerEvents = "auto";
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Sets up the transform layer with proper styles and dimensions
|
|
103
|
-
function setupTransformLayer(transformLayer, config) {
|
|
104
|
-
transformLayer.style.position = "absolute";
|
|
105
|
-
const rulerOffset = config.rulerSize;
|
|
106
|
-
transformLayer.style.top = `${rulerOffset}px`;
|
|
107
|
-
transformLayer.style.left = `${rulerOffset}px`;
|
|
108
|
-
transformLayer.style.width = `${config.width}px`;
|
|
109
|
-
transformLayer.style.height = `${config.height}px`;
|
|
110
|
-
transformLayer.style.transformOrigin = "0 0";
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function createCanvasLayers(container, config) {
|
|
114
|
-
const existingContent = Array.from(container.children);
|
|
115
|
-
// Create or find transform layer
|
|
116
|
-
let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
|
|
117
|
-
if (!transformLayer) {
|
|
118
|
-
transformLayer = document.createElement("div");
|
|
119
|
-
transformLayer.className = TRANSFORM_LAYER_CLASS;
|
|
120
|
-
container.appendChild(transformLayer);
|
|
121
|
-
}
|
|
122
|
-
setupTransformLayer(transformLayer, config);
|
|
123
|
-
// Create or find content layer
|
|
124
|
-
let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
|
|
125
|
-
if (!contentLayer) {
|
|
126
|
-
contentLayer = document.createElement("div");
|
|
127
|
-
contentLayer.className = CONTENT_LAYER_CLASS;
|
|
128
|
-
transformLayer.appendChild(contentLayer);
|
|
129
|
-
moveExistingContent(existingContent, contentLayer, transformLayer);
|
|
130
|
-
}
|
|
131
|
-
// Set content layer properties
|
|
132
|
-
setupContentLayer(contentLayer);
|
|
133
|
-
return { transformLayer, contentLayer };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
71
|
function canvasToContent(canvasX, canvasY, matrix) {
|
|
137
72
|
if (!matrix?.inverse) {
|
|
138
73
|
return { x: canvasX, y: canvasY };
|
|
@@ -160,6 +95,19 @@ function createMatrix(scale, translateX, translateY) {
|
|
|
160
95
|
return new DOMMatrix([scale, 0, 0, scale, translateX, translateY]);
|
|
161
96
|
}
|
|
162
97
|
|
|
98
|
+
// Default transform values
|
|
99
|
+
const DEFAULT_ZOOM = 1.0;
|
|
100
|
+
// Validation thresholds
|
|
101
|
+
const ZOOM_CHANGE_THRESHOLD = 0.001;
|
|
102
|
+
// CSS transition values
|
|
103
|
+
const FALLBACK_TRANSITION_DURATION = 0.2;
|
|
104
|
+
// Zoom to fit padding factor
|
|
105
|
+
const ZOOM_FIT_PADDING = 0.9;
|
|
106
|
+
// CSS class names
|
|
107
|
+
const CANVAS_CONTAINER_CLASS = "canvas-container";
|
|
108
|
+
const TRANSFORM_LAYER_CLASS = "transform-layer";
|
|
109
|
+
const CONTENT_LAYER_CLASS = "content-layer";
|
|
110
|
+
|
|
163
111
|
function getZoomToMouseTransform(mouseX, mouseY, currentTransform, zoomFactor, config) {
|
|
164
112
|
const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
|
|
165
113
|
const transform = currentTransform || {
|
|
@@ -221,6 +169,16 @@ function getEmptyBounds() {
|
|
|
221
169
|
};
|
|
222
170
|
}
|
|
223
171
|
|
|
172
|
+
function getVisibleArea(canvas) {
|
|
173
|
+
const bounds = canvas.getBounds();
|
|
174
|
+
return bounds.visibleArea;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isPointVisible(canvas, x, y) {
|
|
178
|
+
const visibleArea = getVisibleArea(canvas);
|
|
179
|
+
return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
|
|
180
|
+
}
|
|
181
|
+
|
|
224
182
|
function withClampedZoom(config, operation) {
|
|
225
183
|
const clampFunction = (scale) => clampZoom(scale, config);
|
|
226
184
|
return operation(clampFunction);
|
|
@@ -307,8 +265,7 @@ const DEFAULT_CONFIG = {
|
|
|
307
265
|
width: 8000,
|
|
308
266
|
height: 8000,
|
|
309
267
|
enableAcceleration: true,
|
|
310
|
-
// Global
|
|
311
|
-
bindToWindow: false,
|
|
268
|
+
// Global Instance Access
|
|
312
269
|
name: "markupCanvas",
|
|
313
270
|
enablePostMessageAPI: false,
|
|
314
271
|
// Interaction controls
|
|
@@ -316,7 +273,7 @@ const DEFAULT_CONFIG = {
|
|
|
316
273
|
enablePan: true,
|
|
317
274
|
enableTouch: true,
|
|
318
275
|
enableKeyboard: true,
|
|
319
|
-
bindKeyboardEventsTo: "
|
|
276
|
+
bindKeyboardEventsTo: "document",
|
|
320
277
|
// Zoom behavior
|
|
321
278
|
zoomSpeed: 1.5,
|
|
322
279
|
minZoom: 0.05,
|
|
@@ -362,14 +319,11 @@ const DEFAULT_CONFIG = {
|
|
|
362
319
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
363
320
|
// Theme
|
|
364
321
|
themeMode: "light",
|
|
365
|
-
// Callbacks
|
|
366
|
-
onTransformUpdate: () => { },
|
|
367
322
|
};
|
|
368
323
|
|
|
369
|
-
function getCanvasBounds(canvas) {
|
|
324
|
+
function getCanvasBounds(canvas, config) {
|
|
370
325
|
try {
|
|
371
326
|
const container = canvas.container;
|
|
372
|
-
const config = canvas.config;
|
|
373
327
|
const transform = canvas.transform || {
|
|
374
328
|
scale: 1.0,
|
|
375
329
|
translateX: 0,
|
|
@@ -419,37 +373,6 @@ function getCanvasBounds(canvas) {
|
|
|
419
373
|
}
|
|
420
374
|
}
|
|
421
375
|
|
|
422
|
-
function createMatrixString(matrix) {
|
|
423
|
-
return `matrix3d(${matrix.m11}, ${matrix.m12}, ${matrix.m13}, ${matrix.m14}, ${matrix.m21}, ${matrix.m22}, ${matrix.m23}, ${matrix.m24}, ${matrix.m31}, ${matrix.m32}, ${matrix.m33}, ${matrix.m34}, ${matrix.m41}, ${matrix.m42}, ${matrix.m43}, ${matrix.m44})`;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
function applyTransform(element, matrix) {
|
|
427
|
-
if (!element?.style || !matrix) {
|
|
428
|
-
return false;
|
|
429
|
-
}
|
|
430
|
-
try {
|
|
431
|
-
element.style.transform = createMatrixString(matrix);
|
|
432
|
-
return true;
|
|
433
|
-
}
|
|
434
|
-
catch (error) {
|
|
435
|
-
console.warn("Transform application failed:", error);
|
|
436
|
-
return false;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function enableHardwareAcceleration(element) {
|
|
441
|
-
try {
|
|
442
|
-
// Set CSS properties for hardware acceleration
|
|
443
|
-
element.style.transform = element.style.transform || "translateZ(0)";
|
|
444
|
-
element.style.backfaceVisibility = "hidden";
|
|
445
|
-
return true;
|
|
446
|
-
}
|
|
447
|
-
catch (error) {
|
|
448
|
-
console.error("Failed to enable hardware acceleration:", error);
|
|
449
|
-
return false;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
|
|
453
376
|
function disableTransition(element, config) {
|
|
454
377
|
try {
|
|
455
378
|
if (config.enableTransition) {
|
|
@@ -501,85 +424,386 @@ function withTransition(element, config, operation) {
|
|
|
501
424
|
}
|
|
502
425
|
}
|
|
503
426
|
|
|
504
|
-
function
|
|
505
|
-
return {
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
427
|
+
function centerContent(canvas, config, updateTransformFn, transformLayer) {
|
|
428
|
+
return withTransition(transformLayer, config, () => {
|
|
429
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
430
|
+
const centerX = (bounds.width - bounds.contentWidth * canvas.transform.scale) / 2;
|
|
431
|
+
const centerY = (bounds.height - bounds.contentHeight * canvas.transform.scale) / 2;
|
|
432
|
+
return updateTransformFn({
|
|
433
|
+
translateX: centerX,
|
|
434
|
+
translateY: centerY,
|
|
435
|
+
});
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
function panDown(canvas, config, updateTransform) {
|
|
440
|
+
const panDistance = config.keyboardPanStep;
|
|
441
|
+
const newTransform = {
|
|
442
|
+
translateY: canvas.transform.translateY - panDistance,
|
|
443
|
+
};
|
|
444
|
+
return updateTransform(newTransform);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function panLeft(canvas, config, updateTransform) {
|
|
448
|
+
const panDistance = config.keyboardPanStep;
|
|
449
|
+
const newTransform = {
|
|
450
|
+
translateX: canvas.transform.translateX + panDistance,
|
|
451
|
+
};
|
|
452
|
+
return updateTransform(newTransform);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function panRight(canvas, config, updateTransform) {
|
|
456
|
+
const panDistance = config.keyboardPanStep;
|
|
457
|
+
const newTransform = {
|
|
458
|
+
translateX: canvas.transform.translateX - panDistance,
|
|
459
|
+
};
|
|
460
|
+
return updateTransform(newTransform);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function panUp(canvas, config, updateTransform) {
|
|
464
|
+
const panDistance = config.keyboardPanStep;
|
|
465
|
+
const newTransform = {
|
|
466
|
+
translateY: canvas.transform.translateY + panDistance,
|
|
467
|
+
};
|
|
468
|
+
return updateTransform(newTransform);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function scrollToPoint(canvas, config, x, y, updateTransform, transformLayer) {
|
|
472
|
+
return withTransition(transformLayer, config, () => {
|
|
473
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
474
|
+
const centerX = bounds.width / 2;
|
|
475
|
+
const centerY = bounds.height / 2;
|
|
476
|
+
// Calculate new translation to center the point
|
|
477
|
+
const newTranslateX = centerX - x * canvas.transform.scale;
|
|
478
|
+
const newTranslateY = centerY - y * canvas.transform.scale;
|
|
479
|
+
return updateTransform({
|
|
480
|
+
translateX: newTranslateX,
|
|
481
|
+
translateY: newTranslateY,
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function createMatrixString(matrix) {
|
|
487
|
+
return `matrix3d(${matrix.m11}, ${matrix.m12}, ${matrix.m13}, ${matrix.m14}, ${matrix.m21}, ${matrix.m22}, ${matrix.m23}, ${matrix.m24}, ${matrix.m31}, ${matrix.m32}, ${matrix.m33}, ${matrix.m34}, ${matrix.m41}, ${matrix.m42}, ${matrix.m43}, ${matrix.m44})`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function applyTransform(element, matrix) {
|
|
491
|
+
if (!element?.style || !matrix) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
element.style.transform = createMatrixString(matrix);
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
console.warn("Transform application failed:", error);
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function enableHardwareAcceleration(element) {
|
|
505
|
+
try {
|
|
506
|
+
// Set CSS properties for hardware acceleration
|
|
507
|
+
element.style.transform = element.style.transform || "translateZ(0)";
|
|
508
|
+
element.style.backfaceVisibility = "hidden";
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
console.error("Failed to enable hardware acceleration:", error);
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function updateTransform(canvas, newTransform) {
|
|
518
|
+
canvas.transform = { ...canvas.transform, ...newTransform };
|
|
519
|
+
const matrix = createMatrix(canvas.transform.scale, canvas.transform.translateX, canvas.transform.translateY);
|
|
520
|
+
return applyTransform(canvas.transformLayer, matrix);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
function resetTransform(canvas) {
|
|
524
|
+
const resetTransformData = {
|
|
525
|
+
scale: 1.0,
|
|
526
|
+
translateX: 0,
|
|
527
|
+
translateY: 0,
|
|
528
|
+
};
|
|
529
|
+
return updateTransform(canvas, resetTransformData);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function hideGrid(rulers) {
|
|
533
|
+
if (rulers?.gridOverlay) {
|
|
534
|
+
rulers.gridOverlay.style.display = "none";
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function isGridVisible(rulers) {
|
|
541
|
+
if (rulers?.gridOverlay) {
|
|
542
|
+
return rulers.gridOverlay.style.display !== "none";
|
|
543
|
+
}
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function showGrid(rulers) {
|
|
548
|
+
if (rulers?.gridOverlay) {
|
|
549
|
+
rulers.gridOverlay.style.display = "block";
|
|
550
|
+
return true;
|
|
551
|
+
}
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function toggleGrid(rulers) {
|
|
556
|
+
if (rulers?.toggleGrid) {
|
|
557
|
+
rulers.toggleGrid();
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
return false;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function areRulersVisible(rulers) {
|
|
564
|
+
if (rulers?.horizontalRuler) {
|
|
565
|
+
return rulers.horizontalRuler.style.display !== "none";
|
|
566
|
+
}
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
function hideRulers(rulers) {
|
|
571
|
+
if (rulers) {
|
|
572
|
+
rulers.hide();
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function showRulers(rulers) {
|
|
579
|
+
if (rulers) {
|
|
580
|
+
rulers.show();
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
return false;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function toggleRulers(rulers, areRulersVisible) {
|
|
587
|
+
if (rulers) {
|
|
588
|
+
const isVisible = areRulersVisible();
|
|
589
|
+
if (isVisible) {
|
|
590
|
+
rulers.hide();
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
rulers.show();
|
|
594
|
+
}
|
|
595
|
+
return true;
|
|
596
|
+
}
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function toggleTransition(enableTransition) {
|
|
601
|
+
return !enableTransition;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function createMarkupCanvasConfig(options = {}) {
|
|
605
|
+
const config = {
|
|
606
|
+
...DEFAULT_CONFIG,
|
|
607
|
+
...options,
|
|
608
|
+
};
|
|
609
|
+
if (typeof config.width !== "number" || config.width <= 0) {
|
|
610
|
+
console.warn("Invalid width, using default");
|
|
611
|
+
config.width = DEFAULT_CONFIG.width;
|
|
612
|
+
}
|
|
613
|
+
if (typeof config.height !== "number" || config.height <= 0) {
|
|
614
|
+
console.warn("Invalid height, using default");
|
|
615
|
+
config.height = DEFAULT_CONFIG.height;
|
|
616
|
+
}
|
|
617
|
+
if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
|
|
618
|
+
console.warn("Invalid zoomSpeed, using default");
|
|
619
|
+
config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
|
|
620
|
+
}
|
|
621
|
+
if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
|
|
622
|
+
console.warn("Invalid minZoom, using default");
|
|
623
|
+
config.minZoom = DEFAULT_CONFIG.minZoom;
|
|
624
|
+
}
|
|
625
|
+
if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
|
|
626
|
+
console.warn("Invalid maxZoom, using default");
|
|
627
|
+
config.maxZoom = DEFAULT_CONFIG.maxZoom;
|
|
628
|
+
}
|
|
629
|
+
if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
|
|
630
|
+
console.warn("Invalid keyboardPanStep, using default");
|
|
631
|
+
config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
|
|
632
|
+
}
|
|
633
|
+
if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
|
|
634
|
+
console.warn("Invalid keyboardFastMultiplier, using default");
|
|
635
|
+
config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
|
|
636
|
+
}
|
|
637
|
+
if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
|
|
638
|
+
console.warn("Invalid clickZoomLevel, using default");
|
|
639
|
+
config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
|
|
640
|
+
}
|
|
641
|
+
if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
|
|
642
|
+
console.warn("Invalid rulerFontSize, using default");
|
|
643
|
+
config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
|
|
644
|
+
}
|
|
645
|
+
if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
|
|
646
|
+
console.warn("Invalid rulerSize, using default");
|
|
647
|
+
config.rulerSize = DEFAULT_CONFIG.rulerSize;
|
|
648
|
+
}
|
|
649
|
+
return config;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
function updateThemeMode(canvasContainer, config, rulers, mode) {
|
|
653
|
+
const newConfig = {
|
|
654
|
+
...config,
|
|
655
|
+
themeMode: mode,
|
|
656
|
+
};
|
|
657
|
+
const updatedConfig = createMarkupCanvasConfig(newConfig);
|
|
658
|
+
// Update canvas background color
|
|
659
|
+
const backgroundColor = getThemeValue(updatedConfig, "canvasBackgroundColor");
|
|
660
|
+
canvasContainer.style.backgroundColor = backgroundColor;
|
|
661
|
+
// Update rulers if they exist
|
|
662
|
+
if (rulers) {
|
|
663
|
+
rulers.updateTheme(updatedConfig);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function resetView(canvas, transformLayer, config) {
|
|
668
|
+
return withTransition(transformLayer, config, () => {
|
|
669
|
+
return withRulerSize(canvas, config.rulerSize, (rulerSize) => {
|
|
670
|
+
const resetTransformData = {
|
|
671
|
+
scale: 1.0,
|
|
672
|
+
translateX: rulerSize * -1,
|
|
673
|
+
translateY: rulerSize * -1,
|
|
674
|
+
};
|
|
675
|
+
return updateTransform(canvas, resetTransformData);
|
|
676
|
+
});
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function getViewportCenter(canvas) {
|
|
681
|
+
try {
|
|
682
|
+
const bounds = canvas.getBounds();
|
|
683
|
+
return {
|
|
684
|
+
x: bounds.width / 2,
|
|
685
|
+
y: bounds.height / 2,
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
catch (error) {
|
|
689
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
690
|
+
return { x: 0, y: 0 };
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function resetViewToCenter(canvas, transformLayer, config, zoomToPoint) {
|
|
695
|
+
return withTransition(transformLayer, config, () => {
|
|
696
|
+
return withClampedZoom(config, (clamp) => {
|
|
697
|
+
const newScale = clamp(1.0);
|
|
698
|
+
const center = getViewportCenter(canvas);
|
|
699
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function setZoom(canvas, transformLayer, config, zoomToPoint, zoomLevel) {
|
|
705
|
+
return withTransition(transformLayer, config, () => {
|
|
706
|
+
return withClampedZoom(config, (clamp) => {
|
|
707
|
+
const newScale = clamp(zoomLevel);
|
|
708
|
+
const center = getViewportCenter(canvas);
|
|
709
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
710
|
+
});
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function zoomIn(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
|
|
715
|
+
return withTransition(transformLayer, config, () => {
|
|
716
|
+
return withClampedZoom(config, (clamp) => {
|
|
717
|
+
const newScale = clamp(canvas.transform.scale * (1 + factor));
|
|
718
|
+
const center = getViewportCenter(canvas);
|
|
719
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
720
|
+
});
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function zoomOut(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
|
|
725
|
+
return withTransition(transformLayer, config, () => {
|
|
726
|
+
return withClampedZoom(config, (clamp) => {
|
|
727
|
+
const newScale = clamp(canvas.transform.scale * (1 - factor));
|
|
728
|
+
const center = getViewportCenter(canvas);
|
|
729
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
function zoomToPoint(canvas, transformLayer, config, x, y, targetScale) {
|
|
735
|
+
return withTransition(transformLayer, config, () => {
|
|
736
|
+
const newTransform = getZoomToMouseTransform(x, y, canvas.transform, targetScale / canvas.transform.scale, config);
|
|
737
|
+
return updateTransform(canvas, newTransform);
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function fitToScreen(canvas, transformLayer, config) {
|
|
742
|
+
return withTransition(transformLayer, config, () => {
|
|
743
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
744
|
+
const scaleX = bounds.width / config.width;
|
|
745
|
+
const scaleY = bounds.height / config.height;
|
|
746
|
+
const fitScale = withClampedZoom(config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
|
|
747
|
+
// Center the content
|
|
748
|
+
const scaledWidth = config.width * fitScale;
|
|
749
|
+
const scaledHeight = config.height * fitScale;
|
|
750
|
+
const centerX = (bounds.width - scaledWidth) / 2;
|
|
751
|
+
const centerY = (bounds.height - scaledHeight) / 2;
|
|
752
|
+
return updateTransform(canvas, {
|
|
753
|
+
scale: fitScale,
|
|
754
|
+
translateX: centerX,
|
|
755
|
+
translateY: centerY,
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function moveExistingContent(existingContent, contentLayer, transformLayer) {
|
|
761
|
+
existingContent.forEach((child) => {
|
|
762
|
+
if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
|
|
763
|
+
contentLayer.appendChild(child);
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function setupContentLayer(contentLayer) {
|
|
769
|
+
contentLayer.style.position = "relative";
|
|
770
|
+
contentLayer.style.width = "100%";
|
|
771
|
+
contentLayer.style.height = "100%";
|
|
772
|
+
contentLayer.style.pointerEvents = "auto";
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// Sets up the transform layer with proper styles and dimensions
|
|
776
|
+
function setupTransformLayer(transformLayer, config) {
|
|
777
|
+
transformLayer.style.position = "absolute";
|
|
778
|
+
const rulerOffset = config.rulerSize;
|
|
779
|
+
transformLayer.style.top = `${rulerOffset}px`;
|
|
780
|
+
transformLayer.style.left = `${rulerOffset}px`;
|
|
781
|
+
transformLayer.style.width = `${config.width}px`;
|
|
782
|
+
transformLayer.style.height = `${config.height}px`;
|
|
783
|
+
transformLayer.style.transformOrigin = "0 0";
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
function createCanvasLayers(container, config) {
|
|
787
|
+
const existingContent = Array.from(container.children);
|
|
788
|
+
// Create or find transform layer
|
|
789
|
+
let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
|
|
790
|
+
if (!transformLayer) {
|
|
791
|
+
transformLayer = document.createElement("div");
|
|
792
|
+
transformLayer.className = TRANSFORM_LAYER_CLASS;
|
|
793
|
+
container.appendChild(transformLayer);
|
|
794
|
+
}
|
|
795
|
+
setupTransformLayer(transformLayer, config);
|
|
796
|
+
// Create or find content layer
|
|
797
|
+
let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
|
|
798
|
+
if (!contentLayer) {
|
|
799
|
+
contentLayer = document.createElement("div");
|
|
800
|
+
contentLayer.className = CONTENT_LAYER_CLASS;
|
|
801
|
+
transformLayer.appendChild(contentLayer);
|
|
802
|
+
moveExistingContent(existingContent, contentLayer, transformLayer);
|
|
803
|
+
}
|
|
804
|
+
// Set content layer properties
|
|
805
|
+
setupContentLayer(contentLayer);
|
|
806
|
+
return { transformLayer, contentLayer };
|
|
583
807
|
}
|
|
584
808
|
|
|
585
809
|
function checkContainerDimensions(container) {
|
|
@@ -615,7 +839,6 @@ function setupCanvasContainer(container, config) {
|
|
|
615
839
|
}
|
|
616
840
|
}
|
|
617
841
|
|
|
618
|
-
// Creates and initializes a canvas with the required DOM structure
|
|
619
842
|
function createCanvas(container, config) {
|
|
620
843
|
if (!container?.appendChild) {
|
|
621
844
|
console.error("Invalid container element provided to createCanvas");
|
|
@@ -624,10 +847,9 @@ function createCanvas(container, config) {
|
|
|
624
847
|
try {
|
|
625
848
|
setupCanvasContainer(container, config);
|
|
626
849
|
const { transformLayer, contentLayer } = createCanvasLayers(container, config);
|
|
627
|
-
|
|
628
|
-
if (config.enableAcceleration) {
|
|
850
|
+
withFeatureEnabled(config, "enableAcceleration", () => {
|
|
629
851
|
enableHardwareAcceleration(transformLayer);
|
|
630
|
-
}
|
|
852
|
+
});
|
|
631
853
|
const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
|
|
632
854
|
const initialTransform = {
|
|
633
855
|
scale: DEFAULT_ZOOM,
|
|
@@ -642,12 +864,8 @@ function createCanvas(container, config) {
|
|
|
642
864
|
container,
|
|
643
865
|
transformLayer,
|
|
644
866
|
contentLayer,
|
|
645
|
-
// Configuration
|
|
646
|
-
config: config,
|
|
647
867
|
// Current state
|
|
648
868
|
transform: initialTransform,
|
|
649
|
-
// Add all canvas methods
|
|
650
|
-
...getCanvasMethods(),
|
|
651
869
|
};
|
|
652
870
|
return canvas;
|
|
653
871
|
}
|
|
@@ -657,54 +875,6 @@ function createCanvas(container, config) {
|
|
|
657
875
|
}
|
|
658
876
|
}
|
|
659
877
|
|
|
660
|
-
function createMarkupCanvasConfig(options = {}) {
|
|
661
|
-
const config = {
|
|
662
|
-
...DEFAULT_CONFIG,
|
|
663
|
-
...options,
|
|
664
|
-
};
|
|
665
|
-
if (typeof config.width !== "number" || config.width <= 0) {
|
|
666
|
-
console.warn("Invalid width, using default");
|
|
667
|
-
config.width = DEFAULT_CONFIG.width;
|
|
668
|
-
}
|
|
669
|
-
if (typeof config.height !== "number" || config.height <= 0) {
|
|
670
|
-
console.warn("Invalid height, using default");
|
|
671
|
-
config.height = DEFAULT_CONFIG.height;
|
|
672
|
-
}
|
|
673
|
-
if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
|
|
674
|
-
console.warn("Invalid zoomSpeed, using default");
|
|
675
|
-
config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
|
|
676
|
-
}
|
|
677
|
-
if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
|
|
678
|
-
console.warn("Invalid minZoom, using default");
|
|
679
|
-
config.minZoom = DEFAULT_CONFIG.minZoom;
|
|
680
|
-
}
|
|
681
|
-
if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
|
|
682
|
-
console.warn("Invalid maxZoom, using default");
|
|
683
|
-
config.maxZoom = DEFAULT_CONFIG.maxZoom;
|
|
684
|
-
}
|
|
685
|
-
if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
|
|
686
|
-
console.warn("Invalid keyboardPanStep, using default");
|
|
687
|
-
config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
|
|
688
|
-
}
|
|
689
|
-
if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
|
|
690
|
-
console.warn("Invalid keyboardFastMultiplier, using default");
|
|
691
|
-
config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
|
|
692
|
-
}
|
|
693
|
-
if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
|
|
694
|
-
console.warn("Invalid clickZoomLevel, using default");
|
|
695
|
-
config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
|
|
696
|
-
}
|
|
697
|
-
if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
|
|
698
|
-
console.warn("Invalid rulerFontSize, using default");
|
|
699
|
-
config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
|
|
700
|
-
}
|
|
701
|
-
if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
|
|
702
|
-
console.warn("Invalid rulerSize, using default");
|
|
703
|
-
config.rulerSize = DEFAULT_CONFIG.rulerSize;
|
|
704
|
-
}
|
|
705
|
-
return config;
|
|
706
|
-
}
|
|
707
|
-
|
|
708
878
|
class EventEmitter {
|
|
709
879
|
constructor() {
|
|
710
880
|
this.listeners = new Map();
|
|
@@ -743,6 +913,13 @@ class EventEmitter {
|
|
|
743
913
|
}
|
|
744
914
|
}
|
|
745
915
|
|
|
916
|
+
function emitTransformEvents(listen, canvas) {
|
|
917
|
+
const transform = canvas.transform;
|
|
918
|
+
listen.emit("transform", transform);
|
|
919
|
+
listen.emit("zoom", transform.scale);
|
|
920
|
+
listen.emit("pan", { x: transform.translateX, y: transform.translateY });
|
|
921
|
+
}
|
|
922
|
+
|
|
746
923
|
const REFERENCE_DISPLAY_AREA = 1920 * 1080;
|
|
747
924
|
const TRACKPAD_PINCH_SPEED_FACTOR = 0.05;
|
|
748
925
|
const ADAPTIVE_ZOOM_FACTOR = 1;
|
|
@@ -1237,6 +1414,17 @@ function setupPostMessageEvents(canvas) {
|
|
|
1237
1414
|
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
1238
1415
|
canvas.updateThemeMode(newMode);
|
|
1239
1416
|
}
|
|
1417
|
+
// Transition methods
|
|
1418
|
+
else if (action === "updateTransition") {
|
|
1419
|
+
const enabled = args[0];
|
|
1420
|
+
if (typeof enabled !== "boolean") {
|
|
1421
|
+
throw new Error(`Invalid transition enabled value: ${enabled}. Must be a boolean.`);
|
|
1422
|
+
}
|
|
1423
|
+
canvas.updateTransition(enabled);
|
|
1424
|
+
}
|
|
1425
|
+
else if (action === "toggleTransitionMode") {
|
|
1426
|
+
canvas.toggleTransitionMode();
|
|
1427
|
+
}
|
|
1240
1428
|
else {
|
|
1241
1429
|
throw new Error(`Unknown action: ${action}`);
|
|
1242
1430
|
}
|
|
@@ -1472,20 +1660,6 @@ function setupWheelEvents(canvas, config) {
|
|
|
1472
1660
|
};
|
|
1473
1661
|
}
|
|
1474
1662
|
|
|
1475
|
-
function getViewportCenter(canvas) {
|
|
1476
|
-
try {
|
|
1477
|
-
const bounds = canvas.getBounds();
|
|
1478
|
-
return {
|
|
1479
|
-
x: bounds.width / 2,
|
|
1480
|
-
y: bounds.height / 2,
|
|
1481
|
-
};
|
|
1482
|
-
}
|
|
1483
|
-
catch (error) {
|
|
1484
|
-
console.warn("Failed to calculate viewport center:", error);
|
|
1485
|
-
return { x: 0, y: 0 };
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
1663
|
// Rulers
|
|
1490
1664
|
const RULER_Z_INDEX = {
|
|
1491
1665
|
GRID: 100,
|
|
@@ -1848,8 +2022,6 @@ function createRulers(canvas, config) {
|
|
|
1848
2022
|
elements.verticalRuler.style.display = "block";
|
|
1849
2023
|
if (elements.cornerBox)
|
|
1850
2024
|
elements.cornerBox.style.display = "flex";
|
|
1851
|
-
if (elements.gridOverlay)
|
|
1852
|
-
elements.gridOverlay.style.display = "block";
|
|
1853
2025
|
},
|
|
1854
2026
|
hide: () => {
|
|
1855
2027
|
if (elements.horizontalRuler)
|
|
@@ -1893,14 +2065,163 @@ function createRulers(canvas, config) {
|
|
|
1893
2065
|
}
|
|
1894
2066
|
}
|
|
1895
2067
|
|
|
2068
|
+
function broadcastEvent(event, data, config) {
|
|
2069
|
+
if (typeof window === "undefined") {
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2072
|
+
// Receivers can get the instance from the window binding
|
|
2073
|
+
let broadcastData = data;
|
|
2074
|
+
if (event === "ready") {
|
|
2075
|
+
broadcastData = { ready: true };
|
|
2076
|
+
}
|
|
2077
|
+
window.postMessage({
|
|
2078
|
+
source: "markup-canvas",
|
|
2079
|
+
event,
|
|
2080
|
+
data: broadcastData,
|
|
2081
|
+
timestamp: Date.now(),
|
|
2082
|
+
canvasName: config.name,
|
|
2083
|
+
}, "*");
|
|
2084
|
+
if (window.parent) {
|
|
2085
|
+
window.parent.postMessage({
|
|
2086
|
+
source: "markup-canvas",
|
|
2087
|
+
event,
|
|
2088
|
+
data: broadcastData,
|
|
2089
|
+
timestamp: Date.now(),
|
|
2090
|
+
canvasName: config.name,
|
|
2091
|
+
}, "*");
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
function cleanupWindowBinding(config) {
|
|
2096
|
+
if (typeof window === "undefined") {
|
|
2097
|
+
return;
|
|
2098
|
+
}
|
|
2099
|
+
const canvasName = config.name || "markupCanvas";
|
|
2100
|
+
const windowObj = window;
|
|
2101
|
+
delete windowObj[canvasName];
|
|
2102
|
+
if (windowObj.__markupCanvasInstances) {
|
|
2103
|
+
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
function bindCanvasToWindow(canvas, config) {
|
|
2108
|
+
if (typeof window === "undefined") {
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
const canvasName = config.name || "markupCanvas";
|
|
2112
|
+
const windowObj = window;
|
|
2113
|
+
const api = {
|
|
2114
|
+
config: {
|
|
2115
|
+
get current() {
|
|
2116
|
+
return canvas.config;
|
|
2117
|
+
},
|
|
2118
|
+
get: canvas.getConfig.bind(canvas),
|
|
2119
|
+
update: canvas.updateConfig.bind(canvas),
|
|
2120
|
+
},
|
|
2121
|
+
// Transform group
|
|
2122
|
+
transform: {
|
|
2123
|
+
update: canvas.updateTransform.bind(canvas),
|
|
2124
|
+
reset: canvas.reset.bind(canvas),
|
|
2125
|
+
},
|
|
2126
|
+
// Zoom group
|
|
2127
|
+
zoom: {
|
|
2128
|
+
set: canvas.setZoom.bind(canvas),
|
|
2129
|
+
toPoint: canvas.zoomToPoint.bind(canvas),
|
|
2130
|
+
in: canvas.zoomIn.bind(canvas),
|
|
2131
|
+
out: canvas.zoomOut.bind(canvas),
|
|
2132
|
+
reset: canvas.resetZoom.bind(canvas),
|
|
2133
|
+
resetToCenter: canvas.resetViewToCenter.bind(canvas),
|
|
2134
|
+
fitToScreen: canvas.fitToScreen.bind(canvas),
|
|
2135
|
+
},
|
|
2136
|
+
// Pan group
|
|
2137
|
+
pan: {
|
|
2138
|
+
left: canvas.panLeft.bind(canvas),
|
|
2139
|
+
right: canvas.panRight.bind(canvas),
|
|
2140
|
+
up: canvas.panUp.bind(canvas),
|
|
2141
|
+
down: canvas.panDown.bind(canvas),
|
|
2142
|
+
toPoint: canvas.scrollToPoint.bind(canvas),
|
|
2143
|
+
toCenter: canvas.centerContent.bind(canvas),
|
|
2144
|
+
},
|
|
2145
|
+
// Mouse drag group
|
|
2146
|
+
mouseDrag: {
|
|
2147
|
+
enable: canvas.enableMouseDrag.bind(canvas),
|
|
2148
|
+
disable: canvas.disableMouseDrag.bind(canvas),
|
|
2149
|
+
isEnabled: canvas.isMouseDragEnabled.bind(canvas),
|
|
2150
|
+
},
|
|
2151
|
+
// Grid group
|
|
2152
|
+
grid: {
|
|
2153
|
+
toggle: canvas.toggleGrid.bind(canvas),
|
|
2154
|
+
show: canvas.showGrid.bind(canvas),
|
|
2155
|
+
hide: canvas.hideGrid.bind(canvas),
|
|
2156
|
+
isVisible: canvas.isGridVisible.bind(canvas),
|
|
2157
|
+
},
|
|
2158
|
+
// Ruler group
|
|
2159
|
+
rulers: {
|
|
2160
|
+
toggle: canvas.toggleRulers.bind(canvas),
|
|
2161
|
+
show: canvas.showRulers.bind(canvas),
|
|
2162
|
+
hide: canvas.hideRulers.bind(canvas),
|
|
2163
|
+
isVisible: canvas.areRulersVisible.bind(canvas),
|
|
2164
|
+
},
|
|
2165
|
+
// Utility group
|
|
2166
|
+
canvas: {
|
|
2167
|
+
canvasToContent: canvas.canvasToContent.bind(canvas),
|
|
2168
|
+
getVisibleArea: canvas.getVisibleArea.bind(canvas),
|
|
2169
|
+
isPointVisible: canvas.isPointVisible.bind(canvas),
|
|
2170
|
+
getBounds: canvas.getBounds.bind(canvas),
|
|
2171
|
+
},
|
|
2172
|
+
theme: {
|
|
2173
|
+
get current() {
|
|
2174
|
+
return canvas.config.themeMode;
|
|
2175
|
+
},
|
|
2176
|
+
update: canvas.updateThemeMode.bind(canvas),
|
|
2177
|
+
toggle: canvas.toggleThemeMode.bind(canvas),
|
|
2178
|
+
},
|
|
2179
|
+
transition: {
|
|
2180
|
+
get current() {
|
|
2181
|
+
return canvas.config.enableTransition;
|
|
2182
|
+
},
|
|
2183
|
+
set: canvas.updateTransition.bind(canvas),
|
|
2184
|
+
toggle: canvas.toggleTransitionMode.bind(canvas),
|
|
2185
|
+
},
|
|
2186
|
+
// Event group
|
|
2187
|
+
event: canvas.event,
|
|
2188
|
+
// Lifecycle group
|
|
2189
|
+
lifecycle: {
|
|
2190
|
+
cleanup: canvas.cleanup.bind(canvas),
|
|
2191
|
+
destroy: canvas.destroy.bind(canvas),
|
|
2192
|
+
},
|
|
2193
|
+
// Properties/State group
|
|
2194
|
+
state: {
|
|
2195
|
+
get isReady() {
|
|
2196
|
+
return canvas.isReady;
|
|
2197
|
+
},
|
|
2198
|
+
get isTransforming() {
|
|
2199
|
+
return canvas.isTransforming;
|
|
2200
|
+
},
|
|
2201
|
+
get visibleBounds() {
|
|
2202
|
+
return canvas.visibleBounds;
|
|
2203
|
+
},
|
|
2204
|
+
get transform() {
|
|
2205
|
+
return canvas.transform;
|
|
2206
|
+
},
|
|
2207
|
+
},
|
|
2208
|
+
};
|
|
2209
|
+
// Bind public API to window
|
|
2210
|
+
windowObj[canvasName] = api;
|
|
2211
|
+
// Track all instances
|
|
2212
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
2213
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
2214
|
+
}
|
|
2215
|
+
windowObj.__markupCanvasInstances.set(canvasName, api);
|
|
2216
|
+
}
|
|
2217
|
+
|
|
1896
2218
|
class MarkupCanvas {
|
|
1897
2219
|
constructor(container, options = {}) {
|
|
1898
|
-
this.
|
|
2220
|
+
this.cleanupCallbacks = [];
|
|
1899
2221
|
this.rulers = null;
|
|
1900
2222
|
this.dragSetup = null;
|
|
2223
|
+
this.event = new EventEmitter();
|
|
1901
2224
|
this._isReady = false;
|
|
1902
|
-
this.listen = new EventEmitter();
|
|
1903
|
-
this.postMessageCleanup = null;
|
|
1904
2225
|
if (!container) {
|
|
1905
2226
|
throw new Error("Container element is required");
|
|
1906
2227
|
}
|
|
@@ -1909,100 +2230,48 @@ class MarkupCanvas {
|
|
|
1909
2230
|
if (!canvas) {
|
|
1910
2231
|
throw new Error("Failed to create canvas");
|
|
1911
2232
|
}
|
|
1912
|
-
this.
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
2233
|
+
this.canvas = canvas;
|
|
2234
|
+
// Always bind canvas to window
|
|
2235
|
+
this.event.setEmitInterceptor((event, data) => {
|
|
2236
|
+
broadcastEvent(event, data, this.config);
|
|
2237
|
+
});
|
|
2238
|
+
bindCanvasToWindow(this, this.config);
|
|
2239
|
+
// Set up postMessage listener
|
|
2240
|
+
if (this.config.enablePostMessageAPI) {
|
|
2241
|
+
const postMessageCleanup = setupPostMessageEvents(this);
|
|
2242
|
+
this.cleanupCallbacks.push(postMessageCleanup);
|
|
1922
2243
|
}
|
|
1923
2244
|
this.setupEventHandlers();
|
|
1924
2245
|
this._isReady = true;
|
|
1925
2246
|
// Emit ready event
|
|
1926
|
-
this.
|
|
1927
|
-
}
|
|
1928
|
-
setupGlobalBinding() {
|
|
1929
|
-
if (typeof window === "undefined") {
|
|
1930
|
-
return;
|
|
1931
|
-
}
|
|
1932
|
-
const canvasName = this.config.name || "markupCanvas";
|
|
1933
|
-
const windowObj = window;
|
|
1934
|
-
// Bind instance to window
|
|
1935
|
-
windowObj[canvasName] = this;
|
|
1936
|
-
// Track all instances
|
|
1937
|
-
if (!windowObj.__markupCanvasInstances) {
|
|
1938
|
-
windowObj.__markupCanvasInstances = new Map();
|
|
1939
|
-
}
|
|
1940
|
-
windowObj.__markupCanvasInstances.set(canvasName, this);
|
|
1941
|
-
}
|
|
1942
|
-
cleanupGlobalBinding() {
|
|
1943
|
-
if (typeof window === "undefined") {
|
|
1944
|
-
return;
|
|
1945
|
-
}
|
|
1946
|
-
const canvasName = this.config.name || "markupCanvas";
|
|
1947
|
-
const windowObj = window;
|
|
1948
|
-
delete windowObj[canvasName];
|
|
1949
|
-
if (windowObj.__markupCanvasInstances) {
|
|
1950
|
-
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
1951
|
-
}
|
|
1952
|
-
}
|
|
1953
|
-
broadcastEvent(event, data) {
|
|
1954
|
-
if (typeof window === "undefined") {
|
|
1955
|
-
return;
|
|
1956
|
-
}
|
|
1957
|
-
// Receivers can get the instance from the window binding
|
|
1958
|
-
let broadcastData = data;
|
|
1959
|
-
if (event === "ready") {
|
|
1960
|
-
broadcastData = { ready: true };
|
|
1961
|
-
}
|
|
1962
|
-
window.postMessage({
|
|
1963
|
-
source: "markup-canvas",
|
|
1964
|
-
event,
|
|
1965
|
-
data: broadcastData,
|
|
1966
|
-
timestamp: Date.now(),
|
|
1967
|
-
canvasName: this.config.name,
|
|
1968
|
-
}, "*");
|
|
1969
|
-
if (window.parent) {
|
|
1970
|
-
window.parent.postMessage({
|
|
1971
|
-
source: "markup-canvas",
|
|
1972
|
-
event,
|
|
1973
|
-
data: broadcastData,
|
|
1974
|
-
timestamp: Date.now(),
|
|
1975
|
-
canvasName: this.config.name,
|
|
1976
|
-
}, "*");
|
|
1977
|
-
}
|
|
2247
|
+
this.event.emit("ready", this);
|
|
1978
2248
|
}
|
|
1979
2249
|
setupEventHandlers() {
|
|
1980
2250
|
try {
|
|
1981
|
-
// Wheel
|
|
2251
|
+
// Wheel events
|
|
1982
2252
|
withFeatureEnabled(this.config, "enableZoom", () => {
|
|
1983
2253
|
const wheelCleanup = setupWheelEvents(this, this.config);
|
|
1984
|
-
this.
|
|
2254
|
+
this.cleanupCallbacks.push(wheelCleanup);
|
|
1985
2255
|
});
|
|
1986
|
-
// Mouse events
|
|
1987
|
-
// Set up mouse events if either pan or click-to-zoom is enabled
|
|
2256
|
+
// Mouse events
|
|
1988
2257
|
if (this.config.enablePan || this.config.enableClickToZoom) {
|
|
1989
2258
|
this.dragSetup = setupMouseEvents(this, this.config, true);
|
|
1990
|
-
this.
|
|
2259
|
+
this.cleanupCallbacks.push(this.dragSetup.cleanup);
|
|
1991
2260
|
}
|
|
1992
|
-
// Keyboard
|
|
2261
|
+
// Keyboard events
|
|
1993
2262
|
withFeatureEnabled(this.config, "enableKeyboard", () => {
|
|
1994
2263
|
const keyboardCleanup = setupKeyboardEvents(this, this.config);
|
|
1995
|
-
this.
|
|
2264
|
+
this.cleanupCallbacks.push(keyboardCleanup);
|
|
1996
2265
|
});
|
|
1997
|
-
// Touch events
|
|
2266
|
+
// Touch events
|
|
1998
2267
|
withFeatureEnabled(this.config, "enableTouch", () => {
|
|
1999
2268
|
const touchCleanup = setupTouchEvents(this);
|
|
2000
|
-
this.
|
|
2269
|
+
this.cleanupCallbacks.push(touchCleanup);
|
|
2001
2270
|
});
|
|
2002
2271
|
// Set up rulers and grid
|
|
2003
2272
|
withFeatureEnabled(this.config, "enableRulers", () => {
|
|
2004
|
-
this.rulers = createRulers(this
|
|
2005
|
-
this.
|
|
2273
|
+
this.rulers = createRulers(this, this.config);
|
|
2274
|
+
this.cleanupCallbacks.push(() => {
|
|
2006
2275
|
if (this.rulers) {
|
|
2007
2276
|
this.rulers.destroy();
|
|
2008
2277
|
}
|
|
@@ -2015,20 +2284,18 @@ class MarkupCanvas {
|
|
|
2015
2284
|
throw error;
|
|
2016
2285
|
}
|
|
2017
2286
|
}
|
|
2018
|
-
// Base canvas properties and methods
|
|
2019
2287
|
get container() {
|
|
2020
|
-
return this.
|
|
2288
|
+
return this.canvas.container;
|
|
2021
2289
|
}
|
|
2022
2290
|
get transformLayer() {
|
|
2023
|
-
return this.
|
|
2291
|
+
return this.canvas.transformLayer;
|
|
2024
2292
|
}
|
|
2025
2293
|
get contentLayer() {
|
|
2026
|
-
return this.
|
|
2294
|
+
return this.canvas.contentLayer;
|
|
2027
2295
|
}
|
|
2028
2296
|
get transform() {
|
|
2029
|
-
return this.
|
|
2297
|
+
return this.canvas.transform;
|
|
2030
2298
|
}
|
|
2031
|
-
// State management getters for React integration
|
|
2032
2299
|
get isReady() {
|
|
2033
2300
|
return this._isReady;
|
|
2034
2301
|
}
|
|
@@ -2036,131 +2303,66 @@ class MarkupCanvas {
|
|
|
2036
2303
|
return this.dragSetup?.isEnabled() || false;
|
|
2037
2304
|
}
|
|
2038
2305
|
get visibleBounds() {
|
|
2039
|
-
return
|
|
2306
|
+
return getVisibleArea(this);
|
|
2040
2307
|
}
|
|
2041
2308
|
getBounds() {
|
|
2042
|
-
return this.
|
|
2309
|
+
return getCanvasBounds(this.canvas, this.config);
|
|
2043
2310
|
}
|
|
2044
2311
|
updateTransform(newTransform) {
|
|
2045
|
-
const result = this.
|
|
2312
|
+
const result = updateTransform(this.canvas, newTransform);
|
|
2046
2313
|
if (result) {
|
|
2047
|
-
this.
|
|
2314
|
+
emitTransformEvents(this.event, this.canvas);
|
|
2048
2315
|
}
|
|
2049
2316
|
return result;
|
|
2050
2317
|
}
|
|
2051
|
-
emitTransformEvents() {
|
|
2052
|
-
const transform = this.baseCanvas.transform;
|
|
2053
|
-
this.listen.emit("transform", transform);
|
|
2054
|
-
this.listen.emit("zoom", transform.scale);
|
|
2055
|
-
this.listen.emit("pan", { x: transform.translateX, y: transform.translateY });
|
|
2056
|
-
}
|
|
2057
2318
|
reset() {
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2319
|
+
const result = resetTransform(this.canvas);
|
|
2320
|
+
if (result) {
|
|
2321
|
+
emitTransformEvents(this.event, this.canvas);
|
|
2322
|
+
}
|
|
2323
|
+
return result;
|
|
2062
2324
|
}
|
|
2063
2325
|
setZoom(zoomLevel) {
|
|
2064
|
-
return
|
|
2065
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2066
|
-
const newScale = clamp(zoomLevel);
|
|
2067
|
-
const newTransform = {
|
|
2068
|
-
scale: newScale,
|
|
2069
|
-
};
|
|
2070
|
-
return this.updateTransform(newTransform);
|
|
2071
|
-
});
|
|
2072
|
-
});
|
|
2326
|
+
return setZoom(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), zoomLevel);
|
|
2073
2327
|
}
|
|
2074
2328
|
canvasToContent(x, y) {
|
|
2075
|
-
|
|
2329
|
+
const matrix = createMatrix(this.canvas.transform.scale, this.canvas.transform.translateX, this.canvas.transform.translateY);
|
|
2330
|
+
return canvasToContent(x, y, matrix);
|
|
2076
2331
|
}
|
|
2077
2332
|
zoomToPoint(x, y, targetScale) {
|
|
2078
|
-
|
|
2079
|
-
const result = this.baseCanvas.zoomToPoint(x, y, targetScale);
|
|
2080
|
-
if (result) {
|
|
2081
|
-
this.emitTransformEvents();
|
|
2082
|
-
}
|
|
2083
|
-
return result;
|
|
2084
|
-
});
|
|
2085
|
-
}
|
|
2086
|
-
resetView() {
|
|
2087
|
-
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
2333
|
+
const result = zoomToPoint(this.canvas, this.transformLayer, this.config, x, y, targetScale);
|
|
2088
2334
|
if (result) {
|
|
2089
|
-
this.
|
|
2335
|
+
emitTransformEvents(this.event, this.canvas);
|
|
2090
2336
|
}
|
|
2091
2337
|
return result;
|
|
2092
2338
|
}
|
|
2093
|
-
|
|
2094
|
-
return
|
|
2095
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2096
|
-
const newScale = clamp(1.0);
|
|
2097
|
-
const center = getViewportCenter(this);
|
|
2098
|
-
const result = this.zoomToPoint(center.x, center.y, newScale);
|
|
2099
|
-
if (result) {
|
|
2100
|
-
this.emitTransformEvents();
|
|
2101
|
-
}
|
|
2102
|
-
return result;
|
|
2103
|
-
});
|
|
2104
|
-
});
|
|
2339
|
+
resetView() {
|
|
2340
|
+
return resetView(this.canvas, this.transformLayer, this.config);
|
|
2105
2341
|
}
|
|
2106
|
-
|
|
2107
|
-
return
|
|
2108
|
-
const result = this.baseCanvas.zoomToFitContent();
|
|
2109
|
-
if (result) {
|
|
2110
|
-
this.emitTransformEvents();
|
|
2111
|
-
}
|
|
2112
|
-
return result;
|
|
2113
|
-
});
|
|
2342
|
+
resetViewToCenter() {
|
|
2343
|
+
return resetViewToCenter(this, this.transformLayer, this.config, this.zoomToPoint.bind(this));
|
|
2114
2344
|
}
|
|
2115
|
-
// Pan methods
|
|
2116
2345
|
panLeft(distance) {
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
translateX: this.baseCanvas.transform.translateX + panDistance,
|
|
2120
|
-
};
|
|
2121
|
-
return this.updateTransform(newTransform);
|
|
2346
|
+
return (panLeft(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2347
|
+
(distance ? panLeft(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2122
2348
|
}
|
|
2123
2349
|
panRight(distance) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
translateX: this.baseCanvas.transform.translateX - panDistance,
|
|
2127
|
-
};
|
|
2128
|
-
return this.updateTransform(newTransform);
|
|
2350
|
+
return (panRight(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2351
|
+
(distance ? panRight(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2129
2352
|
}
|
|
2130
2353
|
panUp(distance) {
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
translateY: this.baseCanvas.transform.translateY + panDistance,
|
|
2134
|
-
};
|
|
2135
|
-
return this.updateTransform(newTransform);
|
|
2354
|
+
return (panUp(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2355
|
+
(distance ? panUp(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2136
2356
|
}
|
|
2137
2357
|
panDown(distance) {
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
translateY: this.baseCanvas.transform.translateY - panDistance,
|
|
2141
|
-
};
|
|
2142
|
-
return this.updateTransform(newTransform);
|
|
2358
|
+
return (panDown(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2359
|
+
(distance ? panDown(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2143
2360
|
}
|
|
2144
|
-
// Zoom methods
|
|
2145
2361
|
zoomIn(factor = 0.5) {
|
|
2146
|
-
return
|
|
2147
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2148
|
-
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
2149
|
-
// Get the center of the viewport
|
|
2150
|
-
const center = getViewportCenter(this);
|
|
2151
|
-
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2152
|
-
});
|
|
2153
|
-
});
|
|
2362
|
+
return zoomIn(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
|
|
2154
2363
|
}
|
|
2155
2364
|
zoomOut(factor = 0.5) {
|
|
2156
|
-
return
|
|
2157
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2158
|
-
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
2159
|
-
// Get the center of the viewport
|
|
2160
|
-
const center = getViewportCenter(this);
|
|
2161
|
-
return this.zoomToPoint(center.x, center.y, newScale);
|
|
2162
|
-
});
|
|
2163
|
-
});
|
|
2365
|
+
return zoomOut(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
|
|
2164
2366
|
}
|
|
2165
2367
|
resetZoom() {
|
|
2166
2368
|
return this.resetViewToCenter();
|
|
@@ -2175,110 +2377,68 @@ class MarkupCanvas {
|
|
|
2175
2377
|
isMouseDragEnabled() {
|
|
2176
2378
|
return this.dragSetup?.isEnabled() ?? false;
|
|
2177
2379
|
}
|
|
2178
|
-
// Grid control methods
|
|
2179
2380
|
toggleGrid() {
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2381
|
+
const result = toggleGrid(this.rulers);
|
|
2382
|
+
if (result) {
|
|
2383
|
+
this.event.emit("gridVisibility", this.isGridVisible());
|
|
2183
2384
|
}
|
|
2184
|
-
return
|
|
2385
|
+
return result;
|
|
2185
2386
|
}
|
|
2186
2387
|
showGrid() {
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2388
|
+
const result = showGrid(this.rulers);
|
|
2389
|
+
if (result) {
|
|
2390
|
+
this.event.emit("gridVisibility", true);
|
|
2190
2391
|
}
|
|
2191
|
-
return
|
|
2392
|
+
return result;
|
|
2192
2393
|
}
|
|
2193
2394
|
hideGrid() {
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2395
|
+
const result = hideGrid(this.rulers);
|
|
2396
|
+
if (result) {
|
|
2397
|
+
this.event.emit("gridVisibility", false);
|
|
2197
2398
|
}
|
|
2198
|
-
return
|
|
2399
|
+
return result;
|
|
2199
2400
|
}
|
|
2200
2401
|
isGridVisible() {
|
|
2201
|
-
|
|
2202
|
-
return this.rulers.gridOverlay.style.display !== "none";
|
|
2203
|
-
}
|
|
2204
|
-
return false;
|
|
2402
|
+
return isGridVisible(this.rulers);
|
|
2205
2403
|
}
|
|
2206
|
-
// Ruler control methods
|
|
2207
2404
|
toggleRulers() {
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
this.rulers.hide();
|
|
2212
|
-
}
|
|
2213
|
-
else {
|
|
2214
|
-
this.rulers.show();
|
|
2215
|
-
}
|
|
2216
|
-
return true;
|
|
2405
|
+
const result = toggleRulers(this.rulers, () => this.areRulersVisible());
|
|
2406
|
+
if (result) {
|
|
2407
|
+
this.event.emit("rulersVisibility", this.areRulersVisible());
|
|
2217
2408
|
}
|
|
2218
|
-
return
|
|
2409
|
+
return result;
|
|
2219
2410
|
}
|
|
2220
2411
|
showRulers() {
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2412
|
+
const result = showRulers(this.rulers);
|
|
2413
|
+
if (result) {
|
|
2414
|
+
this.event.emit("rulersVisibility", true);
|
|
2224
2415
|
}
|
|
2225
|
-
return
|
|
2416
|
+
return result;
|
|
2226
2417
|
}
|
|
2227
2418
|
hideRulers() {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2419
|
+
const result = hideRulers(this.rulers);
|
|
2420
|
+
if (result) {
|
|
2421
|
+
this.event.emit("rulersVisibility", false);
|
|
2231
2422
|
}
|
|
2232
|
-
return
|
|
2423
|
+
return result;
|
|
2233
2424
|
}
|
|
2234
2425
|
areRulersVisible() {
|
|
2235
|
-
|
|
2236
|
-
return this.rulers.horizontalRuler.style.display !== "none";
|
|
2237
|
-
}
|
|
2238
|
-
return false;
|
|
2426
|
+
return areRulersVisible(this.rulers);
|
|
2239
2427
|
}
|
|
2240
|
-
// Utility methods
|
|
2241
2428
|
centerContent() {
|
|
2242
|
-
return
|
|
2243
|
-
const bounds = this.baseCanvas.getBounds();
|
|
2244
|
-
const centerX = (bounds.width - bounds.contentWidth * this.baseCanvas.transform.scale) / 2;
|
|
2245
|
-
const centerY = (bounds.height - bounds.contentHeight * this.baseCanvas.transform.scale) / 2;
|
|
2246
|
-
return this.updateTransform({
|
|
2247
|
-
translateX: centerX,
|
|
2248
|
-
translateY: centerY,
|
|
2249
|
-
});
|
|
2250
|
-
});
|
|
2429
|
+
return centerContent(this.canvas, this.config, this.updateTransform.bind(this), this.transformLayer);
|
|
2251
2430
|
}
|
|
2252
2431
|
fitToScreen() {
|
|
2253
|
-
return
|
|
2254
|
-
const result = this.baseCanvas.zoomToFitContent();
|
|
2255
|
-
if (result) {
|
|
2256
|
-
this.emitTransformEvents();
|
|
2257
|
-
}
|
|
2258
|
-
return result;
|
|
2259
|
-
});
|
|
2432
|
+
return fitToScreen(this.canvas, this.transformLayer, this.config);
|
|
2260
2433
|
}
|
|
2261
2434
|
getVisibleArea() {
|
|
2262
|
-
|
|
2263
|
-
return bounds.visibleArea;
|
|
2435
|
+
return getVisibleArea(this);
|
|
2264
2436
|
}
|
|
2265
2437
|
isPointVisible(x, y) {
|
|
2266
|
-
|
|
2267
|
-
return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
|
|
2438
|
+
return isPointVisible(this, x, y);
|
|
2268
2439
|
}
|
|
2269
2440
|
scrollToPoint(x, y) {
|
|
2270
|
-
return
|
|
2271
|
-
const bounds = this.baseCanvas.getBounds();
|
|
2272
|
-
const centerX = bounds.width / 2;
|
|
2273
|
-
const centerY = bounds.height / 2;
|
|
2274
|
-
// Calculate new translation to center the point
|
|
2275
|
-
const newTranslateX = centerX - x * this.baseCanvas.transform.scale;
|
|
2276
|
-
const newTranslateY = centerY - y * this.baseCanvas.transform.scale;
|
|
2277
|
-
return this.updateTransform({
|
|
2278
|
-
translateX: newTranslateX,
|
|
2279
|
-
translateY: newTranslateY,
|
|
2280
|
-
});
|
|
2281
|
-
});
|
|
2441
|
+
return scrollToPoint(this.canvas, this.config, x, y, this.updateTransform.bind(this), this.transformLayer);
|
|
2282
2442
|
}
|
|
2283
2443
|
// Configuration access
|
|
2284
2444
|
getConfig() {
|
|
@@ -2289,28 +2449,28 @@ class MarkupCanvas {
|
|
|
2289
2449
|
}
|
|
2290
2450
|
// Theme management
|
|
2291
2451
|
updateThemeMode(mode) {
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
this.config
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
}
|
|
2452
|
+
this.config = createMarkupCanvasConfig({ ...this.config, themeMode: mode });
|
|
2453
|
+
updateThemeMode(this.canvas.container, this.config, this.rulers, mode);
|
|
2454
|
+
}
|
|
2455
|
+
toggleThemeMode() {
|
|
2456
|
+
const currentMode = this.config.themeMode;
|
|
2457
|
+
const newMode = currentMode === "light" ? "dark" : "light";
|
|
2458
|
+
this.updateThemeMode(newMode);
|
|
2459
|
+
return newMode;
|
|
2460
|
+
}
|
|
2461
|
+
// Transition management
|
|
2462
|
+
updateTransition(enabled) {
|
|
2463
|
+
this.config = createMarkupCanvasConfig({ ...this.config, enableTransition: enabled });
|
|
2464
|
+
}
|
|
2465
|
+
toggleTransitionMode() {
|
|
2466
|
+
const newEnableTransition = toggleTransition(this.config.enableTransition);
|
|
2467
|
+
this.updateTransition(newEnableTransition);
|
|
2468
|
+
return newEnableTransition;
|
|
2304
2469
|
}
|
|
2305
2470
|
// Cleanup method
|
|
2306
2471
|
cleanup() {
|
|
2307
|
-
this.
|
|
2308
|
-
|
|
2309
|
-
if (this.postMessageCleanup) {
|
|
2310
|
-
this.postMessageCleanup();
|
|
2311
|
-
this.postMessageCleanup = null;
|
|
2312
|
-
}
|
|
2313
|
-
this.cleanupFunctions.forEach((cleanup) => {
|
|
2472
|
+
cleanupWindowBinding(this.config);
|
|
2473
|
+
this.cleanupCallbacks.forEach((cleanup) => {
|
|
2314
2474
|
try {
|
|
2315
2475
|
cleanup();
|
|
2316
2476
|
}
|
|
@@ -2318,22 +2478,22 @@ class MarkupCanvas {
|
|
|
2318
2478
|
console.warn("Error during cleanup:", cleanupError);
|
|
2319
2479
|
}
|
|
2320
2480
|
});
|
|
2321
|
-
this.
|
|
2481
|
+
this.cleanupCallbacks = [];
|
|
2322
2482
|
// Remove all event listeners
|
|
2323
2483
|
this.removeAllListeners();
|
|
2324
2484
|
}
|
|
2325
2485
|
// Event emitter delegation methods
|
|
2326
2486
|
on(event, handler) {
|
|
2327
|
-
this.
|
|
2487
|
+
this.event.on(event, handler);
|
|
2328
2488
|
}
|
|
2329
2489
|
off(event, handler) {
|
|
2330
|
-
this.
|
|
2490
|
+
this.event.off(event, handler);
|
|
2331
2491
|
}
|
|
2332
2492
|
emit(event, data) {
|
|
2333
|
-
this.
|
|
2493
|
+
this.event.emit(event, data);
|
|
2334
2494
|
}
|
|
2335
2495
|
removeAllListeners() {
|
|
2336
|
-
this.
|
|
2496
|
+
this.event.removeAllListeners();
|
|
2337
2497
|
}
|
|
2338
2498
|
destroy() {
|
|
2339
2499
|
this.cleanup();
|