@overtone-art/canvas-editor-core 0.2.2 → 0.2.5
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.mts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +80 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -95,11 +95,29 @@ interface PatternConfig {
|
|
|
95
95
|
angle: number;
|
|
96
96
|
/** Brick row/column shift, as % of tile size. */
|
|
97
97
|
horizontalOffset: number;
|
|
98
|
+
/**
|
|
99
|
+
* Phase shift of the whole tile grid inside the print area, as % of tile
|
|
100
|
+
* width. The pattern object itself is pinned to the print area (it can't be
|
|
101
|
+
* dragged — that would expose bare edges), so this is how the tiling is
|
|
102
|
+
* nudged into alignment. Clamped to ±100% so coverage is never broken.
|
|
103
|
+
*/
|
|
104
|
+
offsetX: number;
|
|
105
|
+
/** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */
|
|
106
|
+
offsetY: number;
|
|
98
107
|
/** Added rotation per horizontal step, in degrees. */
|
|
99
108
|
rotationStepH: number;
|
|
100
109
|
/** Added rotation per vertical step, in degrees. */
|
|
101
110
|
rotationStepV: number;
|
|
102
111
|
}
|
|
112
|
+
/** Fabric interaction flags frozen while a layer is rendered as a pattern. */
|
|
113
|
+
interface PatternLocks {
|
|
114
|
+
lockMovementX: boolean;
|
|
115
|
+
lockMovementY: boolean;
|
|
116
|
+
lockScalingX: boolean;
|
|
117
|
+
lockScalingY: boolean;
|
|
118
|
+
lockRotation: boolean;
|
|
119
|
+
hasControls: boolean;
|
|
120
|
+
}
|
|
103
121
|
/** Persisted on a layer's meta while it is rendered as a repeating pattern. */
|
|
104
122
|
interface PatternState {
|
|
105
123
|
config: PatternConfig;
|
|
@@ -113,6 +131,13 @@ interface PatternState {
|
|
|
113
131
|
* serialization / reload / undo.
|
|
114
132
|
*/
|
|
115
133
|
originalClip?: Record<string, unknown> | null;
|
|
134
|
+
/**
|
|
135
|
+
* Interaction flags the object carried before it became a pattern. A pattern
|
|
136
|
+
* fills the whole print area, so while it is on the object is locked in place
|
|
137
|
+
* (a drag/resize would slide the tiled bitmap off the area and leave a bare
|
|
138
|
+
* band); the captured flags are restored when the pattern is turned off.
|
|
139
|
+
*/
|
|
140
|
+
originalLocks?: PatternLocks;
|
|
116
141
|
/** Original fabric transform, restored when the pattern is turned off. */
|
|
117
142
|
original: {
|
|
118
143
|
left: number;
|
|
@@ -338,12 +363,31 @@ declare class PatternManager {
|
|
|
338
363
|
getConfig(layerId: string): PatternConfig | null;
|
|
339
364
|
/** Turn a plain image layer into a pattern, or update an existing one. */
|
|
340
365
|
apply(layerId: string, config: PatternConfig): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
368
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
369
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
370
|
+
* after a state restore, where the canvas may be a different display size
|
|
371
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
372
|
+
* pattern could still be dragged out of the print area.
|
|
373
|
+
*/
|
|
374
|
+
repinAll(): void;
|
|
341
375
|
/** Restore the original image and drop the pattern. */
|
|
342
376
|
disable(layerId: string): Promise<void>;
|
|
343
377
|
/** Run `task` after any in-flight work for this layer, regardless of outcome. */
|
|
344
378
|
private enqueue;
|
|
345
379
|
private renderLayer;
|
|
346
380
|
}
|
|
381
|
+
/** Interaction flags to restore when a pattern is turned off. */
|
|
382
|
+
declare function captureLocks(obj: FabricObject): PatternLocks;
|
|
383
|
+
/** Pin a pattern object: no move, scale or rotate while the pattern is on. */
|
|
384
|
+
declare function applyPatternLocks(obj: FabricObject): void;
|
|
385
|
+
/**
|
|
386
|
+
* Restore pre-pattern interaction flags. States saved before locking existed
|
|
387
|
+
* carry no snapshot — fall back to a fully interactive object rather than
|
|
388
|
+
* leaving it frozen forever.
|
|
389
|
+
*/
|
|
390
|
+
declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
|
|
347
391
|
/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
|
|
348
392
|
declare function clearPatternImageCache(): void;
|
|
349
393
|
/**
|
|
@@ -474,4 +518,4 @@ declare function round2(v: number): number;
|
|
|
474
518
|
declare function serializeEditor(editor: CanvasEditor): EditorState;
|
|
475
519
|
declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
|
|
476
520
|
|
|
477
|
-
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };
|
|
521
|
+
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, type PatternLocks, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, restoreLocks, round2, serializeEditor };
|
package/dist/index.d.ts
CHANGED
|
@@ -95,11 +95,29 @@ interface PatternConfig {
|
|
|
95
95
|
angle: number;
|
|
96
96
|
/** Brick row/column shift, as % of tile size. */
|
|
97
97
|
horizontalOffset: number;
|
|
98
|
+
/**
|
|
99
|
+
* Phase shift of the whole tile grid inside the print area, as % of tile
|
|
100
|
+
* width. The pattern object itself is pinned to the print area (it can't be
|
|
101
|
+
* dragged — that would expose bare edges), so this is how the tiling is
|
|
102
|
+
* nudged into alignment. Clamped to ±100% so coverage is never broken.
|
|
103
|
+
*/
|
|
104
|
+
offsetX: number;
|
|
105
|
+
/** Phase shift of the tile grid, as % of tile height. See {@link offsetX}. */
|
|
106
|
+
offsetY: number;
|
|
98
107
|
/** Added rotation per horizontal step, in degrees. */
|
|
99
108
|
rotationStepH: number;
|
|
100
109
|
/** Added rotation per vertical step, in degrees. */
|
|
101
110
|
rotationStepV: number;
|
|
102
111
|
}
|
|
112
|
+
/** Fabric interaction flags frozen while a layer is rendered as a pattern. */
|
|
113
|
+
interface PatternLocks {
|
|
114
|
+
lockMovementX: boolean;
|
|
115
|
+
lockMovementY: boolean;
|
|
116
|
+
lockScalingX: boolean;
|
|
117
|
+
lockScalingY: boolean;
|
|
118
|
+
lockRotation: boolean;
|
|
119
|
+
hasControls: boolean;
|
|
120
|
+
}
|
|
103
121
|
/** Persisted on a layer's meta while it is rendered as a repeating pattern. */
|
|
104
122
|
interface PatternState {
|
|
105
123
|
config: PatternConfig;
|
|
@@ -113,6 +131,13 @@ interface PatternState {
|
|
|
113
131
|
* serialization / reload / undo.
|
|
114
132
|
*/
|
|
115
133
|
originalClip?: Record<string, unknown> | null;
|
|
134
|
+
/**
|
|
135
|
+
* Interaction flags the object carried before it became a pattern. A pattern
|
|
136
|
+
* fills the whole print area, so while it is on the object is locked in place
|
|
137
|
+
* (a drag/resize would slide the tiled bitmap off the area and leave a bare
|
|
138
|
+
* band); the captured flags are restored when the pattern is turned off.
|
|
139
|
+
*/
|
|
140
|
+
originalLocks?: PatternLocks;
|
|
116
141
|
/** Original fabric transform, restored when the pattern is turned off. */
|
|
117
142
|
original: {
|
|
118
143
|
left: number;
|
|
@@ -338,12 +363,31 @@ declare class PatternManager {
|
|
|
338
363
|
getConfig(layerId: string): PatternConfig | null;
|
|
339
364
|
/** Turn a plain image layer into a pattern, or update an existing one. */
|
|
340
365
|
apply(layerId: string, config: PatternConfig): Promise<void>;
|
|
366
|
+
/**
|
|
367
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
368
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
369
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
370
|
+
* after a state restore, where the canvas may be a different display size
|
|
371
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
372
|
+
* pattern could still be dragged out of the print area.
|
|
373
|
+
*/
|
|
374
|
+
repinAll(): void;
|
|
341
375
|
/** Restore the original image and drop the pattern. */
|
|
342
376
|
disable(layerId: string): Promise<void>;
|
|
343
377
|
/** Run `task` after any in-flight work for this layer, regardless of outcome. */
|
|
344
378
|
private enqueue;
|
|
345
379
|
private renderLayer;
|
|
346
380
|
}
|
|
381
|
+
/** Interaction flags to restore when a pattern is turned off. */
|
|
382
|
+
declare function captureLocks(obj: FabricObject): PatternLocks;
|
|
383
|
+
/** Pin a pattern object: no move, scale or rotate while the pattern is on. */
|
|
384
|
+
declare function applyPatternLocks(obj: FabricObject): void;
|
|
385
|
+
/**
|
|
386
|
+
* Restore pre-pattern interaction flags. States saved before locking existed
|
|
387
|
+
* carry no snapshot — fall back to a fully interactive object rather than
|
|
388
|
+
* leaving it frozen forever.
|
|
389
|
+
*/
|
|
390
|
+
declare function restoreLocks(obj: FabricObject, locks: PatternLocks | undefined): void;
|
|
347
391
|
/** Clear the decoded-image cache (e.g. on teardown). Exported for completeness. */
|
|
348
392
|
declare function clearPatternImageCache(): void;
|
|
349
393
|
/**
|
|
@@ -474,4 +518,4 @@ declare function round2(v: number): number;
|
|
|
474
518
|
declare function serializeEditor(editor: CanvasEditor): EditorState;
|
|
475
519
|
declare function deserializeEditor(editor: CanvasEditor, state: EditorState): Promise<void>;
|
|
476
520
|
|
|
477
|
-
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, buildPatternDataURL, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, round2, serializeEditor };
|
|
521
|
+
export { CanvasEditor, CropController, DEFAULT_PATTERN_CONFIG, type EditorConfig, type EditorEvents, type EditorState, EventEmitter, type ExportFormat, type FileAdapter, HistoryManager, type ImageProvider, type ImageProviderResult, Layer, type LayerData, LayerManager, type LayerMeta, type LayerType, type MockupConfig, type MockupPrintArea, type PatternConfig, type PatternLocks, PatternManager, type PatternState, type PngExportOptions, type PrintifyPositioning, type SerializedLayer, type ShapePlugin, SnapManager, type TemplateDefinition, type TemplateParameter, type TileMode, type TilePlacement, type Unit, UnitConverter, applyPatternLocks, buildPatternDataURL, captureLocks, clamp, clearPatternImageCache, computeTilePositions, deserializeEditor, drawTiles, exportDataURL, exportPNG, exportSVG, generateId, restoreLocks, round2, serializeEditor };
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,9 @@ __export(index_exports, {
|
|
|
40
40
|
PatternManager: () => PatternManager,
|
|
41
41
|
SnapManager: () => SnapManager,
|
|
42
42
|
UnitConverter: () => UnitConverter,
|
|
43
|
+
applyPatternLocks: () => applyPatternLocks,
|
|
43
44
|
buildPatternDataURL: () => buildPatternDataURL,
|
|
45
|
+
captureLocks: () => captureLocks,
|
|
44
46
|
clamp: () => clamp,
|
|
45
47
|
clearPatternImageCache: () => clearPatternImageCache,
|
|
46
48
|
computeTilePositions: () => computeTilePositions,
|
|
@@ -50,6 +52,7 @@ __export(index_exports, {
|
|
|
50
52
|
exportPNG: () => exportPNG,
|
|
51
53
|
exportSVG: () => exportSVG,
|
|
52
54
|
generateId: () => generateId,
|
|
55
|
+
restoreLocks: () => restoreLocks,
|
|
53
56
|
round2: () => round2,
|
|
54
57
|
serializeEditor: () => serializeEditor
|
|
55
58
|
});
|
|
@@ -633,6 +636,7 @@ var PatternManager = class {
|
|
|
633
636
|
config,
|
|
634
637
|
originalSrc: elementToDataURL(image) ?? image.getSrc(),
|
|
635
638
|
originalClip: clip ? clip.toObject() : null,
|
|
639
|
+
originalLocks: captureLocks(image),
|
|
636
640
|
original: {
|
|
637
641
|
left: image.left ?? 0,
|
|
638
642
|
top: image.top ?? 0,
|
|
@@ -657,6 +661,34 @@ var PatternManager = class {
|
|
|
657
661
|
this.history.save();
|
|
658
662
|
});
|
|
659
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Stretch every restored pattern layer back over the full print area and
|
|
666
|
+
* re-freeze it, without re-rasterising: the baked bitmap keeps whatever size
|
|
667
|
+
* it was saved at, so it is scaled (not re-tiled) to the current canvas. Used
|
|
668
|
+
* after a state restore, where the canvas may be a different display size
|
|
669
|
+
* than when the pattern was baked — and to repair states saved while a
|
|
670
|
+
* pattern could still be dragged out of the print area.
|
|
671
|
+
*/
|
|
672
|
+
repinAll() {
|
|
673
|
+
const cw = this.canvas.getWidth();
|
|
674
|
+
const ch = this.canvas.getHeight();
|
|
675
|
+
let changed = false;
|
|
676
|
+
for (const layer of this.layers.getAll()) {
|
|
677
|
+
if (!layer.meta.pattern || layer.type !== "image") continue;
|
|
678
|
+
const image = layer.fabricObject;
|
|
679
|
+
image.set({
|
|
680
|
+
left: 0,
|
|
681
|
+
top: 0,
|
|
682
|
+
angle: 0,
|
|
683
|
+
scaleX: cw / (image.width || cw),
|
|
684
|
+
scaleY: ch / (image.height || ch)
|
|
685
|
+
});
|
|
686
|
+
applyPatternLocks(image);
|
|
687
|
+
image.setCoords();
|
|
688
|
+
changed = true;
|
|
689
|
+
}
|
|
690
|
+
if (changed) this.canvas.requestRenderAll();
|
|
691
|
+
}
|
|
660
692
|
/** Restore the original image and drop the pattern. */
|
|
661
693
|
disable(layerId) {
|
|
662
694
|
return this.enqueue(layerId, async () => {
|
|
@@ -677,6 +709,7 @@ var PatternManager = class {
|
|
|
677
709
|
angle: state.original.angle
|
|
678
710
|
});
|
|
679
711
|
image.clipPath = state.originalClip ? (await import_fabric2.util.enlivenObjects([state.originalClip]))[0] : void 0;
|
|
712
|
+
restoreLocks(image, state.originalLocks);
|
|
680
713
|
image.setCoords();
|
|
681
714
|
delete layer.meta.pattern;
|
|
682
715
|
this.canvas.requestRenderAll();
|
|
@@ -725,10 +758,43 @@ var PatternManager = class {
|
|
|
725
758
|
angle: 0
|
|
726
759
|
});
|
|
727
760
|
image.clipPath = void 0;
|
|
761
|
+
applyPatternLocks(image);
|
|
728
762
|
image.setCoords();
|
|
729
763
|
this.canvas.requestRenderAll();
|
|
730
764
|
}
|
|
731
765
|
};
|
|
766
|
+
function captureLocks(obj) {
|
|
767
|
+
return {
|
|
768
|
+
lockMovementX: obj.lockMovementX ?? false,
|
|
769
|
+
lockMovementY: obj.lockMovementY ?? false,
|
|
770
|
+
lockScalingX: obj.lockScalingX ?? false,
|
|
771
|
+
lockScalingY: obj.lockScalingY ?? false,
|
|
772
|
+
lockRotation: obj.lockRotation ?? false,
|
|
773
|
+
hasControls: obj.hasControls ?? true
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function applyPatternLocks(obj) {
|
|
777
|
+
obj.set({
|
|
778
|
+
lockMovementX: true,
|
|
779
|
+
lockMovementY: true,
|
|
780
|
+
lockScalingX: true,
|
|
781
|
+
lockScalingY: true,
|
|
782
|
+
lockRotation: true,
|
|
783
|
+
hasControls: false
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
function restoreLocks(obj, locks) {
|
|
787
|
+
obj.set(
|
|
788
|
+
locks ?? {
|
|
789
|
+
lockMovementX: false,
|
|
790
|
+
lockMovementY: false,
|
|
791
|
+
lockScalingX: false,
|
|
792
|
+
lockScalingY: false,
|
|
793
|
+
lockRotation: false,
|
|
794
|
+
hasControls: true
|
|
795
|
+
}
|
|
796
|
+
);
|
|
797
|
+
}
|
|
732
798
|
function elementToDataURL(image) {
|
|
733
799
|
try {
|
|
734
800
|
const el = image.getElement();
|
|
@@ -797,11 +863,13 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH) {
|
|
|
797
863
|
const rows = Math.ceil(diag / tileH) + 2;
|
|
798
864
|
const halfCols = Math.ceil(cols / 2);
|
|
799
865
|
const halfRows = Math.ceil(rows / 2);
|
|
866
|
+
const shiftX = tileW * (clampOffset(config.offsetX) / 100);
|
|
867
|
+
const shiftY = tileH * (clampOffset(config.offsetY) / 100);
|
|
800
868
|
const placements = [];
|
|
801
869
|
for (let j = -halfRows; j <= halfRows; j++) {
|
|
802
870
|
for (let i = -halfCols; i <= halfCols; i++) {
|
|
803
|
-
let x = i * tileW;
|
|
804
|
-
let y = j * tileH;
|
|
871
|
+
let x = i * tileW + shiftX;
|
|
872
|
+
let y = j * tileH + shiftY;
|
|
805
873
|
if (config.mode === "brick-horizontal" && mod2(j) === 1) {
|
|
806
874
|
x += tileW * (config.horizontalOffset / 100);
|
|
807
875
|
} else if (config.mode === "brick-vertical" && mod2(i) === 1) {
|
|
@@ -826,6 +894,10 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH) {
|
|
|
826
894
|
}
|
|
827
895
|
ctx.restore();
|
|
828
896
|
}
|
|
897
|
+
function clampOffset(value) {
|
|
898
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
899
|
+
return Math.max(-100, Math.min(100, value));
|
|
900
|
+
}
|
|
829
901
|
function mod2(n) {
|
|
830
902
|
return (n % 2 + 2) % 2;
|
|
831
903
|
}
|
|
@@ -1087,6 +1159,7 @@ var CanvasEditor = class {
|
|
|
1087
1159
|
}
|
|
1088
1160
|
async fromJSON(state) {
|
|
1089
1161
|
await deserializeEditor(this, state);
|
|
1162
|
+
this.patterns.repinAll();
|
|
1090
1163
|
}
|
|
1091
1164
|
// ─── Export ──────────────────────────────────────────
|
|
1092
1165
|
async toPNG(options) {
|
|
@@ -1281,6 +1354,8 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1281
1354
|
verticalSpacing: 0,
|
|
1282
1355
|
angle: 0,
|
|
1283
1356
|
horizontalOffset: 0,
|
|
1357
|
+
offsetX: 0,
|
|
1358
|
+
offsetY: 0,
|
|
1284
1359
|
rotationStepH: 0,
|
|
1285
1360
|
rotationStepV: 0
|
|
1286
1361
|
};
|
|
@@ -1296,7 +1371,9 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1296
1371
|
PatternManager,
|
|
1297
1372
|
SnapManager,
|
|
1298
1373
|
UnitConverter,
|
|
1374
|
+
applyPatternLocks,
|
|
1299
1375
|
buildPatternDataURL,
|
|
1376
|
+
captureLocks,
|
|
1300
1377
|
clamp,
|
|
1301
1378
|
clearPatternImageCache,
|
|
1302
1379
|
computeTilePositions,
|
|
@@ -1306,6 +1383,7 @@ var DEFAULT_PATTERN_CONFIG = {
|
|
|
1306
1383
|
exportPNG,
|
|
1307
1384
|
exportSVG,
|
|
1308
1385
|
generateId,
|
|
1386
|
+
restoreLocks,
|
|
1309
1387
|
round2,
|
|
1310
1388
|
serializeEditor
|
|
1311
1389
|
});
|