js-draw 0.7.1 → 0.7.2
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/CHANGELOG.md +6 -0
- package/CONTRIBUTING.md +75 -0
- package/dist/bundle.js +1 -1
- package/dist/src/SVGLoader.js +1 -0
- package/dist/src/rendering/renderers/SVGRenderer.js +19 -7
- package/dist/src/tools/TextTool.js +4 -2
- package/package.json +1 -1
- package/src/SVGLoader.ts +1 -0
- package/src/rendering/renderers/SVGRenderer.ts +23 -7
- package/src/tools/TextTool.ts +5 -2
package/dist/src/SVGLoader.js
CHANGED
@@ -177,6 +177,7 @@ export default class SVGLoader {
|
|
177
177
|
else if (child.nodeType === Node.ELEMENT_NODE) {
|
178
178
|
const subElem = child;
|
179
179
|
if (subElem.tagName.toLowerCase() === 'tspan') {
|
180
|
+
// FIXME: tspan's (x, y) components are absolute, not relative to the parent.
|
180
181
|
contentList.push(this.makeText(subElem));
|
181
182
|
}
|
182
183
|
else {
|
@@ -83,11 +83,14 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
83
83
|
}
|
84
84
|
this.lastPathString.push(path.toString());
|
85
85
|
}
|
86
|
-
// Apply [elemTransform] to [elem].
|
87
|
-
|
86
|
+
// Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
|
87
|
+
// Otherwise, just uses a `matrix`.
|
88
|
+
transformFrom(elemTransform, elem, inCanvasSpace = false, setXY = true) {
|
88
89
|
let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
|
89
90
|
const translation = transform.transformVec2(Vec2.zero);
|
90
|
-
|
91
|
+
if (setXY) {
|
92
|
+
transform = transform.rightMul(Mat33.translation(translation.times(-1)));
|
93
|
+
}
|
91
94
|
if (!transform.eq(Mat33.identity)) {
|
92
95
|
elem.style.transform = `matrix(
|
93
96
|
${transform.a1}, ${transform.b1},
|
@@ -98,8 +101,10 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
98
101
|
else {
|
99
102
|
elem.style.transform = '';
|
100
103
|
}
|
101
|
-
|
102
|
-
|
104
|
+
if (setXY) {
|
105
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
106
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
107
|
+
}
|
103
108
|
}
|
104
109
|
drawText(text, transform, style) {
|
105
110
|
var _a;
|
@@ -120,7 +125,10 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
120
125
|
if (!this.textContainer) {
|
121
126
|
const container = document.createElementNS(svgNameSpace, 'text');
|
122
127
|
container.appendChild(document.createTextNode(text));
|
123
|
-
|
128
|
+
// Don't set .x/.y properties (just use .style.transform).
|
129
|
+
// Child nodes aren't translated by .x/.y properties, but are by .style.transform.
|
130
|
+
const setXY = false;
|
131
|
+
this.transformFrom(transform, container, true, setXY);
|
124
132
|
applyTextStyles(container, style);
|
125
133
|
this.elem.appendChild(container);
|
126
134
|
(_a = this.objectElems) === null || _a === void 0 ? void 0 : _a.push(container);
|
@@ -133,8 +141,12 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
133
141
|
const elem = document.createElementNS(svgNameSpace, 'tspan');
|
134
142
|
elem.appendChild(document.createTextNode(text));
|
135
143
|
this.textContainer.appendChild(elem);
|
144
|
+
// Make .x/.y relative to the parent.
|
136
145
|
transform = this.textContainerTransform.inverse().rightMul(transform);
|
137
|
-
|
146
|
+
// .style.transform does nothing to tspan elements. As such, we need to set x/y:
|
147
|
+
const translation = transform.transformVec2(Vec2.zero);
|
148
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
149
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
138
150
|
applyTextStyles(elem, style);
|
139
151
|
}
|
140
152
|
}
|
@@ -119,7 +119,7 @@ export default class TextTool extends BaseTool {
|
|
119
119
|
this.textInputElem = document.createElement('textarea');
|
120
120
|
this.textInputElem.value = initialText;
|
121
121
|
this.textInputElem.style.display = 'inline-block';
|
122
|
-
this.textTargetPosition = textCanvasPos;
|
122
|
+
this.textTargetPosition = this.editor.viewport.roundPoint(textCanvasPos);
|
123
123
|
this.textRotation = -this.editor.viewport.getRotationAngle();
|
124
124
|
this.textScale = Vec2.of(1, 1).times(this.editor.viewport.getSizeOfPixelOnCanvas());
|
125
125
|
this.updateTextInput();
|
@@ -172,6 +172,8 @@ export default class TextTool extends BaseTool {
|
|
172
172
|
const testRegion = Rect2.fromCorners(canvasPos.minus(halfTestRegionSize), canvasPos.plus(halfTestRegionSize));
|
173
173
|
const targetNodes = this.editor.image.getElementsIntersectingRegion(testRegion);
|
174
174
|
const targetTextNodes = targetNodes.filter(node => node instanceof TextComponent);
|
175
|
+
// End any TextNodes we're currently editing.
|
176
|
+
this.flushInput();
|
175
177
|
if (targetTextNodes.length > 0) {
|
176
178
|
const targetNode = targetTextNodes[targetTextNodes.length - 1];
|
177
179
|
this.setTextStyle(targetNode.getTextStyle());
|
@@ -227,7 +229,7 @@ export default class TextTool extends BaseTool {
|
|
227
229
|
}
|
228
230
|
setTextStyle(style) {
|
229
231
|
// Copy the style — we may change parts of it.
|
230
|
-
this.textStyle = Object.assign({}, style);
|
232
|
+
this.textStyle = Object.assign(Object.assign({}, style), { renderingStyle: Object.assign({}, style.renderingStyle) });
|
231
233
|
this.dispatchUpdateEvent();
|
232
234
|
}
|
233
235
|
}
|
package/package.json
CHANGED
package/src/SVGLoader.ts
CHANGED
@@ -218,6 +218,7 @@ export default class SVGLoader implements ImageLoader {
|
|
218
218
|
} else if (child.nodeType === Node.ELEMENT_NODE) {
|
219
219
|
const subElem = child as SVGElement;
|
220
220
|
if (subElem.tagName.toLowerCase() === 'tspan') {
|
221
|
+
// FIXME: tspan's (x, y) components are absolute, not relative to the parent.
|
221
222
|
contentList.push(this.makeText(subElem as SVGTSpanElement));
|
222
223
|
} else {
|
223
224
|
throw new Error(`Unrecognized text child element: ${subElem}`);
|
@@ -99,11 +99,15 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
99
99
|
this.lastPathString.push(path.toString());
|
100
100
|
}
|
101
101
|
|
102
|
-
// Apply [elemTransform] to [elem].
|
103
|
-
|
102
|
+
// Apply [elemTransform] to [elem]. Uses both a `matrix` and `.x`, `.y` properties if `setXY` is true.
|
103
|
+
// Otherwise, just uses a `matrix`.
|
104
|
+
private transformFrom(elemTransform: Mat33, elem: SVGElement, inCanvasSpace: boolean = false, setXY: boolean = true) {
|
104
105
|
let transform = !inCanvasSpace ? this.getCanvasToScreenTransform().rightMul(elemTransform) : elemTransform;
|
105
106
|
const translation = transform.transformVec2(Vec2.zero);
|
106
|
-
|
107
|
+
|
108
|
+
if (setXY) {
|
109
|
+
transform = transform.rightMul(Mat33.translation(translation.times(-1)));
|
110
|
+
}
|
107
111
|
|
108
112
|
if (!transform.eq(Mat33.identity)) {
|
109
113
|
elem.style.transform = `matrix(
|
@@ -115,8 +119,10 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
115
119
|
elem.style.transform = '';
|
116
120
|
}
|
117
121
|
|
118
|
-
|
119
|
-
|
122
|
+
if (setXY) {
|
123
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
124
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
125
|
+
}
|
120
126
|
}
|
121
127
|
|
122
128
|
private textContainer: SVGTextElement|null = null;
|
@@ -140,7 +146,11 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
140
146
|
if (!this.textContainer) {
|
141
147
|
const container = document.createElementNS(svgNameSpace, 'text');
|
142
148
|
container.appendChild(document.createTextNode(text));
|
143
|
-
|
149
|
+
|
150
|
+
// Don't set .x/.y properties (just use .style.transform).
|
151
|
+
// Child nodes aren't translated by .x/.y properties, but are by .style.transform.
|
152
|
+
const setXY = false;
|
153
|
+
this.transformFrom(transform, container, true, setXY);
|
144
154
|
applyTextStyles(container, style);
|
145
155
|
|
146
156
|
this.elem.appendChild(container);
|
@@ -154,8 +164,14 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
154
164
|
elem.appendChild(document.createTextNode(text));
|
155
165
|
this.textContainer.appendChild(elem);
|
156
166
|
|
167
|
+
// Make .x/.y relative to the parent.
|
157
168
|
transform = this.textContainerTransform!.inverse().rightMul(transform);
|
158
|
-
|
169
|
+
|
170
|
+
// .style.transform does nothing to tspan elements. As such, we need to set x/y:
|
171
|
+
const translation = transform.transformVec2(Vec2.zero);
|
172
|
+
elem.setAttribute('x', `${toRoundedString(translation.x)}`);
|
173
|
+
elem.setAttribute('y', `${toRoundedString(translation.y)}`);
|
174
|
+
|
159
175
|
applyTextStyles(elem, style);
|
160
176
|
}
|
161
177
|
}
|
package/src/tools/TextTool.ts
CHANGED
@@ -148,7 +148,7 @@ export default class TextTool extends BaseTool {
|
|
148
148
|
this.textInputElem = document.createElement('textarea');
|
149
149
|
this.textInputElem.value = initialText;
|
150
150
|
this.textInputElem.style.display = 'inline-block';
|
151
|
-
this.textTargetPosition = textCanvasPos;
|
151
|
+
this.textTargetPosition = this.editor.viewport.roundPoint(textCanvasPos);
|
152
152
|
this.textRotation = -this.editor.viewport.getRotationAngle();
|
153
153
|
this.textScale = Vec2.of(1, 1).times(this.editor.viewport.getSizeOfPixelOnCanvas());
|
154
154
|
this.updateTextInput();
|
@@ -210,6 +210,9 @@ export default class TextTool extends BaseTool {
|
|
210
210
|
const targetNodes = this.editor.image.getElementsIntersectingRegion(testRegion);
|
211
211
|
const targetTextNodes = targetNodes.filter(node => node instanceof TextComponent) as TextComponent[];
|
212
212
|
|
213
|
+
// End any TextNodes we're currently editing.
|
214
|
+
this.flushInput();
|
215
|
+
|
213
216
|
if (targetTextNodes.length > 0) {
|
214
217
|
const targetNode = targetTextNodes[targetTextNodes.length - 1];
|
215
218
|
this.setTextStyle(targetNode.getTextStyle());
|
@@ -290,7 +293,7 @@ export default class TextTool extends BaseTool {
|
|
290
293
|
|
291
294
|
private setTextStyle(style: TextStyle) {
|
292
295
|
// Copy the style — we may change parts of it.
|
293
|
-
this.textStyle = {...style};
|
296
|
+
this.textStyle = { ...style, renderingStyle: { ...style.renderingStyle } };
|
294
297
|
this.dispatchUpdateEvent();
|
295
298
|
}
|
296
299
|
}
|