@overtone-art/canvas-editor-core 0.5.0 → 0.5.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.mts +42 -2
- package/dist/index.d.ts +42 -2
- package/dist/index.global.js +28 -28
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +77 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +77 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -886,8 +886,10 @@ function computeTilePositions(config, targetW, targetH, baseW, baseH, origin) {
|
|
|
886
886
|
}
|
|
887
887
|
return placements;
|
|
888
888
|
}
|
|
889
|
-
function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin) {
|
|
889
|
+
function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin, draw) {
|
|
890
890
|
const anchor = origin ?? { x: targetW / 2, y: targetH / 2 };
|
|
891
|
+
const drawW = draw && draw.width > 0 ? draw.width : baseW;
|
|
892
|
+
const drawH = draw && draw.height > 0 ? draw.height : baseH;
|
|
891
893
|
ctx.save();
|
|
892
894
|
ctx.translate(anchor.x, anchor.y);
|
|
893
895
|
ctx.rotate(config.angle * Math.PI / 180);
|
|
@@ -895,7 +897,7 @@ function drawTiles(ctx, img, config, targetW, targetH, baseW, baseH, origin) {
|
|
|
895
897
|
ctx.save();
|
|
896
898
|
ctx.translate(tile.x, tile.y);
|
|
897
899
|
ctx.rotate(tile.rotation);
|
|
898
|
-
ctx.drawImage(img, -
|
|
900
|
+
ctx.drawImage(img, -drawW / 2, -drawH / 2, drawW, drawH);
|
|
899
901
|
ctx.restore();
|
|
900
902
|
}
|
|
901
903
|
ctx.restore();
|
|
@@ -917,6 +919,7 @@ function mod2(n) {
|
|
|
917
919
|
var MAX_SNAPSHOT_PIXELS = 16e6;
|
|
918
920
|
var MAX_SNAPSHOT_SCALE = 8;
|
|
919
921
|
var SNAPSHOT_SHRINK_FACTOR = 2;
|
|
922
|
+
var TILE_BLEED_DEVICE_PX = 2;
|
|
920
923
|
var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
921
924
|
static type = "TiledPattern";
|
|
922
925
|
/** The layer's real object. Never rendered directly (it is kept at opacity 0). */
|
|
@@ -1006,7 +1009,8 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1006
1009
|
const { width, height } = this.area;
|
|
1007
1010
|
if (width <= 0 || height <= 0) return;
|
|
1008
1011
|
const { tileW, tileH } = this.liveTile();
|
|
1009
|
-
const
|
|
1012
|
+
const scale = contextScale(ctx);
|
|
1013
|
+
const snapshot = this.ensureSnapshot(scale * (tileW / Math.max(1, this.baseW())));
|
|
1010
1014
|
if (!snapshot) return;
|
|
1011
1015
|
const centre = this.getCenterPoint();
|
|
1012
1016
|
const angle = this.liveAngle();
|
|
@@ -1017,7 +1021,17 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1017
1021
|
const shift = this.shiftVector(tileW, tileH, angle);
|
|
1018
1022
|
const origin = { x: centre.x - shift.x, y: centre.y - shift.y };
|
|
1019
1023
|
const config = { ...this.config, angle, offsetX: 0, offsetY: 0 };
|
|
1020
|
-
drawTiles(
|
|
1024
|
+
drawTiles(
|
|
1025
|
+
ctx,
|
|
1026
|
+
snapshot,
|
|
1027
|
+
config,
|
|
1028
|
+
width,
|
|
1029
|
+
height,
|
|
1030
|
+
tileW,
|
|
1031
|
+
tileH,
|
|
1032
|
+
origin,
|
|
1033
|
+
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX / scale)
|
|
1034
|
+
);
|
|
1021
1035
|
ctx.restore();
|
|
1022
1036
|
}
|
|
1023
1037
|
/** Raster fallback for SVG export — one `<image>` covering the print area. */
|
|
@@ -1043,7 +1057,9 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1043
1057
|
height,
|
|
1044
1058
|
tileW,
|
|
1045
1059
|
tileH,
|
|
1046
|
-
{ x: centre.x - shift.x, y: centre.y - shift.y }
|
|
1060
|
+
{ x: centre.x - shift.x, y: centre.y - shift.y },
|
|
1061
|
+
// The fallback raster is built at 1:1, so a device pixel is a canvas unit.
|
|
1062
|
+
this.drawSize(tileW, tileH, TILE_BLEED_DEVICE_PX)
|
|
1047
1063
|
);
|
|
1048
1064
|
return [
|
|
1049
1065
|
`<g transform="rotate(${-angle}) translate(${-centre.x} ${-centre.y})">`,
|
|
@@ -1083,18 +1099,64 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1083
1099
|
});
|
|
1084
1100
|
return Promise.resolve(copy);
|
|
1085
1101
|
}
|
|
1102
|
+
/**
|
|
1103
|
+
* The source's tile footprint: its bounding box, minus a stroke it reserves
|
|
1104
|
+
* room for but never paints.
|
|
1105
|
+
*
|
|
1106
|
+
* fabric keeps `strokeWidth` inside an object's box whether or not a `stroke`
|
|
1107
|
+
* colour paints it, and the built-in shapes arrive with `strokeWidth: 1` and no
|
|
1108
|
+
* stroke. Stepping the grid by that box puts a transparent seam between every
|
|
1109
|
+
* pair of neighbours at zero spacing: the artwork is a pixel narrower than the
|
|
1110
|
+
* box it is stepped by.
|
|
1111
|
+
*/
|
|
1112
|
+
sourceBox() {
|
|
1113
|
+
const rect = this.source.getBoundingRect();
|
|
1114
|
+
if (paintsStroke(this.source)) return { width: rect.width, height: rect.height };
|
|
1115
|
+
const dims = this.source._getTransformedDimensions({ strokeWidth: 0 });
|
|
1116
|
+
const radians = (this.source.angle ?? 0) * Math.PI / 180;
|
|
1117
|
+
const cos = Math.abs(Math.cos(radians));
|
|
1118
|
+
const sin = Math.abs(Math.sin(radians));
|
|
1119
|
+
return {
|
|
1120
|
+
width: dims.x * cos + dims.y * sin,
|
|
1121
|
+
height: dims.x * sin + dims.y * cos
|
|
1122
|
+
};
|
|
1123
|
+
}
|
|
1086
1124
|
/** The source's on-canvas width, before the tile scale. */
|
|
1087
1125
|
baseW() {
|
|
1088
|
-
return Math.max(1, this.
|
|
1126
|
+
return Math.max(1, this.sourceBox().width);
|
|
1127
|
+
}
|
|
1128
|
+
/** The source's on-canvas height, before the tile scale. */
|
|
1129
|
+
baseH() {
|
|
1130
|
+
return Math.max(1, this.sourceBox().height);
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* Size to paint the snapshot at, for a tile of `tileW × tileH`.
|
|
1134
|
+
*
|
|
1135
|
+
* The snapshot is NOT exactly the source's box: `toCanvasElement` rounds the
|
|
1136
|
+
* raster up to whole pixels and pads it further for a shadow. Painting it into
|
|
1137
|
+
* the tile step would squeeze the artwork inside that padding — every tile
|
|
1138
|
+
* shrinks by up to a pixel and the grid shows transparent seams at zero
|
|
1139
|
+
* spacing. So the bitmap is painted at its own footprint, scaled by the same
|
|
1140
|
+
* factor the tile is, and the step stays the source's box.
|
|
1141
|
+
*/
|
|
1142
|
+
drawSize(tileW, tileH, bleed = 0) {
|
|
1143
|
+
const snapshot = this.snapshotEl;
|
|
1144
|
+
if (!snapshot || this.snapshotScale <= 0) {
|
|
1145
|
+
return { width: tileW + bleed, height: tileH + bleed };
|
|
1146
|
+
}
|
|
1147
|
+
return {
|
|
1148
|
+
width: snapshot.width / this.snapshotScale * (tileW / this.baseW()) + bleed,
|
|
1149
|
+
height: snapshot.height / this.snapshotScale * (tileH / this.baseH()) + bleed
|
|
1150
|
+
};
|
|
1089
1151
|
}
|
|
1090
1152
|
/** Tile size from the config alone, ignoring any in-flight gesture. */
|
|
1091
1153
|
baseTile() {
|
|
1092
|
-
const
|
|
1154
|
+
const box = this.sourceBox();
|
|
1093
1155
|
const scale = Math.max(1, this.config.scale ?? 100) / 100;
|
|
1094
1156
|
const floor = this.tileFloor();
|
|
1095
1157
|
return {
|
|
1096
|
-
tileW: Math.max(floor,
|
|
1097
|
-
tileH: Math.max(floor,
|
|
1158
|
+
tileW: Math.max(floor, box.width * scale),
|
|
1159
|
+
tileH: Math.max(floor, box.height * scale)
|
|
1098
1160
|
};
|
|
1099
1161
|
}
|
|
1100
1162
|
/** Tile size as drawn right now, including a live scale gesture. */
|
|
@@ -1157,6 +1219,12 @@ var TiledPatternObject = class _TiledPatternObject extends FabricObject {
|
|
|
1157
1219
|
return Math.max(0.05, Math.min(requested, budgeted));
|
|
1158
1220
|
}
|
|
1159
1221
|
};
|
|
1222
|
+
function paintsStroke(object) {
|
|
1223
|
+
if (!object.strokeWidth || object.strokeWidth <= 0) return false;
|
|
1224
|
+
const { stroke } = object;
|
|
1225
|
+
if (typeof stroke === "string") return stroke !== "" && stroke !== "transparent";
|
|
1226
|
+
return stroke !== null && stroke !== void 0;
|
|
1227
|
+
}
|
|
1160
1228
|
function clampPercent(value) {
|
|
1161
1229
|
if (typeof value !== "number" || !Number.isFinite(value)) return 0;
|
|
1162
1230
|
return Math.max(-100, Math.min(100, value));
|