js-draw 1.3.0 → 1.3.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.
@@ -240,29 +240,44 @@ class SVGLoader {
240
240
  // If given, 'supportedAttrs' will have x, y, etc. attributes that were used in computing the transform added to it,
241
241
  // to prevent storing duplicate transform information when saving the component.
242
242
  getTransform(elem, supportedAttrs, computedStyles) {
243
- computedStyles ??= window.getComputedStyle(elem);
244
- let transformProperty = computedStyles.transform;
245
- if (transformProperty === '' || transformProperty === 'none') {
246
- transformProperty = elem.style.transform || 'none';
247
- }
248
- // Prefer the actual .style.transform
249
- // to the computed stylesheet -- in some browsers, the computedStyles version
250
- // can have lower precision.
243
+ // If possible, load the js-draw specific transform attribute
244
+ const highpTransformAttribute = 'data-highp-transform';
245
+ const rawTransformData = elem.getAttribute(highpTransformAttribute);
251
246
  let transform;
252
- try {
253
- transform = math_1.Mat33.fromCSSMatrix(elem.style.transform);
254
- }
255
- catch (_e) {
256
- transform = math_1.Mat33.fromCSSMatrix(transformProperty);
257
- }
258
- const elemX = elem.getAttribute('x');
259
- const elemY = elem.getAttribute('y');
260
- if (elemX || elemY) {
261
- const x = parseFloat(elemX ?? '0');
262
- const y = parseFloat(elemY ?? '0');
263
- if (!isNaN(x) && !isNaN(y)) {
264
- supportedAttrs?.push('x', 'y');
265
- transform = transform.rightMul(math_1.Mat33.translation(math_1.Vec2.of(x, y)));
247
+ if (rawTransformData) {
248
+ try {
249
+ transform = math_1.Mat33.fromCSSMatrix(rawTransformData);
250
+ supportedAttrs?.push(highpTransformAttribute);
251
+ }
252
+ catch (e) {
253
+ console.warn(`Unable to parse raw transform data, ${rawTransformData}. Falling back to CSS data.`);
254
+ }
255
+ }
256
+ if (!transform) {
257
+ computedStyles ??= window.getComputedStyle(elem);
258
+ let transformProperty = computedStyles.transform;
259
+ if (transformProperty === '' || transformProperty === 'none') {
260
+ transformProperty = elem.style.transform || 'none';
261
+ }
262
+ // Prefer the actual .style.transform
263
+ // to the computed stylesheet -- in some browsers, the computedStyles version
264
+ // can have lower precision.
265
+ try {
266
+ transform = math_1.Mat33.fromCSSMatrix(elem.style.transform);
267
+ }
268
+ catch (_e) {
269
+ console.warn('matrix parse error', _e);
270
+ transform = math_1.Mat33.fromCSSMatrix(transformProperty);
271
+ }
272
+ const elemX = elem.getAttribute('x');
273
+ const elemY = elem.getAttribute('y');
274
+ if (elemX || elemY) {
275
+ const x = parseFloat(elemX ?? '0');
276
+ const y = parseFloat(elemY ?? '0');
277
+ if (!isNaN(x) && !isNaN(y)) {
278
+ supportedAttrs?.push('x', 'y');
279
+ transform = transform.rightMul(math_1.Mat33.translation(math_1.Vec2.of(x, y)));
280
+ }
266
281
  }
267
282
  }
268
283
  return transform;
@@ -165,7 +165,8 @@ class BackgroundComponent extends AbstractComponent_1.default {
165
165
  }
166
166
  generateGridPath(visibleRect) {
167
167
  const contentBBox = this.getFullBoundingBox(visibleRect);
168
- const targetRect = visibleRect?.grownBy(this.gridStrokeWidth)?.intersection(contentBBox) ?? contentBBox;
168
+ // .grownBy acts on all sides, so we need only grow by strokeWidth / 2 (1 * the stroke radius)
169
+ const targetRect = (visibleRect?.intersection(contentBBox) ?? contentBBox).grownBy(this.gridStrokeWidth / 2);
169
170
  const roundDownToGrid = (coord) => Math.floor(coord / this.gridSize) * this.gridSize;
170
171
  const roundUpToGrid = (coord) => Math.ceil(coord / this.gridSize) * this.gridSize;
171
172
  const startY = roundUpToGrid(targetRect.y);
@@ -135,26 +135,18 @@ class SVGRenderer extends AbstractRenderer_1.default {
135
135
  }
136
136
  // Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
137
137
  // Otherwise, just uses a `matrix`.
138
- transformFrom(elemTransform, elem, inCanvasSpace = false, setXY = true) {
139
- let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
140
- const translation = transform.transformVec2(math_1.Vec2.zero);
141
- if (setXY) {
142
- transform = transform.rightMul(math_1.Mat33.translation(translation.times(-1)));
143
- }
138
+ transformFrom(elemTransform, elem, inCanvasSpace = false) {
139
+ const transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
144
140
  if (!transform.eq(math_1.Mat33.identity)) {
145
- elem.style.transform = `matrix(
146
- ${transform.a1}, ${transform.b1},
147
- ${transform.a2}, ${transform.b2},
148
- ${transform.a3}, ${transform.b3}
149
- )`;
141
+ const matrixString = transform.toCSSMatrix();
142
+ elem.style.transform = matrixString;
143
+ // Most browsers round the components of CSS transforms.
144
+ // Include a higher precision copy of the element's transform.
145
+ elem.setAttribute('data-highp-transform', matrixString);
150
146
  }
151
147
  else {
152
148
  elem.style.transform = '';
153
149
  }
154
- if (setXY) {
155
- elem.setAttribute('x', `${(0, math_1.toRoundedString)(translation.x)}`);
156
- elem.setAttribute('y', `${(0, math_1.toRoundedString)(translation.y)}`);
157
- }
158
150
  }
159
151
  drawText(text, transform, style) {
160
152
  const applyTextStyles = (elem, style) => {
@@ -190,10 +182,7 @@ class SVGRenderer extends AbstractRenderer_1.default {
190
182
  if (!this.textContainer) {
191
183
  const container = document.createElementNS(svgNameSpace, 'text');
192
184
  container.appendChild(document.createTextNode(text));
193
- // Don't set .x/.y properties (just use .style.transform).
194
- // Child nodes aren't translated by .x/.y properties, but are by .style.transform.
195
- const setXY = false;
196
- this.transformFrom(transform, container, true, setXY);
185
+ this.transformFrom(transform, container, true);
197
186
  applyTextStyles(container, style);
198
187
  this.elem.appendChild(container);
199
188
  this.objectElems?.push(container);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
- number: '1.3.0',
4
+ number: '1.3.1',
5
5
  };
@@ -211,29 +211,44 @@ export default class SVGLoader {
211
211
  // If given, 'supportedAttrs' will have x, y, etc. attributes that were used in computing the transform added to it,
212
212
  // to prevent storing duplicate transform information when saving the component.
213
213
  getTransform(elem, supportedAttrs, computedStyles) {
214
- computedStyles ??= window.getComputedStyle(elem);
215
- let transformProperty = computedStyles.transform;
216
- if (transformProperty === '' || transformProperty === 'none') {
217
- transformProperty = elem.style.transform || 'none';
218
- }
219
- // Prefer the actual .style.transform
220
- // to the computed stylesheet -- in some browsers, the computedStyles version
221
- // can have lower precision.
214
+ // If possible, load the js-draw specific transform attribute
215
+ const highpTransformAttribute = 'data-highp-transform';
216
+ const rawTransformData = elem.getAttribute(highpTransformAttribute);
222
217
  let transform;
223
- try {
224
- transform = Mat33.fromCSSMatrix(elem.style.transform);
225
- }
226
- catch (_e) {
227
- transform = Mat33.fromCSSMatrix(transformProperty);
228
- }
229
- const elemX = elem.getAttribute('x');
230
- const elemY = elem.getAttribute('y');
231
- if (elemX || elemY) {
232
- const x = parseFloat(elemX ?? '0');
233
- const y = parseFloat(elemY ?? '0');
234
- if (!isNaN(x) && !isNaN(y)) {
235
- supportedAttrs?.push('x', 'y');
236
- transform = transform.rightMul(Mat33.translation(Vec2.of(x, y)));
218
+ if (rawTransformData) {
219
+ try {
220
+ transform = Mat33.fromCSSMatrix(rawTransformData);
221
+ supportedAttrs?.push(highpTransformAttribute);
222
+ }
223
+ catch (e) {
224
+ console.warn(`Unable to parse raw transform data, ${rawTransformData}. Falling back to CSS data.`);
225
+ }
226
+ }
227
+ if (!transform) {
228
+ computedStyles ??= window.getComputedStyle(elem);
229
+ let transformProperty = computedStyles.transform;
230
+ if (transformProperty === '' || transformProperty === 'none') {
231
+ transformProperty = elem.style.transform || 'none';
232
+ }
233
+ // Prefer the actual .style.transform
234
+ // to the computed stylesheet -- in some browsers, the computedStyles version
235
+ // can have lower precision.
236
+ try {
237
+ transform = Mat33.fromCSSMatrix(elem.style.transform);
238
+ }
239
+ catch (_e) {
240
+ console.warn('matrix parse error', _e);
241
+ transform = Mat33.fromCSSMatrix(transformProperty);
242
+ }
243
+ const elemX = elem.getAttribute('x');
244
+ const elemY = elem.getAttribute('y');
245
+ if (elemX || elemY) {
246
+ const x = parseFloat(elemX ?? '0');
247
+ const y = parseFloat(elemY ?? '0');
248
+ if (!isNaN(x) && !isNaN(y)) {
249
+ supportedAttrs?.push('x', 'y');
250
+ transform = transform.rightMul(Mat33.translation(Vec2.of(x, y)));
251
+ }
237
252
  }
238
253
  }
239
254
  return transform;
@@ -136,7 +136,8 @@ export default class BackgroundComponent extends AbstractComponent {
136
136
  }
137
137
  generateGridPath(visibleRect) {
138
138
  const contentBBox = this.getFullBoundingBox(visibleRect);
139
- const targetRect = visibleRect?.grownBy(this.gridStrokeWidth)?.intersection(contentBBox) ?? contentBBox;
139
+ // .grownBy acts on all sides, so we need only grow by strokeWidth / 2 (1 * the stroke radius)
140
+ const targetRect = (visibleRect?.intersection(contentBBox) ?? contentBBox).grownBy(this.gridStrokeWidth / 2);
140
141
  const roundDownToGrid = (coord) => Math.floor(coord / this.gridSize) * this.gridSize;
141
142
  const roundUpToGrid = (coord) => Math.ceil(coord / this.gridSize) * this.gridSize;
142
143
  const startY = roundUpToGrid(targetRect.y);
@@ -129,26 +129,18 @@ export default class SVGRenderer extends AbstractRenderer {
129
129
  }
130
130
  // Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
131
131
  // Otherwise, just uses a `matrix`.
132
- transformFrom(elemTransform, elem, inCanvasSpace = false, setXY = true) {
133
- let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
134
- const translation = transform.transformVec2(Vec2.zero);
135
- if (setXY) {
136
- transform = transform.rightMul(Mat33.translation(translation.times(-1)));
137
- }
132
+ transformFrom(elemTransform, elem, inCanvasSpace = false) {
133
+ const transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
138
134
  if (!transform.eq(Mat33.identity)) {
139
- elem.style.transform = `matrix(
140
- ${transform.a1}, ${transform.b1},
141
- ${transform.a2}, ${transform.b2},
142
- ${transform.a3}, ${transform.b3}
143
- )`;
135
+ const matrixString = transform.toCSSMatrix();
136
+ elem.style.transform = matrixString;
137
+ // Most browsers round the components of CSS transforms.
138
+ // Include a higher precision copy of the element's transform.
139
+ elem.setAttribute('data-highp-transform', matrixString);
144
140
  }
145
141
  else {
146
142
  elem.style.transform = '';
147
143
  }
148
- if (setXY) {
149
- elem.setAttribute('x', `${toRoundedString(translation.x)}`);
150
- elem.setAttribute('y', `${toRoundedString(translation.y)}`);
151
- }
152
144
  }
153
145
  drawText(text, transform, style) {
154
146
  const applyTextStyles = (elem, style) => {
@@ -184,10 +176,7 @@ export default class SVGRenderer extends AbstractRenderer {
184
176
  if (!this.textContainer) {
185
177
  const container = document.createElementNS(svgNameSpace, 'text');
186
178
  container.appendChild(document.createTextNode(text));
187
- // Don't set .x/.y properties (just use .style.transform).
188
- // Child nodes aren't translated by .x/.y properties, but are by .style.transform.
189
- const setXY = false;
190
- this.transformFrom(transform, container, true, setXY);
179
+ this.transformFrom(transform, container, true);
191
180
  applyTextStyles(container, style);
192
181
  this.elem.appendChild(container);
193
182
  this.objectElems?.push(container);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,3 @@
1
1
  export default {
2
- number: '1.3.0',
2
+ number: '1.3.1',
3
3
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "types": "./dist/mjs/lib.d.ts",
6
6
  "main": "./dist/cjs/lib.js",
@@ -64,7 +64,7 @@
64
64
  "postpack": "ts-node tools/copyREADME.ts revert"
65
65
  },
66
66
  "dependencies": {
67
- "@js-draw/math": "^1.3.0",
67
+ "@js-draw/math": "^1.3.1",
68
68
  "@melloware/coloris": "0.21.0"
69
69
  },
70
70
  "devDependencies": {
@@ -86,5 +86,5 @@
86
86
  "freehand",
87
87
  "svg"
88
88
  ],
89
- "gitHead": "46b3d8f819f8e083f6e3e1d01e027e4311355456"
89
+ "gitHead": "65af7ec944f70b69b2a4b07d98e5bb92eeeca029"
90
90
  }