js-draw 0.12.0 → 0.13.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/CHANGELOG.md +14 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Color4.d.ts +12 -0
- package/dist/src/Color4.js +16 -0
- package/dist/src/Editor.d.ts +33 -18
- package/dist/src/Editor.js +22 -19
- package/dist/src/EditorImage.d.ts +12 -0
- package/dist/src/EditorImage.js +12 -0
- package/dist/src/Pointer.d.ts +1 -0
- package/dist/src/Pointer.js +8 -0
- package/dist/src/SVGLoader.d.ts +5 -0
- package/dist/src/SVGLoader.js +6 -1
- package/dist/src/Viewport.d.ts +30 -1
- package/dist/src/Viewport.js +39 -9
- package/dist/src/commands/invertCommand.js +1 -1
- package/dist/src/components/AbstractComponent.d.ts +19 -0
- package/dist/src/components/AbstractComponent.js +17 -2
- package/dist/src/lib.d.ts +6 -3
- package/dist/src/lib.js +4 -1
- package/dist/src/math/Mat33.d.ts +1 -1
- package/dist/src/math/Mat33.js +1 -1
- package/dist/src/rendering/Display.d.ts +9 -11
- package/dist/src/rendering/Display.js +12 -14
- package/dist/src/rendering/lib.d.ts +3 -0
- package/dist/src/rendering/lib.js +3 -0
- package/dist/src/rendering/renderers/DummyRenderer.js +2 -2
- package/dist/src/rendering/renderers/SVGRenderer.js +4 -0
- package/dist/src/toolbar/IconProvider.d.ts +1 -1
- package/dist/src/toolbar/IconProvider.js +90 -29
- package/dist/src/toolbar/makeColorInput.js +8 -1
- package/dist/src/tools/PanZoom.d.ts +3 -1
- package/dist/src/tools/PanZoom.js +31 -6
- package/dist/src/tools/PasteHandler.d.ts +11 -4
- package/dist/src/tools/PasteHandler.js +12 -5
- package/dist/src/tools/Pen.d.ts +7 -2
- package/dist/src/tools/Pen.js +39 -6
- package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -0
- package/dist/src/tools/SelectionTool/SelectionHandle.js +6 -0
- package/dist/src/tools/SelectionTool/SelectionTool.d.ts +3 -1
- package/dist/src/tools/SelectionTool/SelectionTool.js +53 -15
- package/dist/src/tools/ToolSwitcherShortcut.d.ts +8 -0
- package/dist/src/tools/ToolSwitcherShortcut.js +9 -3
- package/dist/src/tools/UndoRedoShortcut.js +2 -4
- package/package.json +2 -2
- package/src/Color4.test.ts +11 -0
- package/src/Color4.ts +22 -0
- package/src/Editor.ts +36 -22
- package/src/EditorImage.ts +12 -0
- package/src/Pointer.ts +19 -0
- package/src/SVGLoader.ts +6 -1
- package/src/Viewport.ts +50 -11
- package/src/commands/invertCommand.ts +1 -1
- package/src/components/AbstractComponent.ts +33 -2
- package/src/lib.ts +6 -3
- package/src/math/Mat33.ts +1 -1
- package/src/rendering/Display.ts +12 -15
- package/src/rendering/RenderingStyle.ts +1 -1
- package/src/rendering/lib.ts +4 -0
- package/src/rendering/renderers/DummyRenderer.ts +2 -3
- package/src/rendering/renderers/SVGRenderer.ts +4 -0
- package/src/rendering/renderers/TextOnlyRenderer.ts +0 -1
- package/src/toolbar/HTMLToolbar.ts +1 -1
- package/src/toolbar/IconProvider.ts +98 -31
- package/src/toolbar/makeColorInput.ts +9 -1
- package/src/tools/PanZoom.ts +37 -7
- package/src/tools/PasteHandler.ts +12 -6
- package/src/tools/Pen.test.ts +44 -1
- package/src/tools/Pen.ts +53 -8
- package/src/tools/SelectionTool/SelectionHandle.ts +9 -0
- package/src/tools/SelectionTool/SelectionTool.ts +67 -15
- package/src/tools/ToolSwitcherShortcut.ts +10 -5
- package/src/tools/UndoRedoShortcut.ts +2 -5
- package/typedoc.json +2 -2
@@ -1,17 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
|
3
|
-
*
|
4
|
-
* @example
|
5
|
-
* ```
|
6
|
-
* const editor = new Editor(document.body);
|
7
|
-
* const w = editor.display.width;
|
8
|
-
* const h = editor.display.height;
|
9
|
-
* const center = Vec2.of(w / 2, h / 2);
|
10
|
-
* const colorAtCenter = editor.display.getColorAt(center);
|
11
|
-
* ```
|
12
|
-
*
|
13
|
-
* @packageDocumentation
|
14
|
-
*/
|
15
1
|
import CanvasRenderer from './renderers/CanvasRenderer';
|
16
2
|
import { EditorEventType } from '../types';
|
17
3
|
import DummyRenderer from './renderers/DummyRenderer';
|
@@ -25,6 +11,18 @@ export var RenderingMode;
|
|
25
11
|
RenderingMode[RenderingMode["CanvasRenderer"] = 1] = "CanvasRenderer";
|
26
12
|
// SVGRenderer is not supported by the main display
|
27
13
|
})(RenderingMode || (RenderingMode = {}));
|
14
|
+
/**
|
15
|
+
* Handles `HTMLCanvasElement`s (or other drawing surfaces if being used) used to display the editor's contents.
|
16
|
+
*
|
17
|
+
* @example
|
18
|
+
* ```
|
19
|
+
* const editor = new Editor(document.body);
|
20
|
+
* const w = editor.display.width;
|
21
|
+
* const h = editor.display.height;
|
22
|
+
* const center = Vec2.of(w / 2, h / 2);
|
23
|
+
* const colorAtCenter = editor.display.getColorAt(center);
|
24
|
+
* ```
|
25
|
+
*/
|
28
26
|
export default class Display {
|
29
27
|
/** @internal */
|
30
28
|
constructor(editor, mode, parent) {
|
@@ -1,6 +1,6 @@
|
|
1
|
-
// Renderer that outputs nothing. Useful for automated tests.
|
2
1
|
import { Vec2 } from '../../math/Vec2';
|
3
2
|
import AbstractRenderer from './AbstractRenderer';
|
3
|
+
// Renderer that outputs almost nothing. Useful for automated tests.
|
4
4
|
export default class DummyRenderer extends AbstractRenderer {
|
5
5
|
constructor(viewport) {
|
6
6
|
super(viewport);
|
@@ -17,7 +17,7 @@ export default class DummyRenderer extends AbstractRenderer {
|
|
17
17
|
}
|
18
18
|
displaySize() {
|
19
19
|
// Do we have a stored viewport size?
|
20
|
-
const viewportSize = this.getViewport().
|
20
|
+
const viewportSize = this.getViewport().getScreenRectSize();
|
21
21
|
// Don't use a 0x0 viewport — DummyRenderer is often used
|
22
22
|
// for tests that run without a display, so pretend we have a
|
23
23
|
// reasonable-sized display.
|
@@ -31,6 +31,10 @@ export default class SVGRenderer extends AbstractRenderer {
|
|
31
31
|
stroke-linecap: round;
|
32
32
|
stroke-linejoin: round;
|
33
33
|
}
|
34
|
+
|
35
|
+
text {
|
36
|
+
white-space: pre;
|
37
|
+
}
|
34
38
|
`.replace(/\s+/g, '');
|
35
39
|
styleSheet.setAttribute('id', renderedStylesheetId);
|
36
40
|
this.elem.appendChild(styleSheet);
|
@@ -21,7 +21,7 @@ export default class IconProvider {
|
|
21
21
|
makeRotationLockIcon(): IconType;
|
22
22
|
makeInsertImageIcon(): IconType;
|
23
23
|
makeTextIcon(textStyle: TextStyle): IconType;
|
24
|
-
makePenIcon(
|
24
|
+
makePenIcon(strokeSize: number, color: string | Color4, rounded?: boolean): IconType;
|
25
25
|
makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory): IconType;
|
26
26
|
makePipetteIcon(color?: Color4): IconType;
|
27
27
|
makeResizeViewportIcon(): IconType;
|
@@ -30,7 +30,7 @@ export default class IconProvider {
|
|
30
30
|
makeUndoIcon() {
|
31
31
|
return this.makeRedoIcon(true);
|
32
32
|
}
|
33
|
-
// @param mirror - reflect across the x-axis
|
33
|
+
// @param mirror - reflect across the x-axis. This parameter is internal.
|
34
34
|
// @returns a redo icon.
|
35
35
|
makeRedoIcon(mirror = false) {
|
36
36
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
@@ -336,45 +336,106 @@ export default class IconProvider {
|
|
336
336
|
icon.appendChild(textNode);
|
337
337
|
return icon;
|
338
338
|
}
|
339
|
-
makePenIcon(
|
339
|
+
makePenIcon(strokeSize, color, rounded) {
|
340
340
|
if (color instanceof Color4) {
|
341
341
|
color = color.toHexString();
|
342
342
|
}
|
343
343
|
const icon = document.createElementNS(svgNamespace, 'svg');
|
344
344
|
icon.setAttribute('viewBox', '0 0 100 100');
|
345
|
-
const
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
345
|
+
const tipThickness = strokeSize / 2;
|
346
|
+
const inkTipPath = `
|
347
|
+
M ${15 - tipThickness},${80 - tipThickness}
|
348
|
+
${15 - tipThickness},${80 + tipThickness}
|
349
|
+
30,83
|
350
|
+
15,65
|
351
|
+
Z
|
352
|
+
`;
|
353
|
+
const trailStartEndY = 80 + tipThickness;
|
354
|
+
const inkTrailPath = `
|
355
|
+
m ${15 - tipThickness * 1.1},${trailStartEndY}
|
356
|
+
c 35,10 55,15 60,30
|
357
|
+
l ${35 + tipThickness * 1.2},${-10 - tipThickness}
|
358
|
+
C 80.47,98.32 50.5,${90 + tipThickness} 20,${trailStartEndY} Z
|
359
|
+
`;
|
360
|
+
const colorBubblePath = `
|
361
|
+
M 72.45,35.67
|
362
|
+
A 10,15 41.8 0 1 55,40.2 10,15 41.8 0 1 57.55,22.3 10,15 41.8 0 1 75,17.8 10,15 41.8 0 1 72.5,35.67
|
363
|
+
Z
|
364
|
+
`;
|
365
|
+
let gripMainPath = 'M 85,-25 25,35 h 10 v 10 h 10 v 10 h 10 v 10 h 10 l -5,10 60,-60 z';
|
366
|
+
let gripShadow1Path = 'M 25,35 H 35 L 90,-15 85,-25 Z';
|
367
|
+
let gripShadow2Path = 'M 60,75 65,65 H 55 l 55,-55 10,5 z';
|
368
|
+
if (rounded) {
|
369
|
+
gripMainPath = 'M 85,-25 25,35 c 15,0 40,30 35,40 l 60,-60 z';
|
370
|
+
gripShadow1Path = 'm 25,35 c 3.92361,0.384473 7.644275,0.980572 10,3 l 55,-53 -5,-10 z';
|
371
|
+
gripShadow2Path = 'M 60,75 C 61,66 59,65 56,59 l 54,-54 10,10 z';
|
354
372
|
}
|
355
|
-
const
|
356
|
-
const
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
373
|
+
const penTipPath = `M 25,35 ${10 - tipThickness / 4},${70 - tipThickness / 2} 20,75 25,85 60,75 70,55 45,25 Z`;
|
374
|
+
const pencilTipColor = Color4.fromHex('#f4d7d7');
|
375
|
+
const tipColor = pencilTipColor.mix(Color4.fromString(color), tipThickness / 40 - 0.1).toHexString();
|
376
|
+
const ink = `
|
377
|
+
<path
|
378
|
+
fill="${checkerboardPatternRef}"
|
379
|
+
d="${inkTipPath}"
|
380
|
+
/>
|
381
|
+
<path
|
382
|
+
fill="${checkerboardPatternRef}"
|
383
|
+
d="${inkTrailPath}"
|
384
|
+
/>
|
385
|
+
<path
|
386
|
+
fill="${color}"
|
387
|
+
d="${inkTipPath}"
|
388
|
+
/>
|
389
|
+
<path
|
390
|
+
fill="${color}"
|
391
|
+
d="${inkTrailPath}"
|
392
|
+
/>
|
393
|
+
`;
|
394
|
+
const penTip = `
|
395
|
+
<path
|
396
|
+
fill="${checkerboardPatternRef}"
|
397
|
+
d="${penTipPath}"
|
398
|
+
/>
|
399
|
+
<path
|
400
|
+
fill="${tipColor}"
|
401
|
+
stroke="${color}"
|
402
|
+
d="${penTipPath}"
|
403
|
+
/>
|
404
|
+
`;
|
405
|
+
const grip = `
|
363
406
|
<path
|
364
|
-
d='M10,10 L90,10 L90,60 L${50 + halfThickness},80 L${50 - halfThickness},80 L10,60 Z'
|
365
407
|
${iconColorStrokeFill}
|
408
|
+
d="${gripMainPath}"
|
366
409
|
/>
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
410
|
+
|
411
|
+
<!-- shadows -->
|
412
|
+
<path
|
413
|
+
fill="rgba(150, 150, 150, 0.3)"
|
414
|
+
d="${gripShadow1Path}"
|
415
|
+
/>
|
416
|
+
<path
|
417
|
+
fill="rgba(100, 100, 100, 0.2)"
|
418
|
+
d="${gripShadow2Path}"
|
419
|
+
/>
|
420
|
+
|
421
|
+
<!-- color bubble -->
|
373
422
|
<path
|
374
|
-
|
375
|
-
|
376
|
-
stroke='${color}'
|
423
|
+
fill="${checkerboardPatternRef}"
|
424
|
+
d="${colorBubblePath}"
|
377
425
|
/>
|
426
|
+
<path
|
427
|
+
fill="${color}"
|
428
|
+
d="${colorBubblePath}"
|
429
|
+
/>
|
430
|
+
`;
|
431
|
+
icon.innerHTML = `
|
432
|
+
<defs>
|
433
|
+
${checkerboardPatternDef}
|
434
|
+
</defs>
|
435
|
+
<g>
|
436
|
+
${ink}
|
437
|
+
${penTip}
|
438
|
+
${grip}
|
378
439
|
</g>
|
379
440
|
`;
|
380
441
|
return icon;
|
@@ -9,7 +9,7 @@ export const makeColorInput = (editor, onColorChange) => {
|
|
9
9
|
colorInput.classList.add('coloris_input');
|
10
10
|
colorInputContainer.classList.add('color-input-container');
|
11
11
|
colorInputContainer.appendChild(colorInput);
|
12
|
-
addPipetteTool(editor, colorInputContainer, (color) => {
|
12
|
+
const pipetteController = addPipetteTool(editor, colorInputContainer, (color) => {
|
13
13
|
colorInput.value = color.toHexString();
|
14
14
|
onInputEnd();
|
15
15
|
// Update the color preview, if it exists (may be managed by Coloris).
|
@@ -41,6 +41,7 @@ export const makeColorInput = (editor, onColorChange) => {
|
|
41
41
|
kind: EditorEventType.ColorPickerToggled,
|
42
42
|
open: true,
|
43
43
|
});
|
44
|
+
pipetteController.cancel();
|
44
45
|
});
|
45
46
|
colorInput.addEventListener('close', () => {
|
46
47
|
editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
@@ -102,5 +103,11 @@ const addPipetteTool = (editor, container, onColorChange) => {
|
|
102
103
|
}
|
103
104
|
};
|
104
105
|
container.appendChild(pipetteButton);
|
106
|
+
return {
|
107
|
+
// Cancel a pipette color selection if one is in progress.
|
108
|
+
cancel: () => {
|
109
|
+
endColorSelectMode();
|
110
|
+
},
|
111
|
+
};
|
105
112
|
};
|
106
113
|
export default makeColorInput;
|
@@ -21,11 +21,12 @@ export default class PanZoom extends BaseTool {
|
|
21
21
|
private editor;
|
22
22
|
private mode;
|
23
23
|
private transform;
|
24
|
-
private lastAngle;
|
25
24
|
private lastDist;
|
26
25
|
private lastScreenCenter;
|
27
26
|
private lastTimestamp;
|
28
27
|
private lastPointerDownTimestamp;
|
28
|
+
private initialTouchAngle;
|
29
|
+
private initialViewportRotation;
|
29
30
|
private inertialScroller;
|
30
31
|
private velocity;
|
31
32
|
constructor(editor: Editor, mode: PanZoomMode, description: string);
|
@@ -34,6 +35,7 @@ export default class PanZoom extends BaseTool {
|
|
34
35
|
onPointerDown({ allPointers: pointers, current: currentPointer }: PointerEvt): boolean;
|
35
36
|
private updateVelocity;
|
36
37
|
private getCenterDelta;
|
38
|
+
private toSnappedRotationDelta;
|
37
39
|
private handleTwoFingerMove;
|
38
40
|
private handleOneFingerMove;
|
39
41
|
onPointerMove({ allPointers }: PointerEvt): void;
|
@@ -78,6 +78,8 @@ export default class PanZoom extends BaseTool {
|
|
78
78
|
this.mode = mode;
|
79
79
|
this.transform = null;
|
80
80
|
this.lastPointerDownTimestamp = 0;
|
81
|
+
this.initialTouchAngle = 0;
|
82
|
+
this.initialViewportRotation = 0;
|
81
83
|
this.inertialScroller = null;
|
82
84
|
this.velocity = null;
|
83
85
|
}
|
@@ -104,9 +106,10 @@ export default class PanZoom extends BaseTool {
|
|
104
106
|
const isRightClick = this.allPointersAreOfType(pointers, PointerDevice.RightButtonMouse);
|
105
107
|
if (allAreTouch && pointers.length === 2 && this.mode & PanZoomMode.TwoFingerTouchGestures) {
|
106
108
|
const { screenCenter, angle, dist } = this.computePinchData(pointers[0], pointers[1]);
|
107
|
-
this.lastAngle = angle;
|
108
109
|
this.lastDist = dist;
|
109
110
|
this.lastScreenCenter = screenCenter;
|
111
|
+
this.initialTouchAngle = angle;
|
112
|
+
this.initialViewportRotation = this.editor.viewport.getRotationAngle();
|
110
113
|
handlingGesture = true;
|
111
114
|
}
|
112
115
|
else if (pointers.length === 1 && ((this.mode & PanZoomMode.OneFingerTouchGestures && allAreTouch)
|
@@ -149,20 +152,42 @@ export default class PanZoom extends BaseTool {
|
|
149
152
|
const delta = this.editor.viewport.screenToCanvasTransform.transformVec3(screenCenter.minus(this.lastScreenCenter));
|
150
153
|
return delta;
|
151
154
|
}
|
155
|
+
// Snaps `angle` to common desired rotations. For example, if `touchAngle` corresponds
|
156
|
+
// to a viewport rotation of 90.1 degrees, this function returns a rotation delta that,
|
157
|
+
// when applied to the viewport, rotates the viewport to 90.0 degrees.
|
158
|
+
//
|
159
|
+
// Returns a snapped rotation delta that, when applied to the viewport, rotates the viewport,
|
160
|
+
// from its position on the last touchDown event, by `touchAngle - initialTouchAngle`.
|
161
|
+
toSnappedRotationDelta(touchAngle) {
|
162
|
+
const deltaAngle = touchAngle - this.initialTouchAngle;
|
163
|
+
let fullRotation = deltaAngle + this.initialViewportRotation;
|
164
|
+
const snapToMultipleOf = Math.PI / 2;
|
165
|
+
const roundedFullRotation = Math.round(fullRotation / snapToMultipleOf) * snapToMultipleOf;
|
166
|
+
// The maximum angle for which we snap the given angle to a multiple of
|
167
|
+
// `snapToMultipleOf`.
|
168
|
+
const maxSnapAngle = 0.07;
|
169
|
+
// Snap the rotation
|
170
|
+
if (Math.abs(fullRotation - roundedFullRotation) < maxSnapAngle) {
|
171
|
+
fullRotation = roundedFullRotation;
|
172
|
+
}
|
173
|
+
return fullRotation - this.editor.viewport.getRotationAngle();
|
174
|
+
}
|
152
175
|
handleTwoFingerMove(allPointers) {
|
153
176
|
const { screenCenter, canvasCenter, angle, dist } = this.computePinchData(allPointers[0], allPointers[1]);
|
154
177
|
const delta = this.getCenterDelta(screenCenter);
|
155
|
-
let
|
178
|
+
let deltaRotation;
|
156
179
|
if (this.isRotationLocked()) {
|
157
|
-
|
180
|
+
deltaRotation = 0;
|
181
|
+
}
|
182
|
+
else {
|
183
|
+
deltaRotation = this.toSnappedRotationDelta(angle);
|
158
184
|
}
|
159
185
|
this.updateVelocity(screenCenter);
|
160
186
|
const transformUpdate = Mat33.translation(delta)
|
161
187
|
.rightMul(Mat33.scaling2D(dist / this.lastDist, canvasCenter))
|
162
|
-
.rightMul(Mat33.zRotation(
|
188
|
+
.rightMul(Mat33.zRotation(deltaRotation, canvasCenter));
|
163
189
|
this.lastScreenCenter = screenCenter;
|
164
190
|
this.lastDist = dist;
|
165
|
-
this.lastAngle = angle;
|
166
191
|
this.transform = Viewport.transformBy(this.transform.transform.rightMul(transformUpdate));
|
167
192
|
}
|
168
193
|
handleOneFingerMove(pointer) {
|
@@ -266,7 +291,7 @@ export default class PanZoom extends BaseTool {
|
|
266
291
|
const toCanvas = this.editor.viewport.screenToCanvasTransform;
|
267
292
|
// Transform without including translation
|
268
293
|
const translation = toCanvas.transformVec3(Vec3.of(-delta.x, -delta.y, 0));
|
269
|
-
const pinchZoomScaleFactor = 1.
|
294
|
+
const pinchZoomScaleFactor = 1.03;
|
270
295
|
const transformUpdate = Mat33.scaling2D(Math.max(0.25, Math.min(Math.pow(pinchZoomScaleFactor, -delta.z), 4)), canvasPos).rightMul(Mat33.translation(translation));
|
271
296
|
this.updateTransform(transformUpdate, true);
|
272
297
|
return true;
|
@@ -1,10 +1,17 @@
|
|
1
|
-
/**
|
2
|
-
* A tool that handles paste events.
|
3
|
-
* @packageDocumentation
|
4
|
-
*/
|
5
1
|
import Editor from '../Editor';
|
6
2
|
import { PasteEvent } from '../types';
|
7
3
|
import BaseTool from './BaseTool';
|
4
|
+
/**
|
5
|
+
* A tool that handles paste events (e.g. as triggered by ctrl+V).
|
6
|
+
*
|
7
|
+
* @example
|
8
|
+
* While `ToolController` has a `PasteHandler` in its default list of tools,
|
9
|
+
* if a non-default set is being used, `PasteHandler` can be added as follows:
|
10
|
+
* ```ts
|
11
|
+
* const toolController = editor.toolController;
|
12
|
+
* toolController.addTool(new PasteHandler(editor));
|
13
|
+
* ```
|
14
|
+
*/
|
8
15
|
export default class PasteHandler extends BaseTool {
|
9
16
|
private editor;
|
10
17
|
constructor(editor: Editor);
|
@@ -1,7 +1,3 @@
|
|
1
|
-
/**
|
2
|
-
* A tool that handles paste events.
|
3
|
-
* @packageDocumentation
|
4
|
-
*/
|
5
1
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
6
2
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
7
3
|
return new (P || (P = Promise))(function (resolve, reject) {
|
@@ -18,12 +14,23 @@ import BaseTool from './BaseTool';
|
|
18
14
|
import TextTool from './TextTool';
|
19
15
|
import Color4 from '../Color4';
|
20
16
|
import ImageComponent from '../components/ImageComponent';
|
21
|
-
|
17
|
+
/**
|
18
|
+
* A tool that handles paste events (e.g. as triggered by ctrl+V).
|
19
|
+
*
|
20
|
+
* @example
|
21
|
+
* While `ToolController` has a `PasteHandler` in its default list of tools,
|
22
|
+
* if a non-default set is being used, `PasteHandler` can be added as follows:
|
23
|
+
* ```ts
|
24
|
+
* const toolController = editor.toolController;
|
25
|
+
* toolController.addTool(new PasteHandler(editor));
|
26
|
+
* ```
|
27
|
+
*/
|
22
28
|
export default class PasteHandler extends BaseTool {
|
23
29
|
constructor(editor) {
|
24
30
|
super(editor.notifier, editor.localization.pasteHandler);
|
25
31
|
this.editor = editor;
|
26
32
|
}
|
33
|
+
// @internal
|
27
34
|
onPaste(event) {
|
28
35
|
const mime = event.mime.toLowerCase();
|
29
36
|
if (mime === 'image/svg+xml') {
|
package/dist/src/tools/Pen.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
2
|
import Editor from '../Editor';
|
3
3
|
import Pointer from '../Pointer';
|
4
|
-
import { KeyPressEvent, PointerEvt, StrokeDataPoint } from '../types';
|
4
|
+
import { KeyPressEvent, KeyUpEvent, PointerEvt, StrokeDataPoint } from '../types';
|
5
5
|
import BaseTool from './BaseTool';
|
6
6
|
import { ComponentBuilder, ComponentBuilderFactory } from '../components/builders/types';
|
7
7
|
export interface PenStyle {
|
@@ -14,6 +14,7 @@ export default class Pen extends BaseTool {
|
|
14
14
|
private builderFactory;
|
15
15
|
protected builder: ComponentBuilder | null;
|
16
16
|
private lastPoint;
|
17
|
+
private ctrlKeyPressed;
|
17
18
|
constructor(editor: Editor, description: string, style: PenStyle, builderFactory?: ComponentBuilderFactory);
|
18
19
|
private getPressureMultiplier;
|
19
20
|
protected toStrokePoint(pointer: Pointer): StrokeDataPoint;
|
@@ -23,6 +24,7 @@ export default class Pen extends BaseTool {
|
|
23
24
|
onPointerMove({ current }: PointerEvt): void;
|
24
25
|
onPointerUp({ current }: PointerEvt): void;
|
25
26
|
onGestureCancel(): void;
|
27
|
+
private finalizeStroke;
|
26
28
|
private noteUpdated;
|
27
29
|
setColor(color: Color4): void;
|
28
30
|
setThickness(thickness: number): void;
|
@@ -30,5 +32,8 @@ export default class Pen extends BaseTool {
|
|
30
32
|
getThickness(): number;
|
31
33
|
getColor(): Color4;
|
32
34
|
getStrokeFactory(): ComponentBuilderFactory;
|
33
|
-
|
35
|
+
setEnabled(enabled: boolean): void;
|
36
|
+
private isSnappingToGrid;
|
37
|
+
onKeyPress({ key, ctrlKey }: KeyPressEvent): boolean;
|
38
|
+
onKeyUp({ key }: KeyUpEvent): boolean;
|
34
39
|
}
|
package/dist/src/tools/Pen.js
CHANGED
@@ -11,6 +11,7 @@ export default class Pen extends BaseTool {
|
|
11
11
|
this.builderFactory = builderFactory;
|
12
12
|
this.builder = null;
|
13
13
|
this.lastPoint = null;
|
14
|
+
this.ctrlKeyPressed = false;
|
14
15
|
}
|
15
16
|
getPressureMultiplier() {
|
16
17
|
return 1 / this.editor.viewport.getScaleFactor() * this.style.thickness;
|
@@ -18,6 +19,9 @@ export default class Pen extends BaseTool {
|
|
18
19
|
// Converts a `pointer` to a `StrokeDataPoint`.
|
19
20
|
toStrokePoint(pointer) {
|
20
21
|
var _a;
|
22
|
+
if (this.isSnappingToGrid()) {
|
23
|
+
pointer = pointer.snappedToGrid(this.editor.viewport);
|
24
|
+
}
|
21
25
|
const minPressure = 0.3;
|
22
26
|
let pressure = Math.max((_a = pointer.pressure) !== null && _a !== void 0 ? _a : 1.0, minPressure);
|
23
27
|
if (!isFinite(pressure)) {
|
@@ -27,8 +31,9 @@ export default class Pen extends BaseTool {
|
|
27
31
|
console.assert(isFinite(pointer.canvasPos.length()), 'Non-finite canvas position!');
|
28
32
|
console.assert(isFinite(pointer.screenPos.length()), 'Non-finite screen position!');
|
29
33
|
console.assert(isFinite(pointer.timeStamp), 'Non-finite timeStamp on pointer!');
|
34
|
+
const pos = pointer.canvasPos;
|
30
35
|
return {
|
31
|
-
pos
|
36
|
+
pos,
|
32
37
|
width: pressure * this.getPressureMultiplier(),
|
33
38
|
color: this.style.color,
|
34
39
|
time: pointer.timeStamp,
|
@@ -65,6 +70,8 @@ export default class Pen extends BaseTool {
|
|
65
70
|
return false;
|
66
71
|
}
|
67
72
|
onPointerMove({ current }) {
|
73
|
+
if (!this.builder)
|
74
|
+
return;
|
68
75
|
this.addPointToStroke(this.toStrokePoint(current));
|
69
76
|
}
|
70
77
|
onPointerUp({ current }) {
|
@@ -76,7 +83,15 @@ export default class Pen extends BaseTool {
|
|
76
83
|
const currentPoint = this.toStrokePoint(current);
|
77
84
|
const strokePoint = Object.assign(Object.assign({}, currentPoint), { width: (_b = (_a = this.lastPoint) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : currentPoint.width });
|
78
85
|
this.addPointToStroke(strokePoint);
|
79
|
-
if (
|
86
|
+
if (current.isPrimary) {
|
87
|
+
this.finalizeStroke();
|
88
|
+
}
|
89
|
+
}
|
90
|
+
onGestureCancel() {
|
91
|
+
this.editor.clearWetInk();
|
92
|
+
}
|
93
|
+
finalizeStroke() {
|
94
|
+
if (this.builder) {
|
80
95
|
const stroke = this.builder.build();
|
81
96
|
this.previewStroke();
|
82
97
|
if (stroke.getBBox().area > 0) {
|
@@ -91,9 +106,6 @@ export default class Pen extends BaseTool {
|
|
91
106
|
this.builder = null;
|
92
107
|
this.editor.clearWetInk();
|
93
108
|
}
|
94
|
-
onGestureCancel() {
|
95
|
-
this.editor.clearWetInk();
|
96
|
-
}
|
97
109
|
noteUpdated() {
|
98
110
|
this.editor.notifier.dispatch(EditorEventType.ToolUpdated, {
|
99
111
|
kind: EditorEventType.ToolUpdated,
|
@@ -121,7 +133,12 @@ export default class Pen extends BaseTool {
|
|
121
133
|
getThickness() { return this.style.thickness; }
|
122
134
|
getColor() { return this.style.color; }
|
123
135
|
getStrokeFactory() { return this.builderFactory; }
|
124
|
-
|
136
|
+
setEnabled(enabled) {
|
137
|
+
super.setEnabled(enabled);
|
138
|
+
this.ctrlKeyPressed = false;
|
139
|
+
}
|
140
|
+
isSnappingToGrid() { return this.ctrlKeyPressed; }
|
141
|
+
onKeyPress({ key, ctrlKey }) {
|
125
142
|
key = key.toLowerCase();
|
126
143
|
let newThickness;
|
127
144
|
if (key === '-' || key === '_') {
|
@@ -135,6 +152,22 @@ export default class Pen extends BaseTool {
|
|
135
152
|
this.setThickness(newThickness);
|
136
153
|
return true;
|
137
154
|
}
|
155
|
+
if (key === 'control') {
|
156
|
+
this.ctrlKeyPressed = true;
|
157
|
+
return true;
|
158
|
+
}
|
159
|
+
// Ctrl+Z: End the stroke so that it can be undone/redone.
|
160
|
+
if (key === 'z' && ctrlKey && this.builder) {
|
161
|
+
this.finalizeStroke();
|
162
|
+
}
|
163
|
+
return false;
|
164
|
+
}
|
165
|
+
onKeyUp({ key }) {
|
166
|
+
key = key.toLowerCase();
|
167
|
+
if (key === 'control') {
|
168
|
+
this.ctrlKeyPressed = false;
|
169
|
+
return true;
|
170
|
+
}
|
138
171
|
return false;
|
139
172
|
}
|
140
173
|
}
|
@@ -17,6 +17,7 @@ export default class SelectionHandle {
|
|
17
17
|
private readonly onDragUpdate;
|
18
18
|
private readonly onDragEnd;
|
19
19
|
private element;
|
20
|
+
private snapToGrid;
|
20
21
|
constructor(shape: HandleShape, parentSide: Vec2, parent: Selection, onDragStart: DragStartCallback, onDragUpdate: DragUpdateCallback, onDragEnd: DragEndCallback);
|
21
22
|
/**
|
22
23
|
* Adds this to `container`, where `conatiner` should be the background/selection
|
@@ -32,4 +33,6 @@ export default class SelectionHandle {
|
|
32
33
|
handleDragStart(pointer: Pointer): void;
|
33
34
|
handleDragUpdate(pointer: Pointer): void;
|
34
35
|
handleDragEnd(): void;
|
36
|
+
setSnapToGrid(snap: boolean): void;
|
37
|
+
isSnappingToGrid(): boolean;
|
35
38
|
}
|
@@ -12,10 +12,12 @@ export default class SelectionTool extends BaseTool {
|
|
12
12
|
private lastEvtTarget;
|
13
13
|
private expandingSelectionBox;
|
14
14
|
private shiftKeyPressed;
|
15
|
+
private ctrlKeyPressed;
|
15
16
|
constructor(editor: Editor, description: string);
|
16
17
|
private makeSelectionBox;
|
18
|
+
private snapSelectionToGrid;
|
17
19
|
private selectionBoxHandlingEvt;
|
18
|
-
onPointerDown(
|
20
|
+
onPointerDown({ allPointers, current }: PointerEvt): boolean;
|
19
21
|
onPointerMove(event: PointerEvt): void;
|
20
22
|
private onSelectionUpdated;
|
21
23
|
private onGestureEnd;
|