js-draw 0.0.2 → 0.0.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/CHANGELOG.md +13 -0
- package/README.md +100 -3
- package/build_tools/BundledFile.ts +167 -0
- package/build_tools/bundle.ts +11 -0
- package/dist/build_tools/BundledFile.d.ts +13 -0
- package/dist/build_tools/BundledFile.js +157 -0
- package/dist/build_tools/bundle.d.ts +1 -0
- package/dist/build_tools/bundle.js +5 -0
- package/dist/bundle.js +1 -0
- package/dist/src/Display.js +4 -1
- package/dist/src/Editor.d.ts +9 -2
- package/dist/src/Editor.js +29 -7
- package/dist/src/EditorImage.d.ts +2 -0
- package/dist/src/EditorImage.js +29 -5
- package/dist/src/Pointer.js +1 -1
- package/dist/src/Viewport.d.ts +1 -1
- package/dist/src/bundle/bundled.d.ts +4 -0
- package/dist/src/bundle/bundled.js +5 -0
- package/dist/src/components/AbstractComponent.d.ts +1 -1
- package/dist/src/components/Stroke.d.ts +1 -1
- package/dist/src/components/Stroke.js +1 -1
- package/dist/src/components/UnknownSVGObject.d.ts +1 -1
- package/dist/src/components/builders/ArrowBuilder.d.ts +17 -0
- package/dist/src/components/builders/ArrowBuilder.js +83 -0
- package/dist/src/{StrokeBuilder.d.ts → components/builders/FreehandLineBuilder.d.ts} +9 -13
- package/dist/src/{StrokeBuilder.js → components/builders/FreehandLineBuilder.js} +27 -9
- package/dist/src/components/builders/LineBuilder.d.ts +16 -0
- package/dist/src/components/builders/LineBuilder.js +57 -0
- package/dist/src/components/builders/RectangleBuilder.d.ts +18 -0
- package/dist/src/components/builders/RectangleBuilder.js +41 -0
- package/dist/src/components/builders/types.d.ts +12 -0
- package/dist/src/components/builders/types.js +1 -0
- package/dist/src/geometry/Path.d.ts +1 -0
- package/dist/src/geometry/Path.js +32 -0
- package/dist/src/geometry/Vec3.d.ts +2 -0
- package/dist/src/geometry/Vec3.js +13 -0
- package/dist/src/localization.d.ts +1 -1
- package/dist/src/localization.js +2 -2
- package/dist/src/rendering/AbstractRenderer.js +3 -25
- package/dist/src/toolbar/HTMLToolbar.d.ts +5 -3
- package/dist/src/toolbar/HTMLToolbar.js +139 -30
- package/dist/src/toolbar/localization.d.ts +20 -0
- package/dist/src/toolbar/localization.js +19 -0
- package/dist/src/toolbar/types.d.ts +0 -13
- package/dist/src/tools/Pen.d.ts +13 -3
- package/dist/src/tools/Pen.js +37 -28
- package/dist/src/tools/SelectionTool.js +2 -1
- package/dist/src/tools/ToolController.js +3 -3
- package/dist/src/types.d.ts +14 -2
- package/dist/src/types.js +1 -0
- package/dist-test/test-dist-bundle.html +35 -0
- package/package.json +15 -5
- package/src/Display.ts +3 -1
- package/src/Editor.css +0 -1
- package/src/Editor.ts +51 -12
- package/src/EditorImage.test.ts +5 -3
- package/src/EditorImage.ts +31 -3
- package/src/Pointer.ts +1 -1
- package/src/Viewport.ts +1 -1
- package/src/bundle/bundled.ts +7 -0
- package/src/components/AbstractComponent.ts +1 -1
- package/src/components/Stroke.ts +2 -2
- package/src/components/UnknownSVGObject.ts +1 -1
- package/src/components/builders/ArrowBuilder.ts +104 -0
- package/src/{StrokeBuilder.ts → components/builders/FreehandLineBuilder.ts} +36 -18
- package/src/components/builders/LineBuilder.ts +75 -0
- package/src/components/builders/RectangleBuilder.ts +59 -0
- package/src/components/builders/types.ts +15 -0
- package/src/geometry/Path.ts +43 -0
- package/src/geometry/Vec2.test.ts +1 -0
- package/src/geometry/Vec3.test.ts +14 -0
- package/src/geometry/Vec3.ts +16 -0
- package/src/localization.ts +2 -3
- package/src/rendering/AbstractRenderer.ts +3 -32
- package/src/{editorStyles.js → styles.js} +0 -0
- package/src/toolbar/HTMLToolbar.ts +167 -39
- package/src/toolbar/localization.ts +44 -0
- package/src/toolbar/toolbar.css +12 -0
- package/src/toolbar/types.ts +0 -16
- package/src/tools/Pen.ts +56 -34
- package/src/tools/SelectionTool.test.ts +1 -1
- package/src/tools/SelectionTool.ts +1 -1
- package/src/tools/ToolController.ts +3 -3
- package/src/types.ts +16 -1
@@ -5,6 +5,15 @@ import Color4 from '../Color4';
|
|
5
5
|
import Pen from '../tools/Pen';
|
6
6
|
import Eraser from '../tools/Eraser';
|
7
7
|
import SelectionTool from '../tools/SelectionTool';
|
8
|
+
import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
|
9
|
+
import { Vec2 } from '../geometry/Vec2';
|
10
|
+
import SVGRenderer from '../rendering/SVGRenderer';
|
11
|
+
import Viewport from '../Viewport';
|
12
|
+
import EventDispatcher from '../EventDispatcher';
|
13
|
+
import { makeArrowBuilder } from '../components/builders/ArrowBuilder';
|
14
|
+
import { makeLineBuilder } from '../components/builders/LineBuilder';
|
15
|
+
import { makeFilledRectangleBuilder, makeOutlinedRectangleBuilder } from '../components/builders/RectangleBuilder';
|
16
|
+
import { defaultToolbarLocalization } from './localization';
|
8
17
|
const primaryForegroundFill = `
|
9
18
|
style='fill: var(--primary-foreground-color);'
|
10
19
|
`;
|
@@ -12,6 +21,7 @@ const primaryForegroundStrokeFill = `
|
|
12
21
|
style='fill: var(--primary-foreground-color); stroke: var(--primary-foreground-color);'
|
13
22
|
`;
|
14
23
|
const toolbarCSSPrefix = 'toolbar-';
|
24
|
+
const svgNamespace = 'http://www.w3.org/2000/svg';
|
15
25
|
class ToolbarWidget {
|
16
26
|
constructor(editor, targetTool, localizationTable) {
|
17
27
|
this.editor = editor;
|
@@ -121,7 +131,7 @@ class ToolbarWidget {
|
|
121
131
|
return !this.dropdownContainer.classList.contains('hidden');
|
122
132
|
}
|
123
133
|
createDropdownIcon() {
|
124
|
-
const icon = document.createElementNS(
|
134
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
125
135
|
icon.innerHTML = `
|
126
136
|
<g>
|
127
137
|
<path
|
@@ -140,7 +150,7 @@ class EraserWidget extends ToolbarWidget {
|
|
140
150
|
return this.localizationTable.eraser;
|
141
151
|
}
|
142
152
|
createIcon() {
|
143
|
-
const icon = document.createElementNS(
|
153
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
144
154
|
// Draw an eraser-like shape
|
145
155
|
icon.innerHTML = `
|
146
156
|
<g>
|
@@ -168,7 +178,7 @@ class SelectionWidget extends ToolbarWidget {
|
|
168
178
|
return this.localizationTable.select;
|
169
179
|
}
|
170
180
|
createIcon() {
|
171
|
-
const icon = document.createElementNS(
|
181
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
172
182
|
// Draw a cursor-like shape
|
173
183
|
icon.innerHTML = `
|
174
184
|
<g>
|
@@ -209,7 +219,7 @@ class TouchDrawingWidget extends ToolbarWidget {
|
|
209
219
|
return this.localizationTable.touchDrawing;
|
210
220
|
}
|
211
221
|
createIcon() {
|
212
|
-
const icon = document.createElementNS(
|
222
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
213
223
|
// Draw a cursor-like shape
|
214
224
|
icon.innerHTML = `
|
215
225
|
<g>
|
@@ -241,9 +251,10 @@ class TouchDrawingWidget extends ToolbarWidget {
|
|
241
251
|
}
|
242
252
|
}
|
243
253
|
class PenWidget extends ToolbarWidget {
|
244
|
-
constructor(editor, tool, localization) {
|
254
|
+
constructor(editor, tool, localization, penTypes) {
|
245
255
|
super(editor, tool, localization);
|
246
256
|
this.tool = tool;
|
257
|
+
this.penTypes = penTypes;
|
247
258
|
this.updateInputs = () => { };
|
248
259
|
this.editor.notifier.on(EditorEventType.ToolUpdated, toolEvt => {
|
249
260
|
if (toolEvt.kind !== EditorEventType.ToolUpdated) {
|
@@ -259,17 +270,14 @@ class PenWidget extends ToolbarWidget {
|
|
259
270
|
getTitle() {
|
260
271
|
return this.targetTool.description;
|
261
272
|
}
|
262
|
-
|
263
|
-
// We need to use createElementNS to embed an SVG element in HTML.
|
264
|
-
// See http://zhangwenli.com/blog/2017/07/26/createelementns/
|
265
|
-
const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
273
|
+
makePenIcon(elem) {
|
266
274
|
// Use a square-root scale to prevent the pen's tip from overflowing.
|
267
275
|
const scale = Math.round(Math.sqrt(this.tool.getThickness()) * 2);
|
268
276
|
const color = this.tool.getColor();
|
269
277
|
// Draw a pen-like shape
|
270
278
|
const primaryStrokeTipPath = `M14,63 L${50 - scale},95 L${50 + scale},90 L88,60 Z`;
|
271
279
|
const backgroundStrokeTipPath = `M14,63 L${50 - scale},85 L${50 + scale},83 L88,60 Z`;
|
272
|
-
|
280
|
+
elem.innerHTML = `
|
273
281
|
<defs>
|
274
282
|
<pattern
|
275
283
|
id='checkerboard'
|
@@ -302,18 +310,61 @@ class PenWidget extends ToolbarWidget {
|
|
302
310
|
/>
|
303
311
|
</g>
|
304
312
|
`;
|
313
|
+
}
|
314
|
+
// Draws an icon with the pen.
|
315
|
+
makeDrawnIcon(icon) {
|
316
|
+
const strokeFactory = this.tool.getStrokeFactory();
|
317
|
+
const toolThickness = this.tool.getThickness();
|
318
|
+
const nowTime = (new Date()).getTime();
|
319
|
+
const startPoint = {
|
320
|
+
pos: Vec2.of(10, 10),
|
321
|
+
width: toolThickness / 5,
|
322
|
+
color: this.tool.getColor(),
|
323
|
+
time: nowTime - 100,
|
324
|
+
};
|
325
|
+
const endPoint = {
|
326
|
+
pos: Vec2.of(90, 90),
|
327
|
+
width: toolThickness / 5,
|
328
|
+
color: this.tool.getColor(),
|
329
|
+
time: nowTime,
|
330
|
+
};
|
331
|
+
const builder = strokeFactory(startPoint, this.editor.viewport);
|
332
|
+
builder.addPoint(endPoint);
|
333
|
+
const viewport = new Viewport(new EventDispatcher());
|
334
|
+
viewport.updateScreenSize(Vec2.of(100, 100));
|
335
|
+
const renderer = new SVGRenderer(icon, viewport);
|
336
|
+
builder.preview(renderer);
|
337
|
+
}
|
338
|
+
createIcon() {
|
339
|
+
// We need to use createElementNS to embed an SVG element in HTML.
|
340
|
+
// See http://zhangwenli.com/blog/2017/07/26/createelementns/
|
341
|
+
const icon = document.createElementNS(svgNamespace, 'svg');
|
305
342
|
icon.setAttribute('viewBox', '0 0 100 100');
|
343
|
+
const strokeFactory = this.tool.getStrokeFactory();
|
344
|
+
if (strokeFactory === makeFreehandLineBuilder) {
|
345
|
+
this.makePenIcon(icon);
|
346
|
+
}
|
347
|
+
else {
|
348
|
+
this.makeDrawnIcon(icon);
|
349
|
+
}
|
306
350
|
return icon;
|
307
351
|
}
|
308
352
|
fillDropdown(dropdown) {
|
309
353
|
const container = document.createElement('div');
|
310
|
-
// Thickness: Value of the input is squared to allow for finer control/larger values.
|
311
354
|
const thicknessRow = document.createElement('div');
|
355
|
+
const objectTypeRow = document.createElement('div');
|
356
|
+
// Thickness: Value of the input is squared to allow for finer control/larger values.
|
312
357
|
const thicknessLabel = document.createElement('label');
|
313
358
|
const thicknessInput = document.createElement('input');
|
359
|
+
const objectSelectLabel = document.createElement('label');
|
360
|
+
const objectTypeSelect = document.createElement('select');
|
361
|
+
// Give inputs IDs so we can label them with a <label for=...>Label text</label>
|
314
362
|
thicknessInput.id = `${toolbarCSSPrefix}thicknessInput${PenWidget.idCounter++}`;
|
363
|
+
objectTypeSelect.id = `${toolbarCSSPrefix}builderSelect${PenWidget.idCounter++}`;
|
315
364
|
thicknessLabel.innerText = this.localizationTable.thicknessLabel;
|
316
365
|
thicknessLabel.setAttribute('for', thicknessInput.id);
|
366
|
+
objectSelectLabel.innerText = this.localizationTable.selectObjectType;
|
367
|
+
objectSelectLabel.setAttribute('for', objectTypeSelect.id);
|
317
368
|
thicknessInput.type = 'range';
|
318
369
|
thicknessInput.min = '1';
|
319
370
|
thicknessInput.max = '20';
|
@@ -323,6 +374,16 @@ class PenWidget extends ToolbarWidget {
|
|
323
374
|
};
|
324
375
|
thicknessRow.appendChild(thicknessLabel);
|
325
376
|
thicknessRow.appendChild(thicknessInput);
|
377
|
+
objectTypeSelect.oninput = () => {
|
378
|
+
const penTypeIdx = parseInt(objectTypeSelect.value);
|
379
|
+
if (penTypeIdx < 0 || penTypeIdx >= this.penTypes.length) {
|
380
|
+
console.error('Invalid pen type index', penTypeIdx);
|
381
|
+
return;
|
382
|
+
}
|
383
|
+
this.tool.setStrokeFactory(this.penTypes[penTypeIdx].factory);
|
384
|
+
};
|
385
|
+
objectTypeRow.appendChild(objectSelectLabel);
|
386
|
+
objectTypeRow.appendChild(objectTypeSelect);
|
326
387
|
const colorRow = document.createElement('div');
|
327
388
|
const colorLabel = document.createElement('label');
|
328
389
|
const colorInput = document.createElement('input');
|
@@ -334,30 +395,80 @@ class PenWidget extends ToolbarWidget {
|
|
334
395
|
colorInput.oninput = () => {
|
335
396
|
this.tool.setColor(Color4.fromHex(colorInput.value));
|
336
397
|
};
|
398
|
+
colorInput.addEventListener('open', () => {
|
399
|
+
this.editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
400
|
+
kind: EditorEventType.ColorPickerToggled,
|
401
|
+
open: true,
|
402
|
+
});
|
403
|
+
});
|
404
|
+
colorInput.addEventListener('close', () => {
|
405
|
+
this.editor.notifier.dispatch(EditorEventType.ColorPickerToggled, {
|
406
|
+
kind: EditorEventType.ColorPickerToggled,
|
407
|
+
open: false,
|
408
|
+
});
|
409
|
+
});
|
337
410
|
colorRow.appendChild(colorLabel);
|
338
411
|
colorRow.appendChild(colorInput);
|
339
412
|
this.updateInputs = () => {
|
340
413
|
colorInput.value = this.tool.getColor().toHexString();
|
341
414
|
thicknessInput.value = Math.sqrt(this.tool.getThickness()).toString();
|
415
|
+
objectTypeSelect.replaceChildren();
|
416
|
+
for (let i = 0; i < this.penTypes.length; i++) {
|
417
|
+
const penType = this.penTypes[i];
|
418
|
+
const option = document.createElement('option');
|
419
|
+
option.value = i.toString();
|
420
|
+
option.innerText = penType.name;
|
421
|
+
objectTypeSelect.appendChild(option);
|
422
|
+
if (penType.factory === this.tool.getStrokeFactory()) {
|
423
|
+
objectTypeSelect.value = i.toString();
|
424
|
+
}
|
425
|
+
}
|
342
426
|
};
|
343
427
|
this.updateInputs();
|
344
|
-
container.replaceChildren(colorRow, thicknessRow);
|
428
|
+
container.replaceChildren(colorRow, thicknessRow, objectTypeRow);
|
345
429
|
dropdown.replaceChildren(container);
|
346
430
|
return true;
|
347
431
|
}
|
348
432
|
}
|
349
433
|
PenWidget.idCounter = 0;
|
350
434
|
export default class HTMLToolbar {
|
351
|
-
constructor(editor, parent, localizationTable =
|
435
|
+
constructor(editor, parent, localizationTable = defaultToolbarLocalization) {
|
352
436
|
this.editor = editor;
|
353
437
|
this.localizationTable = localizationTable;
|
354
438
|
this.container = document.createElement('div');
|
355
439
|
this.container.classList.add(`${toolbarCSSPrefix}root`);
|
356
440
|
this.container.setAttribute('role', 'toolbar');
|
357
|
-
this.addElements();
|
358
441
|
parent.appendChild(this.container);
|
359
|
-
// Initialize color choosers
|
360
442
|
colorisInit();
|
443
|
+
this.setupColorPickers();
|
444
|
+
// Default pen types
|
445
|
+
this.penTypes = [
|
446
|
+
{
|
447
|
+
name: localizationTable.freehandPen,
|
448
|
+
factory: makeFreehandLineBuilder,
|
449
|
+
},
|
450
|
+
{
|
451
|
+
name: localizationTable.arrowPen,
|
452
|
+
factory: makeArrowBuilder,
|
453
|
+
},
|
454
|
+
{
|
455
|
+
name: localizationTable.linePen,
|
456
|
+
factory: makeLineBuilder,
|
457
|
+
},
|
458
|
+
{
|
459
|
+
name: localizationTable.filledRectanglePen,
|
460
|
+
factory: makeFilledRectangleBuilder,
|
461
|
+
},
|
462
|
+
{
|
463
|
+
name: localizationTable.outlinedRectanglePen,
|
464
|
+
factory: makeOutlinedRectangleBuilder,
|
465
|
+
},
|
466
|
+
];
|
467
|
+
}
|
468
|
+
setupColorPickers() {
|
469
|
+
const closePickerOverlay = document.createElement('div');
|
470
|
+
closePickerOverlay.className = `${toolbarCSSPrefix}closeColorPickerOverlay`;
|
471
|
+
this.editor.createHTMLOverlay(closePickerOverlay);
|
361
472
|
coloris({
|
362
473
|
el: '.coloris_input',
|
363
474
|
format: 'hex',
|
@@ -373,6 +484,14 @@ export default class HTMLToolbar {
|
|
373
484
|
Color4.white.toHexString(),
|
374
485
|
],
|
375
486
|
});
|
487
|
+
this.editor.notifier.on(EditorEventType.ColorPickerToggled, event => {
|
488
|
+
if (event.kind !== EditorEventType.ColorPickerToggled) {
|
489
|
+
return;
|
490
|
+
}
|
491
|
+
// Show/hide the overlay. Making the overlay visible gives users a surface to click
|
492
|
+
// on that shows/hides the color picker.
|
493
|
+
closePickerOverlay.style.display = event.open ? 'block' : 'none';
|
494
|
+
});
|
376
495
|
}
|
377
496
|
addActionButton(text, command, parent) {
|
378
497
|
const button = document.createElement('button');
|
@@ -402,13 +521,13 @@ export default class HTMLToolbar {
|
|
402
521
|
redoButton.disabled = event.redoStackSize === 0;
|
403
522
|
});
|
404
523
|
}
|
405
|
-
|
524
|
+
addDefaultToolWidgets() {
|
406
525
|
const toolController = this.editor.toolController;
|
407
526
|
for (const tool of toolController.getMatchingTools(ToolType.Pen)) {
|
408
527
|
if (!(tool instanceof Pen)) {
|
409
528
|
throw new Error('All `Pen` tools must have kind === ToolType.Pen');
|
410
529
|
}
|
411
|
-
const widget = new PenWidget(this.editor, tool, this.localizationTable);
|
530
|
+
const widget = new PenWidget(this.editor, tool, this.localizationTable, this.penTypes);
|
412
531
|
widget.addTo(this.container);
|
413
532
|
}
|
414
533
|
for (const tool of toolController.getMatchingTools(ToolType.Eraser)) {
|
@@ -426,19 +545,9 @@ export default class HTMLToolbar {
|
|
426
545
|
for (const tool of toolController.getMatchingTools(ToolType.TouchPanZoom)) {
|
427
546
|
(new TouchDrawingWidget(this.editor, tool, this.localizationTable)).addTo(this.container);
|
428
547
|
}
|
548
|
+
this.setupColorPickers();
|
549
|
+
}
|
550
|
+
addDefaultActionButtons() {
|
429
551
|
this.addUndoRedoButtons();
|
430
552
|
}
|
431
553
|
}
|
432
|
-
HTMLToolbar.defaultLocalization = {
|
433
|
-
pen: 'Pen',
|
434
|
-
eraser: 'Eraser',
|
435
|
-
select: 'Select',
|
436
|
-
touchDrawing: 'Touch Drawing',
|
437
|
-
thicknessLabel: 'Thickness: ',
|
438
|
-
colorLabel: 'Color: ',
|
439
|
-
resizeImageToSelection: 'Resize image to selection',
|
440
|
-
undo: 'Undo',
|
441
|
-
redo: 'Redo',
|
442
|
-
dropdownShown: (toolName) => `Dropdown for ${toolName} shown`,
|
443
|
-
dropdownHidden: (toolName) => `Dropdown for ${toolName} hidden`,
|
444
|
-
};
|
@@ -0,0 +1,20 @@
|
|
1
|
+
export interface ToolbarLocalization {
|
2
|
+
outlinedRectanglePen: string;
|
3
|
+
filledRectanglePen: string;
|
4
|
+
linePen: string;
|
5
|
+
arrowPen: string;
|
6
|
+
freehandPen: string;
|
7
|
+
selectObjectType: string;
|
8
|
+
colorLabel: string;
|
9
|
+
pen: string;
|
10
|
+
eraser: string;
|
11
|
+
select: string;
|
12
|
+
touchDrawing: string;
|
13
|
+
thicknessLabel: string;
|
14
|
+
resizeImageToSelection: string;
|
15
|
+
undo: string;
|
16
|
+
redo: string;
|
17
|
+
dropdownShown: (toolName: string) => string;
|
18
|
+
dropdownHidden: (toolName: string) => string;
|
19
|
+
}
|
20
|
+
export declare const defaultToolbarLocalization: ToolbarLocalization;
|
@@ -0,0 +1,19 @@
|
|
1
|
+
export const defaultToolbarLocalization = {
|
2
|
+
pen: 'Pen',
|
3
|
+
eraser: 'Eraser',
|
4
|
+
select: 'Select',
|
5
|
+
touchDrawing: 'Touch Drawing',
|
6
|
+
thicknessLabel: 'Thickness: ',
|
7
|
+
colorLabel: 'Color: ',
|
8
|
+
resizeImageToSelection: 'Resize image to selection',
|
9
|
+
undo: 'Undo',
|
10
|
+
redo: 'Redo',
|
11
|
+
selectObjectType: 'Object type: ',
|
12
|
+
freehandPen: 'Freehand',
|
13
|
+
arrowPen: 'Arrow',
|
14
|
+
linePen: 'Line',
|
15
|
+
outlinedRectanglePen: 'Outlined rectangle',
|
16
|
+
filledRectanglePen: 'Filled rectangle',
|
17
|
+
dropdownShown: (toolName) => `Dropdown for ${toolName} shown`,
|
18
|
+
dropdownHidden: (toolName) => `Dropdown for ${toolName} hidden`,
|
19
|
+
};
|
@@ -2,16 +2,3 @@ export declare enum ToolbarButtonType {
|
|
2
2
|
ToggleButton = 0,
|
3
3
|
ActionButton = 1
|
4
4
|
}
|
5
|
-
export interface ToolbarLocalization {
|
6
|
-
colorLabel: string;
|
7
|
-
pen: string;
|
8
|
-
eraser: string;
|
9
|
-
select: string;
|
10
|
-
touchDrawing: string;
|
11
|
-
thicknessLabel: string;
|
12
|
-
resizeImageToSelection: string;
|
13
|
-
undo: string;
|
14
|
-
redo: string;
|
15
|
-
dropdownShown: (toolName: string) => string;
|
16
|
-
dropdownHidden: (toolName: string) => string;
|
17
|
-
}
|
package/dist/src/tools/Pen.d.ts
CHANGED
@@ -3,15 +3,22 @@ import Editor from '../Editor';
|
|
3
3
|
import { PointerEvt } from '../types';
|
4
4
|
import BaseTool from './BaseTool';
|
5
5
|
import { ToolType } from './ToolController';
|
6
|
+
import { ComponentBuilderFactory } from '../components/builders/types';
|
7
|
+
interface PenStyle {
|
8
|
+
color: Color4;
|
9
|
+
thickness: number;
|
10
|
+
}
|
6
11
|
export default class Pen extends BaseTool {
|
7
12
|
private editor;
|
8
|
-
private
|
9
|
-
private thickness;
|
13
|
+
private style;
|
10
14
|
private builder;
|
15
|
+
private builderFactory;
|
16
|
+
private lastPoint;
|
11
17
|
readonly kind: ToolType;
|
12
|
-
constructor(editor: Editor, description: string,
|
18
|
+
constructor(editor: Editor, description: string, style: PenStyle);
|
13
19
|
private getPressureMultiplier;
|
14
20
|
private getStrokePoint;
|
21
|
+
private previewStroke;
|
15
22
|
private addPointToStroke;
|
16
23
|
onPointerDown({ current, allPointers }: PointerEvt): boolean;
|
17
24
|
onPointerMove({ current }: PointerEvt): void;
|
@@ -20,6 +27,9 @@ export default class Pen extends BaseTool {
|
|
20
27
|
private noteUpdated;
|
21
28
|
setColor(color: Color4): void;
|
22
29
|
setThickness(thickness: number): void;
|
30
|
+
setStrokeFactory(factory: ComponentBuilderFactory): void;
|
23
31
|
getThickness(): number;
|
24
32
|
getColor(): Color4;
|
33
|
+
getStrokeFactory(): ComponentBuilderFactory;
|
25
34
|
}
|
35
|
+
export {};
|
package/dist/src/tools/Pen.js
CHANGED
@@ -1,22 +1,21 @@
|
|
1
|
-
import Color4 from '../Color4';
|
2
1
|
import EditorImage from '../EditorImage';
|
3
|
-
import { Vec2 } from '../geometry/Vec2';
|
4
2
|
import { PointerDevice } from '../Pointer';
|
5
|
-
import
|
3
|
+
import { makeFreehandLineBuilder } from '../components/builders/FreehandLineBuilder';
|
6
4
|
import { EditorEventType } from '../types';
|
7
5
|
import BaseTool from './BaseTool';
|
8
6
|
import { ToolType } from './ToolController';
|
9
7
|
export default class Pen extends BaseTool {
|
10
|
-
constructor(editor, description,
|
8
|
+
constructor(editor, description, style) {
|
11
9
|
super(editor.notifier, description);
|
12
10
|
this.editor = editor;
|
13
|
-
this.
|
14
|
-
this.thickness = thickness;
|
11
|
+
this.style = style;
|
15
12
|
this.builder = null;
|
13
|
+
this.builderFactory = makeFreehandLineBuilder;
|
14
|
+
this.lastPoint = null;
|
16
15
|
this.kind = ToolType.Pen;
|
17
16
|
}
|
18
17
|
getPressureMultiplier() {
|
19
|
-
return 1 / this.editor.viewport.getScaleFactor() * this.thickness;
|
18
|
+
return 1 / this.editor.viewport.getScaleFactor() * this.style.thickness;
|
20
19
|
}
|
21
20
|
getStrokePoint(pointer) {
|
22
21
|
var _a;
|
@@ -25,45 +24,48 @@ export default class Pen extends BaseTool {
|
|
25
24
|
return {
|
26
25
|
pos: pointer.canvasPos,
|
27
26
|
width: pressure * this.getPressureMultiplier(),
|
28
|
-
color: this.color,
|
27
|
+
color: this.style.color,
|
29
28
|
time: pointer.timeStamp,
|
30
29
|
};
|
31
30
|
}
|
32
|
-
|
31
|
+
previewStroke() {
|
32
|
+
var _a;
|
33
|
+
this.editor.clearWetInk();
|
34
|
+
(_a = this.builder) === null || _a === void 0 ? void 0 : _a.preview(this.editor.display.getWetInkRenderer());
|
35
|
+
}
|
36
|
+
addPointToStroke(point) {
|
33
37
|
if (!this.builder) {
|
34
38
|
throw new Error('No stroke is currently being generated.');
|
35
39
|
}
|
36
|
-
this.builder.addPoint(
|
37
|
-
this.
|
38
|
-
this.
|
40
|
+
this.builder.addPoint(point);
|
41
|
+
this.lastPoint = point;
|
42
|
+
this.previewStroke();
|
39
43
|
}
|
40
44
|
onPointerDown({ current, allPointers }) {
|
41
45
|
if (current.device === PointerDevice.Eraser) {
|
42
46
|
return false;
|
43
47
|
}
|
44
48
|
if (allPointers.length === 1 || current.device === PointerDevice.Pen) {
|
45
|
-
|
46
|
-
// less than ± 2 px from the curve.
|
47
|
-
const canvasTransform = this.editor.viewport.screenToCanvasTransform;
|
48
|
-
const maxSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 7;
|
49
|
-
const minSmoothingDist = canvasTransform.transformVec3(Vec2.unitX).magnitude() * 2;
|
50
|
-
this.builder = new StrokeBuilder(this.getStrokePoint(current), minSmoothingDist, maxSmoothingDist);
|
49
|
+
this.builder = this.builderFactory(this.getStrokePoint(current), this.editor.viewport);
|
51
50
|
return true;
|
52
51
|
}
|
53
52
|
return false;
|
54
53
|
}
|
55
54
|
onPointerMove({ current }) {
|
56
|
-
this.addPointToStroke(current);
|
55
|
+
this.addPointToStroke(this.getStrokePoint(current));
|
57
56
|
}
|
58
57
|
onPointerUp({ current }) {
|
58
|
+
var _a, _b;
|
59
59
|
if (!this.builder) {
|
60
60
|
return;
|
61
61
|
}
|
62
|
-
|
62
|
+
// onPointerUp events can have zero pressure. Use the last pressure instead.
|
63
|
+
const currentPoint = this.getStrokePoint(current);
|
64
|
+
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 });
|
65
|
+
this.addPointToStroke(strokePoint);
|
63
66
|
if (this.builder && current.isPrimary) {
|
64
67
|
const stroke = this.builder.build();
|
65
|
-
this.
|
66
|
-
this.editor.drawWetInk(...this.builder.preview());
|
68
|
+
this.previewStroke();
|
67
69
|
const canFlatten = true;
|
68
70
|
const action = new EditorImage.AddElementCommand(stroke, canFlatten);
|
69
71
|
this.editor.dispatch(action);
|
@@ -81,17 +83,24 @@ export default class Pen extends BaseTool {
|
|
81
83
|
});
|
82
84
|
}
|
83
85
|
setColor(color) {
|
84
|
-
if (color.toHexString() !== this.color.toHexString()) {
|
85
|
-
this.
|
86
|
+
if (color.toHexString() !== this.style.color.toHexString()) {
|
87
|
+
this.style = Object.assign(Object.assign({}, this.style), { color });
|
86
88
|
this.noteUpdated();
|
87
89
|
}
|
88
90
|
}
|
89
91
|
setThickness(thickness) {
|
90
|
-
if (thickness !== this.thickness) {
|
91
|
-
this.
|
92
|
+
if (thickness !== this.style.thickness) {
|
93
|
+
this.style = Object.assign(Object.assign({}, this.style), { thickness });
|
94
|
+
this.noteUpdated();
|
95
|
+
}
|
96
|
+
}
|
97
|
+
setStrokeFactory(factory) {
|
98
|
+
if (factory !== this.builderFactory) {
|
99
|
+
this.builderFactory = factory;
|
92
100
|
this.noteUpdated();
|
93
101
|
}
|
94
102
|
}
|
95
|
-
getThickness() { return this.thickness; }
|
96
|
-
getColor() { return this.color; }
|
103
|
+
getThickness() { return this.style.thickness; }
|
104
|
+
getColor() { return this.style.color; }
|
105
|
+
getStrokeFactory() { return this.builderFactory; }
|
97
106
|
}
|
@@ -17,15 +17,15 @@ export default class ToolController {
|
|
17
17
|
constructor(editor, localization) {
|
18
18
|
const primaryToolEnabledGroup = new ToolEnabledGroup();
|
19
19
|
const touchPanZoom = new PanZoom(editor, PanZoomMode.OneFingerGestures, localization.touchPanTool);
|
20
|
-
const primaryPenTool = new Pen(editor, localization.penTool(1));
|
20
|
+
const primaryPenTool = new Pen(editor, localization.penTool(1), { color: Color4.purple, thickness: 16 });
|
21
21
|
const primaryTools = [
|
22
22
|
new SelectionTool(editor, localization.selectionTool),
|
23
23
|
new Eraser(editor, localization.eraserTool),
|
24
24
|
// Three pens
|
25
25
|
primaryPenTool,
|
26
|
-
new Pen(editor, localization.penTool(2), Color4.clay, 8),
|
26
|
+
new Pen(editor, localization.penTool(2), { color: Color4.clay, thickness: 8 }),
|
27
27
|
// Highlighter-like pen with width=64
|
28
|
-
new Pen(editor, localization.penTool(3), Color4.ofRGBA(1, 1, 0, 0.5), 64),
|
28
|
+
new Pen(editor, localization.penTool(3), { color: Color4.ofRGBA(1, 1, 0, 0.5), thickness: 64 }),
|
29
29
|
];
|
30
30
|
this.tools = [
|
31
31
|
touchPanZoom,
|
package/dist/src/types.d.ts
CHANGED
@@ -6,6 +6,7 @@ import BaseTool from './tools/BaseTool';
|
|
6
6
|
import AbstractComponent from './components/AbstractComponent';
|
7
7
|
import Rect2 from './geometry/Rect2';
|
8
8
|
import Pointer from './Pointer';
|
9
|
+
import Color4 from './Color4';
|
9
10
|
export interface PointerEvtListener {
|
10
11
|
onPointerDown(event: PointerEvt): boolean;
|
11
12
|
onPointerMove(event: PointerEvt): void;
|
@@ -55,7 +56,8 @@ export declare enum EditorEventType {
|
|
55
56
|
UndoRedoStackUpdated = 3,
|
56
57
|
ObjectAdded = 4,
|
57
58
|
ViewportChanged = 5,
|
58
|
-
DisplayResized = 6
|
59
|
+
DisplayResized = 6,
|
60
|
+
ColorPickerToggled = 7
|
59
61
|
}
|
60
62
|
declare type EditorToolEventType = EditorEventType.ToolEnabled | EditorEventType.ToolDisabled | EditorEventType.ToolUpdated;
|
61
63
|
export interface EditorToolEvent {
|
@@ -79,10 +81,20 @@ export interface EditorUndoStackUpdated {
|
|
79
81
|
readonly undoStackSize: number;
|
80
82
|
readonly redoStackSize: number;
|
81
83
|
}
|
82
|
-
export
|
84
|
+
export interface ColorPickerToggled {
|
85
|
+
readonly kind: EditorEventType.ColorPickerToggled;
|
86
|
+
readonly open: boolean;
|
87
|
+
}
|
88
|
+
export declare type EditorEventDataType = EditorToolEvent | EditorObjectEvent | EditorViewportChangedEvent | DisplayResizedEvent | EditorUndoStackUpdated | ColorPickerToggled;
|
83
89
|
export declare type OnProgressListener = (amountProcessed: number, totalToProcess: number) => Promise<void> | null;
|
84
90
|
export declare type ComponentAddedListener = (component: AbstractComponent) => void;
|
85
91
|
export interface ImageLoader {
|
86
92
|
start(onAddComponent: ComponentAddedListener, onProgressListener: OnProgressListener): Promise<Rect2>;
|
87
93
|
}
|
94
|
+
export interface StrokeDataPoint {
|
95
|
+
pos: Point2;
|
96
|
+
width: number;
|
97
|
+
time: number;
|
98
|
+
color: Color4;
|
99
|
+
}
|
88
100
|
export {};
|
package/dist/src/types.js
CHANGED
@@ -17,4 +17,5 @@ export var EditorEventType;
|
|
17
17
|
EditorEventType[EditorEventType["ObjectAdded"] = 4] = "ObjectAdded";
|
18
18
|
EditorEventType[EditorEventType["ViewportChanged"] = 5] = "ViewportChanged";
|
19
19
|
EditorEventType[EditorEventType["DisplayResized"] = 6] = "DisplayResized";
|
20
|
+
EditorEventType[EditorEventType["ColorPickerToggled"] = 7] = "ColorPickerToggled";
|
20
21
|
})(EditorEventType || (EditorEventType = {}));
|
@@ -0,0 +1,35 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
5
|
+
<meta charset="utf-8"/>
|
6
|
+
<title>Editor from a bundle</title>
|
7
|
+
<style>
|
8
|
+
body .imageEditorContainer {
|
9
|
+
height: 800px;
|
10
|
+
|
11
|
+
--primary-background-color: green;
|
12
|
+
--primary-background-color-transparent: rgba(255, 240, 200, 0.5);
|
13
|
+
--secondary-background-color: yellow;
|
14
|
+
--primary-foreground-color: black;
|
15
|
+
--secondary-foreground-color: black;
|
16
|
+
}
|
17
|
+
</style>
|
18
|
+
</head>
|
19
|
+
<body>
|
20
|
+
<p>
|
21
|
+
This file tests the bundled version of <code>js-draw</code>.
|
22
|
+
Be sure to run <code>yarn build</code> before opening this!
|
23
|
+
</p>
|
24
|
+
<script src="../dist/bundle.js"></script>
|
25
|
+
<script>
|
26
|
+
const editor1 = new jsdraw.Editor(document.body, {
|
27
|
+
wheelEventsEnabled: false,
|
28
|
+
});
|
29
|
+
editor1.addToolbar();
|
30
|
+
|
31
|
+
const editor2 = new jsdraw.Editor(document.body);
|
32
|
+
editor2.addToolbar();
|
33
|
+
</script>
|
34
|
+
</body>
|
35
|
+
</html>
|