js-draw 0.1.10 → 0.1.11
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 +5 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -0
- package/dist/src/Editor.js +17 -1
- package/dist/src/Viewport.js +2 -0
- package/dist/src/localizations/es.js +1 -1
- package/dist/src/toolbar/localization.d.ts +1 -0
- package/dist/src/toolbar/localization.js +1 -0
- package/dist/src/toolbar/widgets/BaseWidget.js +1 -1
- package/dist/src/toolbar/widgets/HandToolWidget.js +8 -1
- package/dist/src/tools/PanZoom.js +6 -4
- package/dist/src/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/Editor.ts +23 -0
- package/src/Viewport.ts +2 -0
- package/src/localizations/es.ts +2 -0
- package/src/toolbar/localization.ts +2 -0
- package/src/toolbar/toolbar.css +1 -2
- package/src/toolbar/widgets/BaseWidget.ts +1 -1
- package/src/toolbar/widgets/HandToolWidget.ts +12 -1
- package/src/tools/PanZoom.ts +7 -4
- package/src/types.ts +1 -0
package/dist/src/Editor.d.ts
CHANGED
@@ -15,6 +15,8 @@ export interface EditorSettings {
|
|
15
15
|
renderingMode: RenderingMode;
|
16
16
|
localization: Partial<EditorLocalization>;
|
17
17
|
wheelEventsEnabled: boolean | 'only-if-focused';
|
18
|
+
minZoom: number;
|
19
|
+
maxZoom: number;
|
18
20
|
}
|
19
21
|
export declare class Editor {
|
20
22
|
private container;
|
package/dist/src/Editor.js
CHANGED
@@ -26,7 +26,7 @@ import Mat33 from './geometry/Mat33';
|
|
26
26
|
import getLocalizationTable from './localizations/getLocalizationTable';
|
27
27
|
export class Editor {
|
28
28
|
constructor(parent, settings = {}) {
|
29
|
-
var _a, _b;
|
29
|
+
var _a, _b, _c, _d;
|
30
30
|
this.announceUndoCallback = (command) => {
|
31
31
|
this.announceForAccessibility(this.localization.undoAnnouncement(command.description(this.localization)));
|
32
32
|
};
|
@@ -40,6 +40,8 @@ export class Editor {
|
|
40
40
|
wheelEventsEnabled: (_a = settings.wheelEventsEnabled) !== null && _a !== void 0 ? _a : true,
|
41
41
|
renderingMode: (_b = settings.renderingMode) !== null && _b !== void 0 ? _b : RenderingMode.CanvasRenderer,
|
42
42
|
localization: this.localization,
|
43
|
+
minZoom: (_c = settings.minZoom) !== null && _c !== void 0 ? _c : 2e-10,
|
44
|
+
maxZoom: (_d = settings.maxZoom) !== null && _d !== void 0 ? _d : 1e12,
|
43
45
|
};
|
44
46
|
this.container = document.createElement('div');
|
45
47
|
this.renderingRegion = document.createElement('div');
|
@@ -71,6 +73,20 @@ export class Editor {
|
|
71
73
|
this.registerListeners();
|
72
74
|
this.queueRerender();
|
73
75
|
this.hideLoadingWarning();
|
76
|
+
// Enforce zoom limits.
|
77
|
+
this.notifier.on(EditorEventType.ViewportChanged, evt => {
|
78
|
+
if (evt.kind === EditorEventType.ViewportChanged) {
|
79
|
+
const zoom = evt.newTransform.transformVec3(Vec2.unitX).length();
|
80
|
+
if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
|
81
|
+
const oldZoom = evt.oldTransform.transformVec3(Vec2.unitX).length();
|
82
|
+
let resetTransform = Mat33.identity;
|
83
|
+
if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
|
84
|
+
resetTransform = evt.oldTransform;
|
85
|
+
}
|
86
|
+
this.viewport.resetTransform(resetTransform);
|
87
|
+
}
|
88
|
+
}
|
89
|
+
});
|
74
90
|
}
|
75
91
|
// Returns a reference to this' container.
|
76
92
|
// Example usage:
|
package/dist/src/Viewport.js
CHANGED
@@ -38,11 +38,13 @@ export class Viewport {
|
|
38
38
|
// Updates the transformation directly. Using ViewportTransform is preferred.
|
39
39
|
// [newTransform] should map from canvas coordinates to screen coordinates.
|
40
40
|
resetTransform(newTransform = Mat33.identity) {
|
41
|
+
const oldTransform = this.transform;
|
41
42
|
this.transform = newTransform;
|
42
43
|
this.inverseTransform = newTransform.inverse();
|
43
44
|
this.notifier.dispatch(EditorEventType.ViewportChanged, {
|
44
45
|
kind: EditorEventType.ViewportChanged,
|
45
46
|
newTransform,
|
47
|
+
oldTransform,
|
46
48
|
});
|
47
49
|
}
|
48
50
|
get screenToCanvasTransform() {
|
@@ -6,7 +6,7 @@ const localization = Object.assign(Object.assign({}, defaultEditorLocalization),
|
|
6
6
|
loading: (percentage) => `Cargando: ${percentage}%...`, imageEditor: 'Editor de dibujos', undoAnnouncement: (commandDescription) => `${commandDescription} fue deshecho`, redoAnnouncement: (commandDescription) => `${commandDescription} fue rehecho`, undo: 'Deshace', redo: 'Rehace',
|
7
7
|
// Strings for the toolbar
|
8
8
|
// (see src/toolbar/localization.ts)
|
9
|
-
pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectObjectType: 'Forma de dibuja:', handTool: 'Mover', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFronScreen: 'Selecciona un color de la pantalla', dropdownShown(toolName) {
|
9
|
+
pen: 'Lapiz', eraser: 'Borrador', select: 'Selecciona', thicknessLabel: 'Tamaño: ', colorLabel: 'Color: ', doneLoading: 'El cargado terminó', fontLabel: 'Fuente: ', anyDevicePanning: 'Mover la pantalla con todo dispotivo', touchPanning: 'Mover la pantalla con un dedo', touchPanTool: 'Instrumento de mover la pantalla con un dedo', outlinedRectanglePen: 'Rectángulo con nada más que un borde', filledRectanglePen: 'Rectángulo sin borde', linePen: 'Línea', arrowPen: 'Flecha', freehandPen: 'Dibuja sin restricción de forma', selectObjectType: 'Forma de dibuja:', handTool: 'Mover', zoom: 'Zoom', resetView: 'Reiniciar vista', resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado', deleteSelection: 'Borra la selección', duplicateSelection: 'Duplica la selección', pickColorFronScreen: 'Selecciona un color de la pantalla', dropdownShown(toolName) {
|
10
10
|
return `Menú por ${toolName} es visible`;
|
11
11
|
}, dropdownHidden: function (toolName) {
|
12
12
|
return `Menú por ${toolName} fue ocultado`;
|
@@ -45,7 +45,7 @@ export default class BaseWidget {
|
|
45
45
|
return true;
|
46
46
|
}
|
47
47
|
setupActionBtnClickListener(button) {
|
48
|
-
const clickTriggers = {
|
48
|
+
const clickTriggers = { Enter: true, ' ': true, };
|
49
49
|
button.onkeydown = (evt) => {
|
50
50
|
let handled = false;
|
51
51
|
if (evt.key in clickTriggers) {
|
@@ -10,10 +10,12 @@ const makeZoomControl = (localizationTable, editor) => {
|
|
10
10
|
const zoomLevelRow = document.createElement('div');
|
11
11
|
const increaseButton = document.createElement('button');
|
12
12
|
const decreaseButton = document.createElement('button');
|
13
|
+
const resetViewButton = document.createElement('button');
|
13
14
|
const zoomLevelDisplay = document.createElement('span');
|
14
15
|
increaseButton.innerText = '+';
|
15
16
|
decreaseButton.innerText = '-';
|
16
|
-
|
17
|
+
resetViewButton.innerText = localizationTable.resetView;
|
18
|
+
zoomLevelRow.replaceChildren(zoomLevelDisplay, increaseButton, decreaseButton, resetViewButton);
|
17
19
|
zoomLevelRow.classList.add(`${toolbarCSSPrefix}zoomLevelEditor`);
|
18
20
|
zoomLevelDisplay.classList.add('zoomDisplay');
|
19
21
|
let lastZoom;
|
@@ -34,6 +36,8 @@ const makeZoomControl = (localizationTable, editor) => {
|
|
34
36
|
editor.notifier.on(EditorEventType.ViewportChanged, (event) => {
|
35
37
|
if (event.kind === EditorEventType.ViewportChanged) {
|
36
38
|
updateZoomDisplay();
|
39
|
+
// Can't reset if already reset.
|
40
|
+
resetViewButton.disabled = event.newTransform.eq(Mat33.identity);
|
37
41
|
}
|
38
42
|
});
|
39
43
|
const zoomBy = (factor) => {
|
@@ -47,6 +51,9 @@ const makeZoomControl = (localizationTable, editor) => {
|
|
47
51
|
decreaseButton.onclick = () => {
|
48
52
|
zoomBy(4.0 / 5);
|
49
53
|
};
|
54
|
+
resetViewButton.onclick = () => {
|
55
|
+
editor.dispatch(new Viewport.ViewportTransform(editor.viewport.canvasToScreenTransform.inverse()), true);
|
56
|
+
};
|
50
57
|
return zoomLevelRow;
|
51
58
|
};
|
52
59
|
class ZoomWidget extends BaseWidget {
|
@@ -121,15 +121,15 @@ export default class PanZoom extends BaseTool {
|
|
121
121
|
this.transform.apply(this.editor);
|
122
122
|
}
|
123
123
|
onWheel({ delta, screenPos }) {
|
124
|
-
|
125
|
-
|
126
|
-
|
124
|
+
// Reset the transformation -- wheel events are individual events, so we don't
|
125
|
+
// need to unapply/reapply.
|
126
|
+
this.transform = new Viewport.ViewportTransform(Mat33.identity);
|
127
127
|
const canvasPos = this.editor.viewport.screenToCanvas(screenPos);
|
128
128
|
const toCanvas = this.editor.viewport.screenToCanvasTransform;
|
129
129
|
// Transform without including translation
|
130
130
|
const translation = toCanvas.transformVec3(Vec3.of(-delta.x, -delta.y, 0));
|
131
131
|
const pinchZoomScaleFactor = 1.04;
|
132
|
-
const transformUpdate = Mat33.scaling2D(Math.pow(pinchZoomScaleFactor, -delta.z), canvasPos).rightMul(Mat33.translation(translation));
|
132
|
+
const transformUpdate = Mat33.scaling2D(Math.max(0.25, Math.min(Math.pow(pinchZoomScaleFactor, -delta.z), 4)), canvasPos).rightMul(Mat33.translation(translation));
|
133
133
|
this.updateTransform(transformUpdate);
|
134
134
|
return true;
|
135
135
|
}
|
@@ -137,6 +137,8 @@ export default class PanZoom extends BaseTool {
|
|
137
137
|
if (!(this.mode & PanZoomMode.Keyboard)) {
|
138
138
|
return false;
|
139
139
|
}
|
140
|
+
// No need to keep the same the transform for keyboard events.
|
141
|
+
this.transform = new Viewport.ViewportTransform(Mat33.identity);
|
140
142
|
let translation = Vec2.zero;
|
141
143
|
let scale = 1;
|
142
144
|
let rotation = 0;
|
package/dist/src/types.d.ts
CHANGED
@@ -79,6 +79,7 @@ export interface EditorObjectEvent {
|
|
79
79
|
export interface EditorViewportChangedEvent {
|
80
80
|
readonly kind: EditorEventType.ViewportChanged;
|
81
81
|
readonly newTransform: Mat33;
|
82
|
+
readonly oldTransform: Mat33;
|
82
83
|
}
|
83
84
|
export interface DisplayResizedEvent {
|
84
85
|
readonly kind: EditorEventType.DisplayResized;
|
package/package.json
CHANGED
package/src/Editor.ts
CHANGED
@@ -31,6 +31,9 @@ export interface EditorSettings {
|
|
31
31
|
// This does not include pinch-zoom events.
|
32
32
|
// Defaults to true.
|
33
33
|
wheelEventsEnabled: boolean|'only-if-focused';
|
34
|
+
|
35
|
+
minZoom: number,
|
36
|
+
maxZoom: number,
|
34
37
|
}
|
35
38
|
|
36
39
|
export class Editor {
|
@@ -69,6 +72,8 @@ export class Editor {
|
|
69
72
|
wheelEventsEnabled: settings.wheelEventsEnabled ?? true,
|
70
73
|
renderingMode: settings.renderingMode ?? RenderingMode.CanvasRenderer,
|
71
74
|
localization: this.localization,
|
75
|
+
minZoom: settings.minZoom ?? 2e-10,
|
76
|
+
maxZoom: settings.maxZoom ?? 1e12,
|
72
77
|
};
|
73
78
|
|
74
79
|
this.container = document.createElement('div');
|
@@ -111,6 +116,24 @@ export class Editor {
|
|
111
116
|
this.registerListeners();
|
112
117
|
this.queueRerender();
|
113
118
|
this.hideLoadingWarning();
|
119
|
+
|
120
|
+
|
121
|
+
// Enforce zoom limits.
|
122
|
+
this.notifier.on(EditorEventType.ViewportChanged, evt => {
|
123
|
+
if (evt.kind === EditorEventType.ViewportChanged) {
|
124
|
+
const zoom = evt.newTransform.transformVec3(Vec2.unitX).length();
|
125
|
+
if (zoom > this.settings.maxZoom || zoom < this.settings.minZoom) {
|
126
|
+
const oldZoom = evt.oldTransform.transformVec3(Vec2.unitX).length();
|
127
|
+
let resetTransform = Mat33.identity;
|
128
|
+
|
129
|
+
if (oldZoom <= this.settings.maxZoom && oldZoom >= this.settings.minZoom) {
|
130
|
+
resetTransform = evt.oldTransform;
|
131
|
+
}
|
132
|
+
|
133
|
+
this.viewport.resetTransform(resetTransform);
|
134
|
+
}
|
135
|
+
}
|
136
|
+
});
|
114
137
|
}
|
115
138
|
|
116
139
|
// Returns a reference to this' container.
|
package/src/Viewport.ts
CHANGED
@@ -103,11 +103,13 @@ export class Viewport {
|
|
103
103
|
// Updates the transformation directly. Using ViewportTransform is preferred.
|
104
104
|
// [newTransform] should map from canvas coordinates to screen coordinates.
|
105
105
|
public resetTransform(newTransform: Mat33 = Mat33.identity) {
|
106
|
+
const oldTransform = this.transform;
|
106
107
|
this.transform = newTransform;
|
107
108
|
this.inverseTransform = newTransform.inverse();
|
108
109
|
this.notifier.dispatch(EditorEventType.ViewportChanged, {
|
109
110
|
kind: EditorEventType.ViewportChanged,
|
110
111
|
newTransform,
|
112
|
+
oldTransform,
|
111
113
|
});
|
112
114
|
}
|
113
115
|
|
package/src/localizations/es.ts
CHANGED
@@ -33,6 +33,8 @@ const localization: EditorLocalization = {
|
|
33
33
|
freehandPen: 'Dibuja sin restricción de forma',
|
34
34
|
selectObjectType: 'Forma de dibuja:',
|
35
35
|
handTool: 'Mover',
|
36
|
+
zoom: 'Zoom',
|
37
|
+
resetView: 'Reiniciar vista',
|
36
38
|
resizeImageToSelection: 'Redimensionar la imagen a lo que está seleccionado',
|
37
39
|
deleteSelection: 'Borra la selección',
|
38
40
|
duplicateSelection: 'Duplica la selección',
|
@@ -23,6 +23,7 @@ export interface ToolbarLocalization {
|
|
23
23
|
undo: string;
|
24
24
|
redo: string;
|
25
25
|
zoom: string;
|
26
|
+
resetView: string;
|
26
27
|
selectionToolKeyboardShortcuts: string;
|
27
28
|
|
28
29
|
dropdownShown: (toolName: string)=> string;
|
@@ -37,6 +38,7 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
|
|
37
38
|
select: 'Select',
|
38
39
|
handTool: 'Pan',
|
39
40
|
zoom: 'Zoom',
|
41
|
+
resetView: 'Reset view',
|
40
42
|
thicknessLabel: 'Thickness: ',
|
41
43
|
colorLabel: 'Color: ',
|
42
44
|
fontLabel: 'Font: ',
|
package/src/toolbar/toolbar.css
CHANGED
@@ -51,7 +51,7 @@ export default abstract class BaseWidget {
|
|
51
51
|
}
|
52
52
|
|
53
53
|
protected setupActionBtnClickListener(button: HTMLElement) {
|
54
|
-
const clickTriggers = {
|
54
|
+
const clickTriggers = { Enter: true, ' ': true, };
|
55
55
|
button.onkeydown = (evt) => {
|
56
56
|
let handled = false;
|
57
57
|
|
@@ -14,10 +14,12 @@ const makeZoomControl = (localizationTable: ToolbarLocalization, editor: Editor)
|
|
14
14
|
|
15
15
|
const increaseButton = document.createElement('button');
|
16
16
|
const decreaseButton = document.createElement('button');
|
17
|
+
const resetViewButton = document.createElement('button');
|
17
18
|
const zoomLevelDisplay = document.createElement('span');
|
18
19
|
increaseButton.innerText = '+';
|
19
20
|
decreaseButton.innerText = '-';
|
20
|
-
|
21
|
+
resetViewButton.innerText = localizationTable.resetView;
|
22
|
+
zoomLevelRow.replaceChildren(zoomLevelDisplay, increaseButton, decreaseButton, resetViewButton);
|
21
23
|
|
22
24
|
zoomLevelRow.classList.add(`${toolbarCSSPrefix}zoomLevelEditor`);
|
23
25
|
zoomLevelDisplay.classList.add('zoomDisplay');
|
@@ -42,6 +44,9 @@ const makeZoomControl = (localizationTable: ToolbarLocalization, editor: Editor)
|
|
42
44
|
editor.notifier.on(EditorEventType.ViewportChanged, (event) => {
|
43
45
|
if (event.kind === EditorEventType.ViewportChanged) {
|
44
46
|
updateZoomDisplay();
|
47
|
+
|
48
|
+
// Can't reset if already reset.
|
49
|
+
resetViewButton.disabled = event.newTransform.eq(Mat33.identity);
|
45
50
|
}
|
46
51
|
});
|
47
52
|
|
@@ -59,6 +64,12 @@ const makeZoomControl = (localizationTable: ToolbarLocalization, editor: Editor)
|
|
59
64
|
zoomBy(4.0/5);
|
60
65
|
};
|
61
66
|
|
67
|
+
resetViewButton.onclick = () => {
|
68
|
+
editor.dispatch(new Viewport.ViewportTransform(
|
69
|
+
editor.viewport.canvasToScreenTransform.inverse()
|
70
|
+
), true);
|
71
|
+
};
|
72
|
+
|
62
73
|
return zoomLevelRow;
|
63
74
|
};
|
64
75
|
|
package/src/tools/PanZoom.ts
CHANGED
@@ -158,9 +158,9 @@ export default class PanZoom extends BaseTool {
|
|
158
158
|
}
|
159
159
|
|
160
160
|
public onWheel({ delta, screenPos }: WheelEvt): boolean {
|
161
|
-
|
162
|
-
|
163
|
-
|
161
|
+
// Reset the transformation -- wheel events are individual events, so we don't
|
162
|
+
// need to unapply/reapply.
|
163
|
+
this.transform = new Viewport.ViewportTransform(Mat33.identity);
|
164
164
|
|
165
165
|
const canvasPos = this.editor.viewport.screenToCanvas(screenPos);
|
166
166
|
const toCanvas = this.editor.viewport.screenToCanvasTransform;
|
@@ -172,7 +172,7 @@ export default class PanZoom extends BaseTool {
|
|
172
172
|
);
|
173
173
|
const pinchZoomScaleFactor = 1.04;
|
174
174
|
const transformUpdate = Mat33.scaling2D(
|
175
|
-
Math.pow(pinchZoomScaleFactor, -delta.z), canvasPos
|
175
|
+
Math.max(0.25, Math.min(Math.pow(pinchZoomScaleFactor, -delta.z), 4)), canvasPos
|
176
176
|
).rightMul(
|
177
177
|
Mat33.translation(translation)
|
178
178
|
);
|
@@ -186,6 +186,9 @@ export default class PanZoom extends BaseTool {
|
|
186
186
|
return false;
|
187
187
|
}
|
188
188
|
|
189
|
+
// No need to keep the same the transform for keyboard events.
|
190
|
+
this.transform = new Viewport.ViewportTransform(Mat33.identity);
|
191
|
+
|
189
192
|
let translation = Vec2.zero;
|
190
193
|
let scale = 1;
|
191
194
|
let rotation = 0;
|