@markup-canvas/core 1.1.6 → 1.2.0
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 +10 -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 +5 -0
- 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 +715 -554
- package/dist/markup-canvas.esm.js +715 -554
- package/dist/markup-canvas.umd.js +711 -547
- package/dist/markup-canvas.umd.min.js +1 -1
- package/dist/types/canvas.d.ts +1 -47
- 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 +142 -308
- 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 +4 -8
- 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 +18 -38
- 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 +14 -0
- 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 -47
- 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/dist/lib/events/keyboard/setupKeyboardNavigation.d.ts +0 -2
- package/src/lib/canvas/config.ts +0 -29
- package/src/lib/canvas/getCanvasMethods.ts +0 -102
- package/src/lib/events/keyboard/setupKeyboardNavigation.ts +0 -115
- /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.
|
|
4
|
+
* @version 1.2.0
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
@@ -9,68 +9,6 @@
|
|
|
9
9
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.MarkupCanvas = factory());
|
|
10
10
|
})(this, (function () { 'use strict';
|
|
11
11
|
|
|
12
|
-
// Default transform values
|
|
13
|
-
const DEFAULT_ZOOM = 1.0;
|
|
14
|
-
// Validation thresholds
|
|
15
|
-
const ZOOM_CHANGE_THRESHOLD = 0.001;
|
|
16
|
-
// CSS transition values
|
|
17
|
-
const FALLBACK_TRANSITION_DURATION = 0.2;
|
|
18
|
-
// Zoom to fit padding factor
|
|
19
|
-
const ZOOM_FIT_PADDING = 0.9;
|
|
20
|
-
// CSS class names
|
|
21
|
-
const CANVAS_CONTAINER_CLASS = "canvas-container";
|
|
22
|
-
const TRANSFORM_LAYER_CLASS = "transform-layer";
|
|
23
|
-
const CONTENT_LAYER_CLASS = "content-layer";
|
|
24
|
-
|
|
25
|
-
function moveExistingContent(existingContent, contentLayer, transformLayer) {
|
|
26
|
-
existingContent.forEach((child) => {
|
|
27
|
-
if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
|
|
28
|
-
contentLayer.appendChild(child);
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function setupContentLayer(contentLayer) {
|
|
34
|
-
contentLayer.style.position = "relative";
|
|
35
|
-
contentLayer.style.width = "100%";
|
|
36
|
-
contentLayer.style.height = "100%";
|
|
37
|
-
contentLayer.style.pointerEvents = "auto";
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Sets up the transform layer with proper styles and dimensions
|
|
41
|
-
function setupTransformLayer(transformLayer, config) {
|
|
42
|
-
transformLayer.style.position = "absolute";
|
|
43
|
-
const rulerOffset = config.rulerSize;
|
|
44
|
-
transformLayer.style.top = `${rulerOffset}px`;
|
|
45
|
-
transformLayer.style.left = `${rulerOffset}px`;
|
|
46
|
-
transformLayer.style.width = `${config.width}px`;
|
|
47
|
-
transformLayer.style.height = `${config.height}px`;
|
|
48
|
-
transformLayer.style.transformOrigin = "0 0";
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function createCanvasLayers(container, config) {
|
|
52
|
-
const existingContent = Array.from(container.children);
|
|
53
|
-
// Create or find transform layer
|
|
54
|
-
let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
|
|
55
|
-
if (!transformLayer) {
|
|
56
|
-
transformLayer = document.createElement("div");
|
|
57
|
-
transformLayer.className = TRANSFORM_LAYER_CLASS;
|
|
58
|
-
container.appendChild(transformLayer);
|
|
59
|
-
}
|
|
60
|
-
setupTransformLayer(transformLayer, config);
|
|
61
|
-
// Create or find content layer
|
|
62
|
-
let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
|
|
63
|
-
if (!contentLayer) {
|
|
64
|
-
contentLayer = document.createElement("div");
|
|
65
|
-
contentLayer.className = CONTENT_LAYER_CLASS;
|
|
66
|
-
transformLayer.appendChild(contentLayer);
|
|
67
|
-
moveExistingContent(existingContent, contentLayer, transformLayer);
|
|
68
|
-
}
|
|
69
|
-
// Set content layer properties
|
|
70
|
-
setupContentLayer(contentLayer);
|
|
71
|
-
return { transformLayer, contentLayer };
|
|
72
|
-
}
|
|
73
|
-
|
|
74
12
|
function canvasToContent(canvasX, canvasY, matrix) {
|
|
75
13
|
if (!matrix?.inverse) {
|
|
76
14
|
return { x: canvasX, y: canvasY };
|
|
@@ -98,6 +36,19 @@
|
|
|
98
36
|
return new DOMMatrix([scale, 0, 0, scale, translateX, translateY]);
|
|
99
37
|
}
|
|
100
38
|
|
|
39
|
+
// Default transform values
|
|
40
|
+
const DEFAULT_ZOOM = 1.0;
|
|
41
|
+
// Validation thresholds
|
|
42
|
+
const ZOOM_CHANGE_THRESHOLD = 0.001;
|
|
43
|
+
// CSS transition values
|
|
44
|
+
const FALLBACK_TRANSITION_DURATION = 0.2;
|
|
45
|
+
// Zoom to fit padding factor
|
|
46
|
+
const ZOOM_FIT_PADDING = 0.9;
|
|
47
|
+
// CSS class names
|
|
48
|
+
const CANVAS_CONTAINER_CLASS = "canvas-container";
|
|
49
|
+
const TRANSFORM_LAYER_CLASS = "transform-layer";
|
|
50
|
+
const CONTENT_LAYER_CLASS = "content-layer";
|
|
51
|
+
|
|
101
52
|
function getZoomToMouseTransform(mouseX, mouseY, currentTransform, zoomFactor, config) {
|
|
102
53
|
const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
|
|
103
54
|
const transform = currentTransform || {
|
|
@@ -159,6 +110,16 @@
|
|
|
159
110
|
};
|
|
160
111
|
}
|
|
161
112
|
|
|
113
|
+
function getVisibleArea(canvas) {
|
|
114
|
+
const bounds = canvas.getBounds();
|
|
115
|
+
return bounds.visibleArea;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function isPointVisible(canvas, x, y) {
|
|
119
|
+
const visibleArea = getVisibleArea(canvas);
|
|
120
|
+
return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
|
|
121
|
+
}
|
|
122
|
+
|
|
162
123
|
function withClampedZoom(config, operation) {
|
|
163
124
|
const clampFunction = (scale) => clampZoom(scale, config);
|
|
164
125
|
return operation(clampFunction);
|
|
@@ -214,13 +175,6 @@
|
|
|
214
175
|
const finalRulerSize = hasRulers ? rulerSize : 0;
|
|
215
176
|
return operation(finalRulerSize);
|
|
216
177
|
}
|
|
217
|
-
function withRulerOffsets(canvas, rulerSize, x, y, operation) {
|
|
218
|
-
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
219
|
-
const adjustedX = x - rulerSize;
|
|
220
|
-
const adjustedY = y - rulerSize;
|
|
221
|
-
return operation(adjustedX, adjustedY);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
178
|
function withRulerOffsetObject(canvas, rulerSize, coords, operation) {
|
|
225
179
|
return withRulerSize(canvas, rulerSize, (rulerSize) => {
|
|
226
180
|
const adjusted = {
|
|
@@ -252,8 +206,7 @@
|
|
|
252
206
|
width: 8000,
|
|
253
207
|
height: 8000,
|
|
254
208
|
enableAcceleration: true,
|
|
255
|
-
// Global
|
|
256
|
-
bindToWindow: false,
|
|
209
|
+
// Global Instance Access
|
|
257
210
|
name: "markupCanvas",
|
|
258
211
|
enablePostMessageAPI: false,
|
|
259
212
|
// Interaction controls
|
|
@@ -261,7 +214,7 @@
|
|
|
261
214
|
enablePan: true,
|
|
262
215
|
enableTouch: true,
|
|
263
216
|
enableKeyboard: true,
|
|
264
|
-
bindKeyboardEventsTo: "
|
|
217
|
+
bindKeyboardEventsTo: "document",
|
|
265
218
|
// Zoom behavior
|
|
266
219
|
zoomSpeed: 1.5,
|
|
267
220
|
minZoom: 0.05,
|
|
@@ -307,14 +260,11 @@
|
|
|
307
260
|
gridColorDark: "rgba(232, 86, 193, 0.5)",
|
|
308
261
|
// Theme
|
|
309
262
|
themeMode: "light",
|
|
310
|
-
// Callbacks
|
|
311
|
-
onTransformUpdate: () => { },
|
|
312
263
|
};
|
|
313
264
|
|
|
314
|
-
function getCanvasBounds(canvas) {
|
|
265
|
+
function getCanvasBounds(canvas, config) {
|
|
315
266
|
try {
|
|
316
267
|
const container = canvas.container;
|
|
317
|
-
const config = canvas.config;
|
|
318
268
|
const transform = canvas.transform || {
|
|
319
269
|
scale: 1.0,
|
|
320
270
|
translateX: 0,
|
|
@@ -364,6 +314,116 @@
|
|
|
364
314
|
}
|
|
365
315
|
}
|
|
366
316
|
|
|
317
|
+
function disableTransition(element, config) {
|
|
318
|
+
try {
|
|
319
|
+
if (config.enableTransition) {
|
|
320
|
+
if (window.__markupCanvasTransitionTimeout) {
|
|
321
|
+
clearTimeout(window.__markupCanvasTransitionTimeout);
|
|
322
|
+
window.__markupCanvasTransitionTimeout = undefined;
|
|
323
|
+
}
|
|
324
|
+
const delay = (config.transitionDuration ?? FALLBACK_TRANSITION_DURATION) * 1000;
|
|
325
|
+
withDebounce("disableTransition", delay, () => {
|
|
326
|
+
element.style.transition = "none";
|
|
327
|
+
window.__markupCanvasTransitionTimeout = undefined;
|
|
328
|
+
});
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
catch (error) {
|
|
334
|
+
console.error("Failed to disable transitions:", error);
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
function enableTransition(element, config) {
|
|
340
|
+
try {
|
|
341
|
+
if (config.enableTransition) {
|
|
342
|
+
if (window.__markupCanvasTransitionTimeout) {
|
|
343
|
+
clearTimeout(window.__markupCanvasTransitionTimeout);
|
|
344
|
+
window.__markupCanvasTransitionTimeout = undefined;
|
|
345
|
+
}
|
|
346
|
+
element.style.transition = `transform ${config.transitionDuration}s linear`;
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
catch (error) {
|
|
352
|
+
console.error("Failed to enable transitions:", error);
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function withTransition(element, config, operation) {
|
|
358
|
+
enableTransition(element, config);
|
|
359
|
+
try {
|
|
360
|
+
const result = operation();
|
|
361
|
+
return result;
|
|
362
|
+
}
|
|
363
|
+
finally {
|
|
364
|
+
disableTransition(element, config);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function centerContent(canvas, config, updateTransformFn, transformLayer) {
|
|
369
|
+
return withTransition(transformLayer, config, () => {
|
|
370
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
371
|
+
const centerX = (bounds.width - bounds.contentWidth * canvas.transform.scale) / 2;
|
|
372
|
+
const centerY = (bounds.height - bounds.contentHeight * canvas.transform.scale) / 2;
|
|
373
|
+
return updateTransformFn({
|
|
374
|
+
translateX: centerX,
|
|
375
|
+
translateY: centerY,
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function panDown(canvas, config, updateTransform) {
|
|
381
|
+
const panDistance = config.keyboardPanStep;
|
|
382
|
+
const newTransform = {
|
|
383
|
+
translateY: canvas.transform.translateY - panDistance,
|
|
384
|
+
};
|
|
385
|
+
return updateTransform(newTransform);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function panLeft(canvas, config, updateTransform) {
|
|
389
|
+
const panDistance = config.keyboardPanStep;
|
|
390
|
+
const newTransform = {
|
|
391
|
+
translateX: canvas.transform.translateX + panDistance,
|
|
392
|
+
};
|
|
393
|
+
return updateTransform(newTransform);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function panRight(canvas, config, updateTransform) {
|
|
397
|
+
const panDistance = config.keyboardPanStep;
|
|
398
|
+
const newTransform = {
|
|
399
|
+
translateX: canvas.transform.translateX - panDistance,
|
|
400
|
+
};
|
|
401
|
+
return updateTransform(newTransform);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function panUp(canvas, config, updateTransform) {
|
|
405
|
+
const panDistance = config.keyboardPanStep;
|
|
406
|
+
const newTransform = {
|
|
407
|
+
translateY: canvas.transform.translateY + panDistance,
|
|
408
|
+
};
|
|
409
|
+
return updateTransform(newTransform);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function scrollToPoint(canvas, config, x, y, updateTransform, transformLayer) {
|
|
413
|
+
return withTransition(transformLayer, config, () => {
|
|
414
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
415
|
+
const centerX = bounds.width / 2;
|
|
416
|
+
const centerY = bounds.height / 2;
|
|
417
|
+
// Calculate new translation to center the point
|
|
418
|
+
const newTranslateX = centerX - x * canvas.transform.scale;
|
|
419
|
+
const newTranslateY = centerY - y * canvas.transform.scale;
|
|
420
|
+
return updateTransform({
|
|
421
|
+
translateX: newTranslateX,
|
|
422
|
+
translateY: newTranslateY,
|
|
423
|
+
});
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
367
427
|
function createMatrixString(matrix) {
|
|
368
428
|
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})`;
|
|
369
429
|
}
|
|
@@ -395,136 +455,296 @@
|
|
|
395
455
|
}
|
|
396
456
|
}
|
|
397
457
|
|
|
398
|
-
function
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
458
|
+
function updateTransform(canvas, newTransform) {
|
|
459
|
+
canvas.transform = { ...canvas.transform, ...newTransform };
|
|
460
|
+
const matrix = createMatrix(canvas.transform.scale, canvas.transform.translateX, canvas.transform.translateY);
|
|
461
|
+
return applyTransform(canvas.transformLayer, matrix);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function resetTransform(canvas) {
|
|
465
|
+
const resetTransformData = {
|
|
466
|
+
scale: 1.0,
|
|
467
|
+
translateX: 0,
|
|
468
|
+
translateY: 0,
|
|
469
|
+
};
|
|
470
|
+
return updateTransform(canvas, resetTransformData);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function hideGrid(rulers) {
|
|
474
|
+
if (rulers?.gridOverlay) {
|
|
475
|
+
rulers.gridOverlay.style.display = "none";
|
|
476
|
+
return true;
|
|
413
477
|
}
|
|
414
|
-
|
|
415
|
-
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function isGridVisible(rulers) {
|
|
482
|
+
if (rulers?.gridOverlay) {
|
|
483
|
+
return rulers.gridOverlay.style.display !== "none";
|
|
484
|
+
}
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function showGrid(rulers) {
|
|
489
|
+
if (rulers?.gridOverlay) {
|
|
490
|
+
rulers.gridOverlay.style.display = "block";
|
|
416
491
|
return true;
|
|
417
492
|
}
|
|
493
|
+
return false;
|
|
418
494
|
}
|
|
419
495
|
|
|
420
|
-
function
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
496
|
+
function toggleGrid(rulers) {
|
|
497
|
+
if (rulers?.toggleGrid) {
|
|
498
|
+
rulers.toggleGrid();
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function areRulersVisible(rulers) {
|
|
505
|
+
if (rulers?.horizontalRuler) {
|
|
506
|
+
return rulers.horizontalRuler.style.display !== "none";
|
|
507
|
+
}
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function hideRulers(rulers) {
|
|
512
|
+
if (rulers) {
|
|
513
|
+
rulers.hide();
|
|
514
|
+
return true;
|
|
515
|
+
}
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function showRulers(rulers) {
|
|
520
|
+
if (rulers) {
|
|
521
|
+
rulers.show();
|
|
522
|
+
return true;
|
|
523
|
+
}
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function toggleRulers(rulers, areRulersVisible) {
|
|
528
|
+
if (rulers) {
|
|
529
|
+
const isVisible = areRulersVisible();
|
|
530
|
+
if (isVisible) {
|
|
531
|
+
rulers.hide();
|
|
429
532
|
}
|
|
430
|
-
|
|
533
|
+
else {
|
|
534
|
+
rulers.show();
|
|
535
|
+
}
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function toggleTransition(enableTransition) {
|
|
542
|
+
return !enableTransition;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function createMarkupCanvasConfig(options = {}) {
|
|
546
|
+
const config = {
|
|
547
|
+
...DEFAULT_CONFIG,
|
|
548
|
+
...options,
|
|
549
|
+
};
|
|
550
|
+
if (typeof config.width !== "number" || config.width <= 0) {
|
|
551
|
+
console.warn("Invalid width, using default");
|
|
552
|
+
config.width = DEFAULT_CONFIG.width;
|
|
553
|
+
}
|
|
554
|
+
if (typeof config.height !== "number" || config.height <= 0) {
|
|
555
|
+
console.warn("Invalid height, using default");
|
|
556
|
+
config.height = DEFAULT_CONFIG.height;
|
|
557
|
+
}
|
|
558
|
+
if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
|
|
559
|
+
console.warn("Invalid zoomSpeed, using default");
|
|
560
|
+
config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
|
|
561
|
+
}
|
|
562
|
+
if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
|
|
563
|
+
console.warn("Invalid minZoom, using default");
|
|
564
|
+
config.minZoom = DEFAULT_CONFIG.minZoom;
|
|
565
|
+
}
|
|
566
|
+
if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
|
|
567
|
+
console.warn("Invalid maxZoom, using default");
|
|
568
|
+
config.maxZoom = DEFAULT_CONFIG.maxZoom;
|
|
569
|
+
}
|
|
570
|
+
if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
|
|
571
|
+
console.warn("Invalid keyboardPanStep, using default");
|
|
572
|
+
config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
|
|
573
|
+
}
|
|
574
|
+
if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
|
|
575
|
+
console.warn("Invalid keyboardFastMultiplier, using default");
|
|
576
|
+
config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
|
|
577
|
+
}
|
|
578
|
+
if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
|
|
579
|
+
console.warn("Invalid clickZoomLevel, using default");
|
|
580
|
+
config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
|
|
581
|
+
}
|
|
582
|
+
if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
|
|
583
|
+
console.warn("Invalid rulerFontSize, using default");
|
|
584
|
+
config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
|
|
585
|
+
}
|
|
586
|
+
if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
|
|
587
|
+
console.warn("Invalid rulerSize, using default");
|
|
588
|
+
config.rulerSize = DEFAULT_CONFIG.rulerSize;
|
|
589
|
+
}
|
|
590
|
+
return config;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function updateThemeMode(canvasContainer, config, rulers, mode) {
|
|
594
|
+
const newConfig = {
|
|
595
|
+
...config,
|
|
596
|
+
themeMode: mode,
|
|
597
|
+
};
|
|
598
|
+
const updatedConfig = createMarkupCanvasConfig(newConfig);
|
|
599
|
+
// Update canvas background color
|
|
600
|
+
const backgroundColor = getThemeValue(updatedConfig, "canvasBackgroundColor");
|
|
601
|
+
canvasContainer.style.backgroundColor = backgroundColor;
|
|
602
|
+
// Update rulers if they exist
|
|
603
|
+
if (rulers) {
|
|
604
|
+
rulers.updateTheme(updatedConfig);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function resetView(canvas, transformLayer, config) {
|
|
609
|
+
return withTransition(transformLayer, config, () => {
|
|
610
|
+
return withRulerSize(canvas, config.rulerSize, (rulerSize) => {
|
|
611
|
+
const resetTransformData = {
|
|
612
|
+
scale: 1.0,
|
|
613
|
+
translateX: rulerSize * -1,
|
|
614
|
+
translateY: rulerSize * -1,
|
|
615
|
+
};
|
|
616
|
+
return updateTransform(canvas, resetTransformData);
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function getViewportCenter(canvas) {
|
|
622
|
+
try {
|
|
623
|
+
const bounds = canvas.getBounds();
|
|
624
|
+
return {
|
|
625
|
+
x: bounds.width / 2,
|
|
626
|
+
y: bounds.height / 2,
|
|
627
|
+
};
|
|
431
628
|
}
|
|
432
629
|
catch (error) {
|
|
433
|
-
console.
|
|
434
|
-
return
|
|
630
|
+
console.warn("Failed to calculate viewport center:", error);
|
|
631
|
+
return { x: 0, y: 0 };
|
|
435
632
|
}
|
|
436
633
|
}
|
|
437
634
|
|
|
438
|
-
function
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
635
|
+
function resetViewToCenter(canvas, transformLayer, config, zoomToPoint) {
|
|
636
|
+
return withTransition(transformLayer, config, () => {
|
|
637
|
+
return withClampedZoom(config, (clamp) => {
|
|
638
|
+
const newScale = clamp(1.0);
|
|
639
|
+
const center = getViewportCenter(canvas);
|
|
640
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
641
|
+
});
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function setZoom(canvas, transformLayer, config, zoomToPoint, zoomLevel) {
|
|
646
|
+
return withTransition(transformLayer, config, () => {
|
|
647
|
+
return withClampedZoom(config, (clamp) => {
|
|
648
|
+
const newScale = clamp(zoomLevel);
|
|
649
|
+
const center = getViewportCenter(canvas);
|
|
650
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
651
|
+
});
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
function zoomIn(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
|
|
656
|
+
return withTransition(transformLayer, config, () => {
|
|
657
|
+
return withClampedZoom(config, (clamp) => {
|
|
658
|
+
const newScale = clamp(canvas.transform.scale * (1 + factor));
|
|
659
|
+
const center = getViewportCenter(canvas);
|
|
660
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function zoomOut(canvas, transformLayer, config, zoomToPoint, factor = 0.5) {
|
|
666
|
+
return withTransition(transformLayer, config, () => {
|
|
667
|
+
return withClampedZoom(config, (clamp) => {
|
|
668
|
+
const newScale = clamp(canvas.transform.scale * (1 - factor));
|
|
669
|
+
const center = getViewportCenter(canvas);
|
|
670
|
+
return zoomToPoint(center.x, center.y, newScale);
|
|
671
|
+
});
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function zoomToPoint(canvas, transformLayer, config, x, y, targetScale) {
|
|
676
|
+
return withTransition(transformLayer, config, () => {
|
|
677
|
+
const newTransform = getZoomToMouseTransform(x, y, canvas.transform, targetScale / canvas.transform.scale, config);
|
|
678
|
+
return updateTransform(canvas, newTransform);
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function fitToScreen(canvas, transformLayer, config) {
|
|
683
|
+
return withTransition(transformLayer, config, () => {
|
|
684
|
+
const bounds = getCanvasBounds(canvas, config);
|
|
685
|
+
const scaleX = bounds.width / config.width;
|
|
686
|
+
const scaleY = bounds.height / config.height;
|
|
687
|
+
const fitScale = withClampedZoom(config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
|
|
688
|
+
// Center the content
|
|
689
|
+
const scaledWidth = config.width * fitScale;
|
|
690
|
+
const scaledHeight = config.height * fitScale;
|
|
691
|
+
const centerX = (bounds.width - scaledWidth) / 2;
|
|
692
|
+
const centerY = (bounds.height - scaledHeight) / 2;
|
|
693
|
+
return updateTransform(canvas, {
|
|
694
|
+
scale: fitScale,
|
|
695
|
+
translateX: centerX,
|
|
696
|
+
translateY: centerY,
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function moveExistingContent(existingContent, contentLayer, transformLayer) {
|
|
702
|
+
existingContent.forEach((child) => {
|
|
703
|
+
if (child !== transformLayer && !child.classList.contains(TRANSFORM_LAYER_CLASS)) {
|
|
704
|
+
contentLayer.appendChild(child);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
function setupContentLayer(contentLayer) {
|
|
710
|
+
contentLayer.style.position = "relative";
|
|
711
|
+
contentLayer.style.width = "100%";
|
|
712
|
+
contentLayer.style.height = "100%";
|
|
713
|
+
contentLayer.style.pointerEvents = "auto";
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// Sets up the transform layer with proper styles and dimensions
|
|
717
|
+
function setupTransformLayer(transformLayer, config) {
|
|
718
|
+
transformLayer.style.position = "absolute";
|
|
719
|
+
const rulerOffset = config.rulerSize;
|
|
720
|
+
transformLayer.style.top = `${rulerOffset}px`;
|
|
721
|
+
transformLayer.style.left = `${rulerOffset}px`;
|
|
722
|
+
transformLayer.style.width = `${config.width}px`;
|
|
723
|
+
transformLayer.style.height = `${config.height}px`;
|
|
724
|
+
transformLayer.style.transformOrigin = "0 0";
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function createCanvasLayers(container, config) {
|
|
728
|
+
const existingContent = Array.from(container.children);
|
|
729
|
+
// Create or find transform layer
|
|
730
|
+
let transformLayer = container.querySelector(`.${TRANSFORM_LAYER_CLASS}`);
|
|
731
|
+
if (!transformLayer) {
|
|
732
|
+
transformLayer = document.createElement("div");
|
|
733
|
+
transformLayer.className = TRANSFORM_LAYER_CLASS;
|
|
734
|
+
container.appendChild(transformLayer);
|
|
443
735
|
}
|
|
444
|
-
|
|
445
|
-
|
|
736
|
+
setupTransformLayer(transformLayer, config);
|
|
737
|
+
// Create or find content layer
|
|
738
|
+
let contentLayer = transformLayer.querySelector(`.${CONTENT_LAYER_CLASS}`);
|
|
739
|
+
if (!contentLayer) {
|
|
740
|
+
contentLayer = document.createElement("div");
|
|
741
|
+
contentLayer.className = CONTENT_LAYER_CLASS;
|
|
742
|
+
transformLayer.appendChild(contentLayer);
|
|
743
|
+
moveExistingContent(existingContent, contentLayer, transformLayer);
|
|
446
744
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
return {
|
|
451
|
-
// Utility methods
|
|
452
|
-
getBounds: function () {
|
|
453
|
-
return getCanvasBounds(this);
|
|
454
|
-
},
|
|
455
|
-
// Transform methods
|
|
456
|
-
updateTransform: function (newTransform) {
|
|
457
|
-
this.transform = { ...this.transform, ...newTransform };
|
|
458
|
-
const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
|
|
459
|
-
const result = applyTransform(this.transformLayer, matrix);
|
|
460
|
-
withFeatureEnabled(this.config, "onTransformUpdate", () => {
|
|
461
|
-
this.config.onTransformUpdate(this.transform);
|
|
462
|
-
});
|
|
463
|
-
return result;
|
|
464
|
-
},
|
|
465
|
-
// Reset method
|
|
466
|
-
reset: function () {
|
|
467
|
-
const resetTransform = {
|
|
468
|
-
scale: 1.0,
|
|
469
|
-
translateX: 0,
|
|
470
|
-
translateY: 0,
|
|
471
|
-
};
|
|
472
|
-
return this.updateTransform(resetTransform);
|
|
473
|
-
},
|
|
474
|
-
// Handle canvas resize
|
|
475
|
-
handleResize: function () {
|
|
476
|
-
return true;
|
|
477
|
-
},
|
|
478
|
-
// Set zoom level
|
|
479
|
-
setZoom: function (zoomLevel) {
|
|
480
|
-
const newScale = withClampedZoom(this.config, (clamp) => clamp(zoomLevel));
|
|
481
|
-
return this.updateTransform({ scale: newScale });
|
|
482
|
-
},
|
|
483
|
-
// Convert canvas coordinates to content coordinates
|
|
484
|
-
canvasToContent: function (x, y) {
|
|
485
|
-
const matrix = createMatrix(this.transform.scale, this.transform.translateX, this.transform.translateY);
|
|
486
|
-
return canvasToContent(x, y, matrix);
|
|
487
|
-
},
|
|
488
|
-
// Zoom to a specific point with animation
|
|
489
|
-
zoomToPoint: function (x, y, targetScale) {
|
|
490
|
-
return withTransition(this.transformLayer, this.config, () => {
|
|
491
|
-
const newTransform = getZoomToMouseTransform(x, y, this.transform, targetScale / this.transform.scale, this.config);
|
|
492
|
-
return this.updateTransform(newTransform);
|
|
493
|
-
});
|
|
494
|
-
},
|
|
495
|
-
// Reset view with animation
|
|
496
|
-
resetView: function () {
|
|
497
|
-
return withTransition(this.transformLayer, this.config, () => {
|
|
498
|
-
return withRulerSize(this, this.config.rulerSize, (rulerSize) => {
|
|
499
|
-
const resetTransform = {
|
|
500
|
-
scale: 1.0,
|
|
501
|
-
translateX: rulerSize * -1,
|
|
502
|
-
translateY: rulerSize * -1,
|
|
503
|
-
};
|
|
504
|
-
return this.updateTransform(resetTransform);
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
},
|
|
508
|
-
// Zoom to fit content in canvas
|
|
509
|
-
zoomToFitContent: function () {
|
|
510
|
-
return withTransition(this.transformLayer, this.config, () => {
|
|
511
|
-
const bounds = this.getBounds();
|
|
512
|
-
const scaleX = bounds.width / this.config.width;
|
|
513
|
-
const scaleY = bounds.height / this.config.height;
|
|
514
|
-
const fitScale = withClampedZoom(this.config, (clamp) => clamp(Math.min(scaleX, scaleY) * ZOOM_FIT_PADDING));
|
|
515
|
-
// Center the content
|
|
516
|
-
const scaledWidth = this.config.width * fitScale;
|
|
517
|
-
const scaledHeight = this.config.height * fitScale;
|
|
518
|
-
const centerX = (bounds.width - scaledWidth) / 2;
|
|
519
|
-
const centerY = (bounds.height - scaledHeight) / 2;
|
|
520
|
-
return this.updateTransform({
|
|
521
|
-
scale: fitScale,
|
|
522
|
-
translateX: centerX,
|
|
523
|
-
translateY: centerY,
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
|
-
},
|
|
527
|
-
};
|
|
745
|
+
// Set content layer properties
|
|
746
|
+
setupContentLayer(contentLayer);
|
|
747
|
+
return { transformLayer, contentLayer };
|
|
528
748
|
}
|
|
529
749
|
|
|
530
750
|
function checkContainerDimensions(container) {
|
|
@@ -560,7 +780,6 @@
|
|
|
560
780
|
}
|
|
561
781
|
}
|
|
562
782
|
|
|
563
|
-
// Creates and initializes a canvas with the required DOM structure
|
|
564
783
|
function createCanvas(container, config) {
|
|
565
784
|
if (!container?.appendChild) {
|
|
566
785
|
console.error("Invalid container element provided to createCanvas");
|
|
@@ -569,10 +788,9 @@
|
|
|
569
788
|
try {
|
|
570
789
|
setupCanvasContainer(container, config);
|
|
571
790
|
const { transformLayer, contentLayer } = createCanvasLayers(container, config);
|
|
572
|
-
|
|
573
|
-
if (config.enableAcceleration) {
|
|
791
|
+
withFeatureEnabled(config, "enableAcceleration", () => {
|
|
574
792
|
enableHardwareAcceleration(transformLayer);
|
|
575
|
-
}
|
|
793
|
+
});
|
|
576
794
|
const rulerOffset = config.enableRulers ? -config.rulerSize : 0;
|
|
577
795
|
const initialTransform = {
|
|
578
796
|
scale: DEFAULT_ZOOM,
|
|
@@ -587,12 +805,8 @@
|
|
|
587
805
|
container,
|
|
588
806
|
transformLayer,
|
|
589
807
|
contentLayer,
|
|
590
|
-
// Configuration
|
|
591
|
-
config: config,
|
|
592
808
|
// Current state
|
|
593
809
|
transform: initialTransform,
|
|
594
|
-
// Add all canvas methods
|
|
595
|
-
...getCanvasMethods(),
|
|
596
810
|
};
|
|
597
811
|
return canvas;
|
|
598
812
|
}
|
|
@@ -602,54 +816,6 @@
|
|
|
602
816
|
}
|
|
603
817
|
}
|
|
604
818
|
|
|
605
|
-
function createMarkupCanvasConfig(options = {}) {
|
|
606
|
-
const config = {
|
|
607
|
-
...DEFAULT_CONFIG,
|
|
608
|
-
...options,
|
|
609
|
-
};
|
|
610
|
-
if (typeof config.width !== "number" || config.width <= 0) {
|
|
611
|
-
console.warn("Invalid width, using default");
|
|
612
|
-
config.width = DEFAULT_CONFIG.width;
|
|
613
|
-
}
|
|
614
|
-
if (typeof config.height !== "number" || config.height <= 0) {
|
|
615
|
-
console.warn("Invalid height, using default");
|
|
616
|
-
config.height = DEFAULT_CONFIG.height;
|
|
617
|
-
}
|
|
618
|
-
if (typeof config.zoomSpeed !== "number" || config.zoomSpeed <= 0) {
|
|
619
|
-
console.warn("Invalid zoomSpeed, using default");
|
|
620
|
-
config.zoomSpeed = DEFAULT_CONFIG.zoomSpeed;
|
|
621
|
-
}
|
|
622
|
-
if (typeof config.minZoom !== "number" || config.minZoom <= 0) {
|
|
623
|
-
console.warn("Invalid minZoom, using default");
|
|
624
|
-
config.minZoom = DEFAULT_CONFIG.minZoom;
|
|
625
|
-
}
|
|
626
|
-
if (typeof config.maxZoom !== "number" || config.maxZoom <= config.minZoom) {
|
|
627
|
-
console.warn("Invalid maxZoom, using default");
|
|
628
|
-
config.maxZoom = DEFAULT_CONFIG.maxZoom;
|
|
629
|
-
}
|
|
630
|
-
if (typeof config.keyboardPanStep !== "number" || config.keyboardPanStep <= 0) {
|
|
631
|
-
console.warn("Invalid keyboardPanStep, using default");
|
|
632
|
-
config.keyboardPanStep = DEFAULT_CONFIG.keyboardPanStep;
|
|
633
|
-
}
|
|
634
|
-
if (typeof config.keyboardFastMultiplier !== "number" || config.keyboardFastMultiplier <= 0) {
|
|
635
|
-
console.warn("Invalid keyboardFastMultiplier, using default");
|
|
636
|
-
config.keyboardFastMultiplier = DEFAULT_CONFIG.keyboardFastMultiplier;
|
|
637
|
-
}
|
|
638
|
-
if (typeof config.clickZoomLevel !== "number" || config.clickZoomLevel <= 0) {
|
|
639
|
-
console.warn("Invalid clickZoomLevel, using default");
|
|
640
|
-
config.clickZoomLevel = DEFAULT_CONFIG.clickZoomLevel;
|
|
641
|
-
}
|
|
642
|
-
if (typeof config.rulerFontSize !== "number" || config.rulerFontSize <= 0) {
|
|
643
|
-
console.warn("Invalid rulerFontSize, using default");
|
|
644
|
-
config.rulerFontSize = DEFAULT_CONFIG.rulerFontSize;
|
|
645
|
-
}
|
|
646
|
-
if (typeof config.rulerSize !== "number" || config.rulerSize <= 0) {
|
|
647
|
-
console.warn("Invalid rulerSize, using default");
|
|
648
|
-
config.rulerSize = DEFAULT_CONFIG.rulerSize;
|
|
649
|
-
}
|
|
650
|
-
return config;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
819
|
class EventEmitter {
|
|
654
820
|
constructor() {
|
|
655
821
|
this.listeners = new Map();
|
|
@@ -688,6 +854,13 @@
|
|
|
688
854
|
}
|
|
689
855
|
}
|
|
690
856
|
|
|
857
|
+
function emitTransformEvents(listen, canvas) {
|
|
858
|
+
const transform = canvas.transform;
|
|
859
|
+
listen.emit("transform", transform);
|
|
860
|
+
listen.emit("zoom", transform.scale);
|
|
861
|
+
listen.emit("pan", { x: transform.translateX, y: transform.translateY });
|
|
862
|
+
}
|
|
863
|
+
|
|
691
864
|
const REFERENCE_DISPLAY_AREA = 1920 * 1080;
|
|
692
865
|
const TRACKPAD_PINCH_SPEED_FACTOR = 0.05;
|
|
693
866
|
const ADAPTIVE_ZOOM_FACTOR = 1;
|
|
@@ -714,42 +887,28 @@
|
|
|
714
887
|
}
|
|
715
888
|
|
|
716
889
|
function setupKeyboardEvents(canvas, config) {
|
|
717
|
-
// Track mouse position
|
|
718
|
-
let lastMouseX = 0;
|
|
719
|
-
let lastMouseY = 0;
|
|
720
|
-
function handleMouseMove(event) {
|
|
721
|
-
const rect = canvas.container.getBoundingClientRect();
|
|
722
|
-
const rawMouseX = event.clientX - rect.left;
|
|
723
|
-
const rawMouseY = event.clientY - rect.top;
|
|
724
|
-
withRulerOffsets(canvas, config.rulerSize, rawMouseX, rawMouseY, (adjustedX, adjustedY) => {
|
|
725
|
-
lastMouseX = adjustedX;
|
|
726
|
-
lastMouseY = adjustedY;
|
|
727
|
-
});
|
|
728
|
-
}
|
|
729
890
|
function handleKeyDown(event) {
|
|
730
891
|
if (!(event instanceof KeyboardEvent))
|
|
731
892
|
return;
|
|
732
893
|
if (config.bindKeyboardEventsTo === "canvas" && document.activeElement !== canvas.container)
|
|
733
894
|
return;
|
|
734
|
-
const isFastPan = event.shiftKey;
|
|
735
|
-
const panDistance = config.keyboardPanStep * (isFastPan ? config.keyboardFastMultiplier : 1);
|
|
736
895
|
let handled = false;
|
|
737
896
|
const newTransform = {};
|
|
738
897
|
switch (event.key) {
|
|
739
898
|
case "ArrowLeft":
|
|
740
|
-
newTransform.translateX = canvas.transform.translateX +
|
|
899
|
+
newTransform.translateX = canvas.transform.translateX + config.keyboardPanStep;
|
|
741
900
|
handled = true;
|
|
742
901
|
break;
|
|
743
902
|
case "ArrowRight":
|
|
744
|
-
newTransform.translateX = canvas.transform.translateX -
|
|
903
|
+
newTransform.translateX = canvas.transform.translateX - config.keyboardPanStep;
|
|
745
904
|
handled = true;
|
|
746
905
|
break;
|
|
747
906
|
case "ArrowUp":
|
|
748
|
-
newTransform.translateY = canvas.transform.translateY +
|
|
907
|
+
newTransform.translateY = canvas.transform.translateY + config.keyboardPanStep;
|
|
749
908
|
handled = true;
|
|
750
909
|
break;
|
|
751
910
|
case "ArrowDown":
|
|
752
|
-
newTransform.translateY = canvas.transform.translateY -
|
|
911
|
+
newTransform.translateY = canvas.transform.translateY - config.keyboardPanStep;
|
|
753
912
|
handled = true;
|
|
754
913
|
break;
|
|
755
914
|
case "=":
|
|
@@ -758,7 +917,7 @@
|
|
|
758
917
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
759
918
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
760
919
|
: config.keyboardZoomStep;
|
|
761
|
-
|
|
920
|
+
canvas.zoomIn(adaptiveZoomStep);
|
|
762
921
|
handled = true;
|
|
763
922
|
}
|
|
764
923
|
break;
|
|
@@ -767,16 +926,21 @@
|
|
|
767
926
|
const adaptiveZoomStep = config.enableAdaptiveSpeed
|
|
768
927
|
? getAdaptiveZoomSpeed(canvas, config.keyboardZoomStep)
|
|
769
928
|
: config.keyboardZoomStep;
|
|
770
|
-
|
|
929
|
+
canvas.zoomOut(adaptiveZoomStep);
|
|
771
930
|
handled = true;
|
|
772
931
|
}
|
|
773
932
|
break;
|
|
774
933
|
case "0":
|
|
775
|
-
if (event.
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
934
|
+
if (event.ctrlKey) {
|
|
935
|
+
if (canvas.resetView) {
|
|
936
|
+
canvas.resetView();
|
|
937
|
+
}
|
|
938
|
+
handled = true;
|
|
939
|
+
}
|
|
940
|
+
else if (event.metaKey || event.ctrlKey) {
|
|
941
|
+
if (canvas.resetViewToCenter) {
|
|
942
|
+
canvas.resetViewToCenter();
|
|
943
|
+
}
|
|
780
944
|
handled = true;
|
|
781
945
|
}
|
|
782
946
|
break;
|
|
@@ -804,10 +968,8 @@
|
|
|
804
968
|
}
|
|
805
969
|
const keyboardTarget = config.bindKeyboardEventsTo === "canvas" ? canvas.container : document;
|
|
806
970
|
keyboardTarget.addEventListener("keydown", handleKeyDown);
|
|
807
|
-
canvas.container.addEventListener("mousemove", handleMouseMove);
|
|
808
971
|
return () => {
|
|
809
972
|
keyboardTarget.removeEventListener("keydown", handleKeyDown);
|
|
810
|
-
canvas.container.removeEventListener("mousemove", handleMouseMove);
|
|
811
973
|
};
|
|
812
974
|
}
|
|
813
975
|
|
|
@@ -1193,6 +1355,17 @@
|
|
|
1193
1355
|
const newMode = currentConfig.themeMode === "light" ? "dark" : "light";
|
|
1194
1356
|
canvas.updateThemeMode(newMode);
|
|
1195
1357
|
}
|
|
1358
|
+
// Transition methods
|
|
1359
|
+
else if (action === "updateTransition") {
|
|
1360
|
+
const enabled = args[0];
|
|
1361
|
+
if (typeof enabled !== "boolean") {
|
|
1362
|
+
throw new Error(`Invalid transition enabled value: ${enabled}. Must be a boolean.`);
|
|
1363
|
+
}
|
|
1364
|
+
canvas.updateTransition(enabled);
|
|
1365
|
+
}
|
|
1366
|
+
else if (action === "toggleTransitionMode") {
|
|
1367
|
+
canvas.toggleTransitionMode();
|
|
1368
|
+
}
|
|
1196
1369
|
else {
|
|
1197
1370
|
throw new Error(`Unknown action: ${action}`);
|
|
1198
1371
|
}
|
|
@@ -1790,8 +1963,6 @@
|
|
|
1790
1963
|
elements.verticalRuler.style.display = "block";
|
|
1791
1964
|
if (elements.cornerBox)
|
|
1792
1965
|
elements.cornerBox.style.display = "flex";
|
|
1793
|
-
if (elements.gridOverlay)
|
|
1794
|
-
elements.gridOverlay.style.display = "block";
|
|
1795
1966
|
},
|
|
1796
1967
|
hide: () => {
|
|
1797
1968
|
if (elements.horizontalRuler)
|
|
@@ -1835,14 +2006,163 @@
|
|
|
1835
2006
|
}
|
|
1836
2007
|
}
|
|
1837
2008
|
|
|
2009
|
+
function broadcastEvent(event, data, config) {
|
|
2010
|
+
if (typeof window === "undefined") {
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
// Receivers can get the instance from the window binding
|
|
2014
|
+
let broadcastData = data;
|
|
2015
|
+
if (event === "ready") {
|
|
2016
|
+
broadcastData = { ready: true };
|
|
2017
|
+
}
|
|
2018
|
+
window.postMessage({
|
|
2019
|
+
source: "markup-canvas",
|
|
2020
|
+
event,
|
|
2021
|
+
data: broadcastData,
|
|
2022
|
+
timestamp: Date.now(),
|
|
2023
|
+
canvasName: config.name,
|
|
2024
|
+
}, "*");
|
|
2025
|
+
if (window.parent) {
|
|
2026
|
+
window.parent.postMessage({
|
|
2027
|
+
source: "markup-canvas",
|
|
2028
|
+
event,
|
|
2029
|
+
data: broadcastData,
|
|
2030
|
+
timestamp: Date.now(),
|
|
2031
|
+
canvasName: config.name,
|
|
2032
|
+
}, "*");
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
function cleanupWindowBinding(config) {
|
|
2037
|
+
if (typeof window === "undefined") {
|
|
2038
|
+
return;
|
|
2039
|
+
}
|
|
2040
|
+
const canvasName = config.name || "markupCanvas";
|
|
2041
|
+
const windowObj = window;
|
|
2042
|
+
delete windowObj[canvasName];
|
|
2043
|
+
if (windowObj.__markupCanvasInstances) {
|
|
2044
|
+
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
function bindCanvasToWindow(canvas, config) {
|
|
2049
|
+
if (typeof window === "undefined") {
|
|
2050
|
+
return;
|
|
2051
|
+
}
|
|
2052
|
+
const canvasName = config.name || "markupCanvas";
|
|
2053
|
+
const windowObj = window;
|
|
2054
|
+
const api = {
|
|
2055
|
+
config: {
|
|
2056
|
+
get current() {
|
|
2057
|
+
return canvas.config;
|
|
2058
|
+
},
|
|
2059
|
+
get: canvas.getConfig.bind(canvas),
|
|
2060
|
+
update: canvas.updateConfig.bind(canvas),
|
|
2061
|
+
},
|
|
2062
|
+
// Transform group
|
|
2063
|
+
transform: {
|
|
2064
|
+
update: canvas.updateTransform.bind(canvas),
|
|
2065
|
+
reset: canvas.reset.bind(canvas),
|
|
2066
|
+
},
|
|
2067
|
+
// Zoom group
|
|
2068
|
+
zoom: {
|
|
2069
|
+
set: canvas.setZoom.bind(canvas),
|
|
2070
|
+
toPoint: canvas.zoomToPoint.bind(canvas),
|
|
2071
|
+
in: canvas.zoomIn.bind(canvas),
|
|
2072
|
+
out: canvas.zoomOut.bind(canvas),
|
|
2073
|
+
reset: canvas.resetZoom.bind(canvas),
|
|
2074
|
+
resetToCenter: canvas.resetViewToCenter.bind(canvas),
|
|
2075
|
+
fitToScreen: canvas.fitToScreen.bind(canvas),
|
|
2076
|
+
},
|
|
2077
|
+
// Pan group
|
|
2078
|
+
pan: {
|
|
2079
|
+
left: canvas.panLeft.bind(canvas),
|
|
2080
|
+
right: canvas.panRight.bind(canvas),
|
|
2081
|
+
up: canvas.panUp.bind(canvas),
|
|
2082
|
+
down: canvas.panDown.bind(canvas),
|
|
2083
|
+
toPoint: canvas.scrollToPoint.bind(canvas),
|
|
2084
|
+
toCenter: canvas.centerContent.bind(canvas),
|
|
2085
|
+
},
|
|
2086
|
+
// Mouse drag group
|
|
2087
|
+
mouseDrag: {
|
|
2088
|
+
enable: canvas.enableMouseDrag.bind(canvas),
|
|
2089
|
+
disable: canvas.disableMouseDrag.bind(canvas),
|
|
2090
|
+
isEnabled: canvas.isMouseDragEnabled.bind(canvas),
|
|
2091
|
+
},
|
|
2092
|
+
// Grid group
|
|
2093
|
+
grid: {
|
|
2094
|
+
toggle: canvas.toggleGrid.bind(canvas),
|
|
2095
|
+
show: canvas.showGrid.bind(canvas),
|
|
2096
|
+
hide: canvas.hideGrid.bind(canvas),
|
|
2097
|
+
isVisible: canvas.isGridVisible.bind(canvas),
|
|
2098
|
+
},
|
|
2099
|
+
// Ruler group
|
|
2100
|
+
rulers: {
|
|
2101
|
+
toggle: canvas.toggleRulers.bind(canvas),
|
|
2102
|
+
show: canvas.showRulers.bind(canvas),
|
|
2103
|
+
hide: canvas.hideRulers.bind(canvas),
|
|
2104
|
+
isVisible: canvas.areRulersVisible.bind(canvas),
|
|
2105
|
+
},
|
|
2106
|
+
// Utility group
|
|
2107
|
+
canvas: {
|
|
2108
|
+
canvasToContent: canvas.canvasToContent.bind(canvas),
|
|
2109
|
+
getVisibleArea: canvas.getVisibleArea.bind(canvas),
|
|
2110
|
+
isPointVisible: canvas.isPointVisible.bind(canvas),
|
|
2111
|
+
getBounds: canvas.getBounds.bind(canvas),
|
|
2112
|
+
},
|
|
2113
|
+
theme: {
|
|
2114
|
+
get current() {
|
|
2115
|
+
return canvas.config.themeMode;
|
|
2116
|
+
},
|
|
2117
|
+
update: canvas.updateThemeMode.bind(canvas),
|
|
2118
|
+
toggle: canvas.toggleThemeMode.bind(canvas),
|
|
2119
|
+
},
|
|
2120
|
+
transition: {
|
|
2121
|
+
get current() {
|
|
2122
|
+
return canvas.config.enableTransition;
|
|
2123
|
+
},
|
|
2124
|
+
set: canvas.updateTransition.bind(canvas),
|
|
2125
|
+
toggle: canvas.toggleTransitionMode.bind(canvas),
|
|
2126
|
+
},
|
|
2127
|
+
// Event group
|
|
2128
|
+
event: canvas.event,
|
|
2129
|
+
// Lifecycle group
|
|
2130
|
+
lifecycle: {
|
|
2131
|
+
cleanup: canvas.cleanup.bind(canvas),
|
|
2132
|
+
destroy: canvas.destroy.bind(canvas),
|
|
2133
|
+
},
|
|
2134
|
+
// Properties/State group
|
|
2135
|
+
state: {
|
|
2136
|
+
get isReady() {
|
|
2137
|
+
return canvas.isReady;
|
|
2138
|
+
},
|
|
2139
|
+
get isTransforming() {
|
|
2140
|
+
return canvas.isTransforming;
|
|
2141
|
+
},
|
|
2142
|
+
get visibleBounds() {
|
|
2143
|
+
return canvas.visibleBounds;
|
|
2144
|
+
},
|
|
2145
|
+
get transform() {
|
|
2146
|
+
return canvas.transform;
|
|
2147
|
+
},
|
|
2148
|
+
},
|
|
2149
|
+
};
|
|
2150
|
+
// Bind public API to window
|
|
2151
|
+
windowObj[canvasName] = api;
|
|
2152
|
+
// Track all instances
|
|
2153
|
+
if (!windowObj.__markupCanvasInstances) {
|
|
2154
|
+
windowObj.__markupCanvasInstances = new Map();
|
|
2155
|
+
}
|
|
2156
|
+
windowObj.__markupCanvasInstances.set(canvasName, api);
|
|
2157
|
+
}
|
|
2158
|
+
|
|
1838
2159
|
class MarkupCanvas {
|
|
1839
2160
|
constructor(container, options = {}) {
|
|
1840
|
-
this.
|
|
2161
|
+
this.cleanupCallbacks = [];
|
|
1841
2162
|
this.rulers = null;
|
|
1842
2163
|
this.dragSetup = null;
|
|
2164
|
+
this.event = new EventEmitter();
|
|
1843
2165
|
this._isReady = false;
|
|
1844
|
-
this.listen = new EventEmitter();
|
|
1845
|
-
this.postMessageCleanup = null;
|
|
1846
2166
|
if (!container) {
|
|
1847
2167
|
throw new Error("Container element is required");
|
|
1848
2168
|
}
|
|
@@ -1851,100 +2171,48 @@
|
|
|
1851
2171
|
if (!canvas) {
|
|
1852
2172
|
throw new Error("Failed to create canvas");
|
|
1853
2173
|
}
|
|
1854
|
-
this.
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
2174
|
+
this.canvas = canvas;
|
|
2175
|
+
// Always bind canvas to window
|
|
2176
|
+
this.event.setEmitInterceptor((event, data) => {
|
|
2177
|
+
broadcastEvent(event, data, this.config);
|
|
2178
|
+
});
|
|
2179
|
+
bindCanvasToWindow(this, this.config);
|
|
2180
|
+
// Set up postMessage listener
|
|
2181
|
+
if (this.config.enablePostMessageAPI) {
|
|
2182
|
+
const postMessageCleanup = setupPostMessageEvents(this);
|
|
2183
|
+
this.cleanupCallbacks.push(postMessageCleanup);
|
|
1864
2184
|
}
|
|
1865
2185
|
this.setupEventHandlers();
|
|
1866
2186
|
this._isReady = true;
|
|
1867
2187
|
// Emit ready event
|
|
1868
|
-
this.
|
|
1869
|
-
}
|
|
1870
|
-
setupGlobalBinding() {
|
|
1871
|
-
if (typeof window === "undefined") {
|
|
1872
|
-
return;
|
|
1873
|
-
}
|
|
1874
|
-
const canvasName = this.config.name || "markupCanvas";
|
|
1875
|
-
const windowObj = window;
|
|
1876
|
-
// Bind instance to window
|
|
1877
|
-
windowObj[canvasName] = this;
|
|
1878
|
-
// Track all instances
|
|
1879
|
-
if (!windowObj.__markupCanvasInstances) {
|
|
1880
|
-
windowObj.__markupCanvasInstances = new Map();
|
|
1881
|
-
}
|
|
1882
|
-
windowObj.__markupCanvasInstances.set(canvasName, this);
|
|
1883
|
-
}
|
|
1884
|
-
cleanupGlobalBinding() {
|
|
1885
|
-
if (typeof window === "undefined") {
|
|
1886
|
-
return;
|
|
1887
|
-
}
|
|
1888
|
-
const canvasName = this.config.name || "markupCanvas";
|
|
1889
|
-
const windowObj = window;
|
|
1890
|
-
delete windowObj[canvasName];
|
|
1891
|
-
if (windowObj.__markupCanvasInstances) {
|
|
1892
|
-
windowObj.__markupCanvasInstances.delete(canvasName);
|
|
1893
|
-
}
|
|
1894
|
-
}
|
|
1895
|
-
broadcastEvent(event, data) {
|
|
1896
|
-
if (typeof window === "undefined") {
|
|
1897
|
-
return;
|
|
1898
|
-
}
|
|
1899
|
-
// Receivers can get the instance from the window binding
|
|
1900
|
-
let broadcastData = data;
|
|
1901
|
-
if (event === "ready") {
|
|
1902
|
-
broadcastData = { ready: true };
|
|
1903
|
-
}
|
|
1904
|
-
window.postMessage({
|
|
1905
|
-
source: "markup-canvas",
|
|
1906
|
-
event,
|
|
1907
|
-
data: broadcastData,
|
|
1908
|
-
timestamp: Date.now(),
|
|
1909
|
-
canvasName: this.config.name,
|
|
1910
|
-
}, "*");
|
|
1911
|
-
if (window.parent) {
|
|
1912
|
-
window.parent.postMessage({
|
|
1913
|
-
source: "markup-canvas",
|
|
1914
|
-
event,
|
|
1915
|
-
data: broadcastData,
|
|
1916
|
-
timestamp: Date.now(),
|
|
1917
|
-
canvasName: this.config.name,
|
|
1918
|
-
}, "*");
|
|
1919
|
-
}
|
|
2188
|
+
this.event.emit("ready", this);
|
|
1920
2189
|
}
|
|
1921
2190
|
setupEventHandlers() {
|
|
1922
2191
|
try {
|
|
1923
|
-
// Wheel
|
|
2192
|
+
// Wheel events
|
|
1924
2193
|
withFeatureEnabled(this.config, "enableZoom", () => {
|
|
1925
2194
|
const wheelCleanup = setupWheelEvents(this, this.config);
|
|
1926
|
-
this.
|
|
2195
|
+
this.cleanupCallbacks.push(wheelCleanup);
|
|
1927
2196
|
});
|
|
1928
|
-
// Mouse events
|
|
1929
|
-
// Set up mouse events if either pan or click-to-zoom is enabled
|
|
2197
|
+
// Mouse events
|
|
1930
2198
|
if (this.config.enablePan || this.config.enableClickToZoom) {
|
|
1931
2199
|
this.dragSetup = setupMouseEvents(this, this.config, true);
|
|
1932
|
-
this.
|
|
2200
|
+
this.cleanupCallbacks.push(this.dragSetup.cleanup);
|
|
1933
2201
|
}
|
|
1934
|
-
// Keyboard
|
|
2202
|
+
// Keyboard events
|
|
1935
2203
|
withFeatureEnabled(this.config, "enableKeyboard", () => {
|
|
1936
2204
|
const keyboardCleanup = setupKeyboardEvents(this, this.config);
|
|
1937
|
-
this.
|
|
2205
|
+
this.cleanupCallbacks.push(keyboardCleanup);
|
|
1938
2206
|
});
|
|
1939
|
-
// Touch events
|
|
2207
|
+
// Touch events
|
|
1940
2208
|
withFeatureEnabled(this.config, "enableTouch", () => {
|
|
1941
2209
|
const touchCleanup = setupTouchEvents(this);
|
|
1942
|
-
this.
|
|
2210
|
+
this.cleanupCallbacks.push(touchCleanup);
|
|
1943
2211
|
});
|
|
1944
2212
|
// Set up rulers and grid
|
|
1945
2213
|
withFeatureEnabled(this.config, "enableRulers", () => {
|
|
1946
|
-
this.rulers = createRulers(this
|
|
1947
|
-
this.
|
|
2214
|
+
this.rulers = createRulers(this, this.config);
|
|
2215
|
+
this.cleanupCallbacks.push(() => {
|
|
1948
2216
|
if (this.rulers) {
|
|
1949
2217
|
this.rulers.destroy();
|
|
1950
2218
|
}
|
|
@@ -1957,20 +2225,18 @@
|
|
|
1957
2225
|
throw error;
|
|
1958
2226
|
}
|
|
1959
2227
|
}
|
|
1960
|
-
// Base canvas properties and methods
|
|
1961
2228
|
get container() {
|
|
1962
|
-
return this.
|
|
2229
|
+
return this.canvas.container;
|
|
1963
2230
|
}
|
|
1964
2231
|
get transformLayer() {
|
|
1965
|
-
return this.
|
|
2232
|
+
return this.canvas.transformLayer;
|
|
1966
2233
|
}
|
|
1967
2234
|
get contentLayer() {
|
|
1968
|
-
return this.
|
|
2235
|
+
return this.canvas.contentLayer;
|
|
1969
2236
|
}
|
|
1970
2237
|
get transform() {
|
|
1971
|
-
return this.
|
|
2238
|
+
return this.canvas.transform;
|
|
1972
2239
|
}
|
|
1973
|
-
// State management getters for React integration
|
|
1974
2240
|
get isReady() {
|
|
1975
2241
|
return this._isReady;
|
|
1976
2242
|
}
|
|
@@ -1978,125 +2244,65 @@
|
|
|
1978
2244
|
return this.dragSetup?.isEnabled() || false;
|
|
1979
2245
|
}
|
|
1980
2246
|
get visibleBounds() {
|
|
1981
|
-
return
|
|
2247
|
+
return getVisibleArea(this);
|
|
1982
2248
|
}
|
|
1983
2249
|
getBounds() {
|
|
1984
|
-
return this.
|
|
2250
|
+
return getCanvasBounds(this.canvas, this.config);
|
|
1985
2251
|
}
|
|
1986
2252
|
updateTransform(newTransform) {
|
|
1987
|
-
const result = this.
|
|
2253
|
+
const result = updateTransform(this.canvas, newTransform);
|
|
1988
2254
|
if (result) {
|
|
1989
|
-
this.
|
|
2255
|
+
emitTransformEvents(this.event, this.canvas);
|
|
1990
2256
|
}
|
|
1991
2257
|
return result;
|
|
1992
2258
|
}
|
|
1993
|
-
emitTransformEvents() {
|
|
1994
|
-
const transform = this.baseCanvas.transform;
|
|
1995
|
-
this.listen.emit("transform", transform);
|
|
1996
|
-
this.listen.emit("zoom", transform.scale);
|
|
1997
|
-
this.listen.emit("pan", { x: transform.translateX, y: transform.translateY });
|
|
1998
|
-
}
|
|
1999
2259
|
reset() {
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2260
|
+
const result = resetTransform(this.canvas);
|
|
2261
|
+
if (result) {
|
|
2262
|
+
emitTransformEvents(this.event, this.canvas);
|
|
2263
|
+
}
|
|
2264
|
+
return result;
|
|
2004
2265
|
}
|
|
2005
2266
|
setZoom(zoomLevel) {
|
|
2006
|
-
return
|
|
2007
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2008
|
-
const newScale = clamp(zoomLevel);
|
|
2009
|
-
const newTransform = {
|
|
2010
|
-
scale: newScale,
|
|
2011
|
-
};
|
|
2012
|
-
return this.updateTransform(newTransform);
|
|
2013
|
-
});
|
|
2014
|
-
});
|
|
2267
|
+
return setZoom(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), zoomLevel);
|
|
2015
2268
|
}
|
|
2016
2269
|
canvasToContent(x, y) {
|
|
2017
|
-
|
|
2270
|
+
const matrix = createMatrix(this.canvas.transform.scale, this.canvas.transform.translateX, this.canvas.transform.translateY);
|
|
2271
|
+
return canvasToContent(x, y, matrix);
|
|
2018
2272
|
}
|
|
2019
2273
|
zoomToPoint(x, y, targetScale) {
|
|
2020
|
-
return
|
|
2021
|
-
const result = this.baseCanvas.zoomToPoint(x, y, targetScale);
|
|
2022
|
-
if (result) {
|
|
2023
|
-
this.emitTransformEvents();
|
|
2024
|
-
}
|
|
2025
|
-
return result;
|
|
2026
|
-
});
|
|
2274
|
+
return zoomToPoint(this.canvas, this.transformLayer, this.config, x, y, targetScale);
|
|
2027
2275
|
}
|
|
2028
2276
|
resetView() {
|
|
2029
|
-
return
|
|
2030
|
-
const result = this.baseCanvas.resetView ? this.baseCanvas.resetView() : false;
|
|
2031
|
-
if (result) {
|
|
2032
|
-
this.emitTransformEvents();
|
|
2033
|
-
}
|
|
2034
|
-
return result;
|
|
2035
|
-
});
|
|
2277
|
+
return resetView(this.canvas, this.transformLayer, this.config);
|
|
2036
2278
|
}
|
|
2037
|
-
|
|
2038
|
-
return
|
|
2039
|
-
const result = this.baseCanvas.zoomToFitContent();
|
|
2040
|
-
if (result) {
|
|
2041
|
-
this.emitTransformEvents();
|
|
2042
|
-
}
|
|
2043
|
-
return result;
|
|
2044
|
-
});
|
|
2279
|
+
resetViewToCenter() {
|
|
2280
|
+
return resetViewToCenter(this, this.transformLayer, this.config, this.zoomToPoint.bind(this));
|
|
2045
2281
|
}
|
|
2046
|
-
// Pan methods
|
|
2047
2282
|
panLeft(distance) {
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
translateX: this.baseCanvas.transform.translateX + panDistance,
|
|
2051
|
-
};
|
|
2052
|
-
return this.updateTransform(newTransform);
|
|
2283
|
+
return (panLeft(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2284
|
+
(distance ? panLeft(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2053
2285
|
}
|
|
2054
2286
|
panRight(distance) {
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
translateX: this.baseCanvas.transform.translateX - panDistance,
|
|
2058
|
-
};
|
|
2059
|
-
return this.updateTransform(newTransform);
|
|
2287
|
+
return (panRight(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2288
|
+
(distance ? panRight(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2060
2289
|
}
|
|
2061
2290
|
panUp(distance) {
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
translateY: this.baseCanvas.transform.translateY + panDistance,
|
|
2065
|
-
};
|
|
2066
|
-
return this.updateTransform(newTransform);
|
|
2291
|
+
return (panUp(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2292
|
+
(distance ? panUp(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2067
2293
|
}
|
|
2068
2294
|
panDown(distance) {
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
translateY: this.baseCanvas.transform.translateY - panDistance,
|
|
2072
|
-
};
|
|
2073
|
-
return this.updateTransform(newTransform);
|
|
2295
|
+
return (panDown(this.canvas, this.config, this.updateTransform.bind(this)) ||
|
|
2296
|
+
(distance ? panDown(this.canvas, { ...this.config, keyboardPanStep: distance }, this.updateTransform.bind(this)) : false));
|
|
2074
2297
|
}
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
return withTransition(this.transformLayer, this.config, () => {
|
|
2078
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2079
|
-
const newScale = clamp(this.baseCanvas.transform.scale * (1 + factor));
|
|
2080
|
-
const newTransform = {
|
|
2081
|
-
scale: newScale,
|
|
2082
|
-
};
|
|
2083
|
-
return this.updateTransform(newTransform);
|
|
2084
|
-
});
|
|
2085
|
-
});
|
|
2298
|
+
zoomIn(factor = 0.5) {
|
|
2299
|
+
return zoomIn(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
|
|
2086
2300
|
}
|
|
2087
|
-
zoomOut(factor = 0.
|
|
2088
|
-
return
|
|
2089
|
-
return withClampedZoom(this.config, (clamp) => {
|
|
2090
|
-
const newScale = clamp(this.baseCanvas.transform.scale * (1 - factor));
|
|
2091
|
-
const newTransform = {
|
|
2092
|
-
scale: newScale,
|
|
2093
|
-
};
|
|
2094
|
-
return this.updateTransform(newTransform);
|
|
2095
|
-
});
|
|
2096
|
-
});
|
|
2301
|
+
zoomOut(factor = 0.5) {
|
|
2302
|
+
return zoomOut(this, this.transformLayer, this.config, this.zoomToPoint.bind(this), factor);
|
|
2097
2303
|
}
|
|
2098
2304
|
resetZoom() {
|
|
2099
|
-
return this.
|
|
2305
|
+
return this.resetViewToCenter();
|
|
2100
2306
|
}
|
|
2101
2307
|
// Mouse drag control methods
|
|
2102
2308
|
enableMouseDrag() {
|
|
@@ -2108,110 +2314,68 @@
|
|
|
2108
2314
|
isMouseDragEnabled() {
|
|
2109
2315
|
return this.dragSetup?.isEnabled() ?? false;
|
|
2110
2316
|
}
|
|
2111
|
-
// Grid control methods
|
|
2112
2317
|
toggleGrid() {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2318
|
+
const result = toggleGrid(this.rulers);
|
|
2319
|
+
if (result) {
|
|
2320
|
+
this.event.emit("gridVisibility", this.isGridVisible());
|
|
2116
2321
|
}
|
|
2117
|
-
return
|
|
2322
|
+
return result;
|
|
2118
2323
|
}
|
|
2119
2324
|
showGrid() {
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2325
|
+
const result = showGrid(this.rulers);
|
|
2326
|
+
if (result) {
|
|
2327
|
+
this.event.emit("gridVisibility", true);
|
|
2123
2328
|
}
|
|
2124
|
-
return
|
|
2329
|
+
return result;
|
|
2125
2330
|
}
|
|
2126
2331
|
hideGrid() {
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2332
|
+
const result = hideGrid(this.rulers);
|
|
2333
|
+
if (result) {
|
|
2334
|
+
this.event.emit("gridVisibility", false);
|
|
2130
2335
|
}
|
|
2131
|
-
return
|
|
2336
|
+
return result;
|
|
2132
2337
|
}
|
|
2133
2338
|
isGridVisible() {
|
|
2134
|
-
|
|
2135
|
-
return this.rulers.gridOverlay.style.display !== "none";
|
|
2136
|
-
}
|
|
2137
|
-
return false;
|
|
2339
|
+
return isGridVisible(this.rulers);
|
|
2138
2340
|
}
|
|
2139
|
-
// Ruler control methods
|
|
2140
2341
|
toggleRulers() {
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
this.rulers.hide();
|
|
2145
|
-
}
|
|
2146
|
-
else {
|
|
2147
|
-
this.rulers.show();
|
|
2148
|
-
}
|
|
2149
|
-
return true;
|
|
2342
|
+
const result = toggleRulers(this.rulers, () => this.areRulersVisible());
|
|
2343
|
+
if (result) {
|
|
2344
|
+
this.event.emit("rulersVisibility", this.areRulersVisible());
|
|
2150
2345
|
}
|
|
2151
|
-
return
|
|
2346
|
+
return result;
|
|
2152
2347
|
}
|
|
2153
2348
|
showRulers() {
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2349
|
+
const result = showRulers(this.rulers);
|
|
2350
|
+
if (result) {
|
|
2351
|
+
this.event.emit("rulersVisibility", true);
|
|
2157
2352
|
}
|
|
2158
|
-
return
|
|
2353
|
+
return result;
|
|
2159
2354
|
}
|
|
2160
2355
|
hideRulers() {
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2356
|
+
const result = hideRulers(this.rulers);
|
|
2357
|
+
if (result) {
|
|
2358
|
+
this.event.emit("rulersVisibility", false);
|
|
2164
2359
|
}
|
|
2165
|
-
return
|
|
2360
|
+
return result;
|
|
2166
2361
|
}
|
|
2167
2362
|
areRulersVisible() {
|
|
2168
|
-
|
|
2169
|
-
return this.rulers.horizontalRuler.style.display !== "none";
|
|
2170
|
-
}
|
|
2171
|
-
return false;
|
|
2363
|
+
return areRulersVisible(this.rulers);
|
|
2172
2364
|
}
|
|
2173
|
-
// Utility methods
|
|
2174
2365
|
centerContent() {
|
|
2175
|
-
return
|
|
2176
|
-
const bounds = this.baseCanvas.getBounds();
|
|
2177
|
-
const centerX = (bounds.width - bounds.contentWidth * this.baseCanvas.transform.scale) / 2;
|
|
2178
|
-
const centerY = (bounds.height - bounds.contentHeight * this.baseCanvas.transform.scale) / 2;
|
|
2179
|
-
return this.updateTransform({
|
|
2180
|
-
translateX: centerX,
|
|
2181
|
-
translateY: centerY,
|
|
2182
|
-
});
|
|
2183
|
-
});
|
|
2366
|
+
return centerContent(this.canvas, this.config, this.updateTransform.bind(this), this.transformLayer);
|
|
2184
2367
|
}
|
|
2185
2368
|
fitToScreen() {
|
|
2186
|
-
return
|
|
2187
|
-
const result = this.baseCanvas.zoomToFitContent();
|
|
2188
|
-
if (result) {
|
|
2189
|
-
this.emitTransformEvents();
|
|
2190
|
-
}
|
|
2191
|
-
return result;
|
|
2192
|
-
});
|
|
2369
|
+
return fitToScreen(this.canvas, this.transformLayer, this.config);
|
|
2193
2370
|
}
|
|
2194
2371
|
getVisibleArea() {
|
|
2195
|
-
|
|
2196
|
-
return bounds.visibleArea;
|
|
2372
|
+
return getVisibleArea(this);
|
|
2197
2373
|
}
|
|
2198
2374
|
isPointVisible(x, y) {
|
|
2199
|
-
|
|
2200
|
-
return x >= visibleArea.x && x <= visibleArea.x + visibleArea.width && y >= visibleArea.y && y <= visibleArea.y + visibleArea.height;
|
|
2375
|
+
return isPointVisible(this, x, y);
|
|
2201
2376
|
}
|
|
2202
2377
|
scrollToPoint(x, y) {
|
|
2203
|
-
return
|
|
2204
|
-
const bounds = this.baseCanvas.getBounds();
|
|
2205
|
-
const centerX = bounds.width / 2;
|
|
2206
|
-
const centerY = bounds.height / 2;
|
|
2207
|
-
// Calculate new translation to center the point
|
|
2208
|
-
const newTranslateX = centerX - x * this.baseCanvas.transform.scale;
|
|
2209
|
-
const newTranslateY = centerY - y * this.baseCanvas.transform.scale;
|
|
2210
|
-
return this.updateTransform({
|
|
2211
|
-
translateX: newTranslateX,
|
|
2212
|
-
translateY: newTranslateY,
|
|
2213
|
-
});
|
|
2214
|
-
});
|
|
2378
|
+
return scrollToPoint(this.canvas, this.config, x, y, this.updateTransform.bind(this), this.transformLayer);
|
|
2215
2379
|
}
|
|
2216
2380
|
// Configuration access
|
|
2217
2381
|
getConfig() {
|
|
@@ -2222,28 +2386,28 @@
|
|
|
2222
2386
|
}
|
|
2223
2387
|
// Theme management
|
|
2224
2388
|
updateThemeMode(mode) {
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
this.config
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
}
|
|
2389
|
+
this.config = createMarkupCanvasConfig({ ...this.config, themeMode: mode });
|
|
2390
|
+
updateThemeMode(this.canvas.container, this.config, this.rulers, mode);
|
|
2391
|
+
}
|
|
2392
|
+
toggleThemeMode() {
|
|
2393
|
+
const currentMode = this.config.themeMode;
|
|
2394
|
+
const newMode = currentMode === "light" ? "dark" : "light";
|
|
2395
|
+
this.updateThemeMode(newMode);
|
|
2396
|
+
return newMode;
|
|
2397
|
+
}
|
|
2398
|
+
// Transition management
|
|
2399
|
+
updateTransition(enabled) {
|
|
2400
|
+
this.config = createMarkupCanvasConfig({ ...this.config, enableTransition: enabled });
|
|
2401
|
+
}
|
|
2402
|
+
toggleTransitionMode() {
|
|
2403
|
+
const newEnableTransition = toggleTransition(this.config.enableTransition);
|
|
2404
|
+
this.updateTransition(newEnableTransition);
|
|
2405
|
+
return newEnableTransition;
|
|
2237
2406
|
}
|
|
2238
2407
|
// Cleanup method
|
|
2239
2408
|
cleanup() {
|
|
2240
|
-
this.
|
|
2241
|
-
|
|
2242
|
-
if (this.postMessageCleanup) {
|
|
2243
|
-
this.postMessageCleanup();
|
|
2244
|
-
this.postMessageCleanup = null;
|
|
2245
|
-
}
|
|
2246
|
-
this.cleanupFunctions.forEach((cleanup) => {
|
|
2409
|
+
cleanupWindowBinding(this.config);
|
|
2410
|
+
this.cleanupCallbacks.forEach((cleanup) => {
|
|
2247
2411
|
try {
|
|
2248
2412
|
cleanup();
|
|
2249
2413
|
}
|
|
@@ -2251,22 +2415,22 @@
|
|
|
2251
2415
|
console.warn("Error during cleanup:", cleanupError);
|
|
2252
2416
|
}
|
|
2253
2417
|
});
|
|
2254
|
-
this.
|
|
2418
|
+
this.cleanupCallbacks = [];
|
|
2255
2419
|
// Remove all event listeners
|
|
2256
2420
|
this.removeAllListeners();
|
|
2257
2421
|
}
|
|
2258
2422
|
// Event emitter delegation methods
|
|
2259
2423
|
on(event, handler) {
|
|
2260
|
-
this.
|
|
2424
|
+
this.event.on(event, handler);
|
|
2261
2425
|
}
|
|
2262
2426
|
off(event, handler) {
|
|
2263
|
-
this.
|
|
2427
|
+
this.event.off(event, handler);
|
|
2264
2428
|
}
|
|
2265
2429
|
emit(event, data) {
|
|
2266
|
-
this.
|
|
2430
|
+
this.event.emit(event, data);
|
|
2267
2431
|
}
|
|
2268
2432
|
removeAllListeners() {
|
|
2269
|
-
this.
|
|
2433
|
+
this.event.removeAllListeners();
|
|
2270
2434
|
}
|
|
2271
2435
|
destroy() {
|
|
2272
2436
|
this.cleanup();
|