js-draw 0.10.0 → 0.10.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 +6 -0
- package/dist/bundle.js +1 -1
- package/dist/src/Editor.d.ts +2 -2
- package/dist/src/EditorImage.d.ts +1 -1
- package/dist/src/EventDispatcher.d.ts +1 -1
- package/dist/src/SVGLoader.d.ts +2 -2
- package/dist/src/UndoRedoHistory.d.ts +2 -2
- package/dist/src/Viewport.d.ts +1 -1
- package/dist/src/commands/SerializableCommand.d.ts +1 -1
- package/dist/src/components/AbstractComponent.d.ts +3 -3
- package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
- package/dist/src/components/builders/FreehandLineBuilder.js +4 -3
- package/dist/src/components/builders/PressureSensitiveFreehandLineBuilder.js +1 -1
- package/dist/src/components/builders/types.d.ts +1 -1
- package/dist/src/components/util/StrokeSmoother.d.ts +1 -1
- package/dist/src/components/util/StrokeSmoother.js +3 -3
- package/dist/src/math/Mat33.d.ts +1 -1
- package/dist/src/math/Path.d.ts +1 -1
- package/dist/src/math/Vec2.d.ts +2 -2
- package/dist/src/rendering/caching/testUtils.d.ts +1 -1
- package/dist/src/rendering/caching/types.d.ts +2 -2
- package/dist/src/toolbar/IconProvider.d.ts +2 -2
- package/dist/src/toolbar/IconProvider.js +11 -3
- package/dist/src/toolbar/makeColorInput.d.ts +2 -2
- package/dist/src/toolbar/widgets/BaseWidget.d.ts +1 -1
- package/dist/src/toolbar/widgets/PenToolWidget.js +2 -1
- package/dist/src/tools/BaseTool.js +4 -4
- package/dist/src/tools/PanZoom.js +13 -3
- package/dist/src/tools/PipetteTool.d.ts +1 -1
- package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -3
- package/dist/src/tools/ToolbarShortcutHandler.d.ts +1 -1
- package/dist/src/types.d.ts +8 -8
- package/package.json +1 -1
- package/src/components/builders/FreehandLineBuilder.ts +4 -3
- package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +1 -1
- package/src/components/util/StrokeSmoother.ts +3 -3
- package/src/toolbar/IconProvider.ts +15 -3
- package/src/toolbar/widgets/PenToolWidget.ts +3 -1
- package/src/tools/PanZoom.ts +16 -3
- package/.firebase/hosting.ZG9jcw.cache +0 -338
package/dist/src/Editor.d.ts
CHANGED
@@ -29,8 +29,8 @@ import Pointer from './Pointer';
|
|
29
29
|
import Rect2 from './math/Rect2';
|
30
30
|
import { EditorLocalization } from './localization';
|
31
31
|
import IconProvider from './toolbar/IconProvider';
|
32
|
-
|
33
|
-
|
32
|
+
type HTMLPointerEventType = 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel';
|
33
|
+
type HTMLPointerEventFilter = (eventName: HTMLPointerEventType, event: PointerEvent) => boolean;
|
34
34
|
export interface EditorSettings {
|
35
35
|
/** Defaults to `RenderingMode.CanvasRenderer` */
|
36
36
|
renderingMode: RenderingMode;
|
@@ -27,7 +27,7 @@ export default class EditorImage {
|
|
27
27
|
static addElement(elem: AbstractComponent, applyByFlattening?: boolean): SerializableCommand;
|
28
28
|
private static AddElementCommand;
|
29
29
|
}
|
30
|
-
|
30
|
+
type TooSmallToRenderCheck = (rect: Rect2) => boolean;
|
31
31
|
/** Part of the Editor's image. @internal */
|
32
32
|
export declare class ImageNode {
|
33
33
|
private parent;
|
@@ -15,7 +15,7 @@
|
|
15
15
|
*
|
16
16
|
* @packageDocumentation
|
17
17
|
*/
|
18
|
-
|
18
|
+
type CallbackHandler<EventType> = (data: EventType) => void;
|
19
19
|
export default class EventDispatcher<EventKeyType extends string | symbol | number, EventMessageType> {
|
20
20
|
private listeners;
|
21
21
|
constructor();
|
package/dist/src/SVGLoader.d.ts
CHANGED
@@ -3,8 +3,8 @@ import { ComponentAddedListener, ImageLoader, OnDetermineExportRectListener, OnP
|
|
3
3
|
export declare const defaultSVGViewRect: Rect2;
|
4
4
|
export declare const svgAttributesDataKey = "svgAttrs";
|
5
5
|
export declare const svgStyleAttributesDataKey = "svgStyleAttrs";
|
6
|
-
export
|
7
|
-
export
|
6
|
+
export type SVGLoaderUnknownAttribute = [string, string];
|
7
|
+
export type SVGLoaderUnknownStyleAttribute = {
|
8
8
|
key: string;
|
9
9
|
value: string;
|
10
10
|
priority?: string;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Editor from './Editor';
|
2
2
|
import Command from './commands/Command';
|
3
|
-
|
4
|
-
|
3
|
+
type AnnounceRedoCallback = (command: Command) => void;
|
4
|
+
type AnnounceUndoCallback = (command: Command) => void;
|
5
5
|
declare class UndoRedoHistory {
|
6
6
|
private readonly editor;
|
7
7
|
private announceRedoCallback;
|
package/dist/src/Viewport.d.ts
CHANGED
@@ -4,7 +4,7 @@ import Rect2 from './math/Rect2';
|
|
4
4
|
import { Point2, Vec2 } from './math/Vec2';
|
5
5
|
import { StrokeDataPoint } from './types';
|
6
6
|
import { EditorNotifier } from './types';
|
7
|
-
|
7
|
+
type PointDataType<T extends Point2 | StrokeDataPoint | number> = T extends Point2 ? Point2 : number;
|
8
8
|
export declare abstract class ViewportTransform extends Command {
|
9
9
|
abstract readonly transform: Mat33;
|
10
10
|
}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Editor from '../Editor';
|
2
2
|
import Command from './Command';
|
3
|
-
export
|
3
|
+
export type DeserializationCallback = (data: Record<string, any> | any[], editor: Editor) => SerializableCommand;
|
4
4
|
export default abstract class SerializableCommand extends Command {
|
5
5
|
private commandTypeId;
|
6
6
|
constructor(commandTypeId: string);
|
@@ -4,9 +4,9 @@ import Mat33 from '../math/Mat33';
|
|
4
4
|
import Rect2 from '../math/Rect2';
|
5
5
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
|
-
export
|
8
|
-
export
|
9
|
-
export
|
7
|
+
export type LoadSaveData = (string[] | Record<symbol, string | number>);
|
8
|
+
export type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
|
9
|
+
export type DeserializeCallback = (data: string) => AbstractComponent;
|
10
10
|
export default abstract class AbstractComponent {
|
11
11
|
private readonly componentKind;
|
12
12
|
protected lastChangedTime: number;
|
@@ -4,7 +4,7 @@ import Rect2 from '../math/Rect2';
|
|
4
4
|
import AbstractRenderer from '../rendering/renderers/AbstractRenderer';
|
5
5
|
import AbstractComponent from './AbstractComponent';
|
6
6
|
import { ImageComponentLocalization } from './localization';
|
7
|
-
|
7
|
+
type GlobalAttrsList = Array<[string, string | null]>;
|
8
8
|
export default class SVGGlobalAttributesObject extends AbstractComponent {
|
9
9
|
private readonly attrs;
|
10
10
|
protected contentBBox: Rect2;
|
@@ -33,7 +33,7 @@ export default class FreehandLineBuilder {
|
|
33
33
|
fill: Color4.transparent,
|
34
34
|
stroke: {
|
35
35
|
color: this.startPoint.color,
|
36
|
-
width: this.roundDistance(this.averageWidth
|
36
|
+
width: this.roundDistance(this.averageWidth),
|
37
37
|
}
|
38
38
|
};
|
39
39
|
}
|
@@ -77,7 +77,7 @@ export default class FreehandLineBuilder {
|
|
77
77
|
return this.previewStroke();
|
78
78
|
}
|
79
79
|
getMinFit() {
|
80
|
-
let minFit = Math.min(this.minFitAllowed, this.averageWidth /
|
80
|
+
let minFit = Math.min(this.minFitAllowed, this.averageWidth / 3);
|
81
81
|
if (minFit < 1e-10) {
|
82
82
|
minFit = this.minFitAllowed;
|
83
83
|
}
|
@@ -98,7 +98,8 @@ export default class FreehandLineBuilder {
|
|
98
98
|
if (!this.isFirstSegment) {
|
99
99
|
return [];
|
100
100
|
}
|
101
|
-
|
101
|
+
// Make the circle small -- because of the stroke style, we'll be drawing a stroke around it.
|
102
|
+
const width = Viewport.roundPoint(this.averageWidth / 10, Math.min(this.minFitAllowed, this.averageWidth / 10));
|
102
103
|
const center = this.roundPoint(this.startPoint.pos);
|
103
104
|
// Start on the right, cycle clockwise:
|
104
105
|
// |
|
@@ -209,7 +209,7 @@ export default class PressureSensitiveFreehandLineBuilder {
|
|
209
209
|
if (!this.isFirstSegment) {
|
210
210
|
return;
|
211
211
|
}
|
212
|
-
const width = Viewport.roundPoint(this.startPoint.width /
|
212
|
+
const width = Viewport.roundPoint(this.startPoint.width / 2.2, Math.min(this.minFitAllowed, this.startPoint.width / 4));
|
213
213
|
const center = this.roundPoint(this.startPoint.pos);
|
214
214
|
// Start on the right, cycle clockwise:
|
215
215
|
// |
|
@@ -9,4 +9,4 @@ export interface ComponentBuilder {
|
|
9
9
|
preview(renderer: AbstractRenderer): void;
|
10
10
|
addPoint(point: StrokeDataPoint): void;
|
11
11
|
}
|
12
|
-
export
|
12
|
+
export type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
|
@@ -8,7 +8,7 @@ export interface Curve {
|
|
8
8
|
endWidth: number;
|
9
9
|
endPoint: Vec2;
|
10
10
|
}
|
11
|
-
|
11
|
+
type OnCurveAddedCallback = (curve: Curve | null) => void;
|
12
12
|
export declare class StrokeSmoother {
|
13
13
|
private startPoint;
|
14
14
|
private minFitAllowed;
|
@@ -113,7 +113,7 @@ export class StrokeSmoother {
|
|
113
113
|
const lastPoint = (_a = this.lastPoint) !== null && _a !== void 0 ? _a : newPoint;
|
114
114
|
this.lastPoint = newPoint;
|
115
115
|
this.buffer.push(newPoint.pos);
|
116
|
-
const pointRadius = newPoint.width
|
116
|
+
const pointRadius = newPoint.width;
|
117
117
|
const prevEndWidth = this.curveEndWidth;
|
118
118
|
this.curveEndWidth = pointRadius;
|
119
119
|
if (this.isFirstSegment) {
|
@@ -128,7 +128,7 @@ export class StrokeSmoother {
|
|
128
128
|
const p3 = newPoint.pos;
|
129
129
|
// Quadratic Bézier curve
|
130
130
|
this.currentCurve = new Bezier(p1.xy, p2.xy, p3.xy);
|
131
|
-
this.curveStartWidth = lastPoint.width
|
131
|
+
this.curveStartWidth = lastPoint.width;
|
132
132
|
console.assert(!isNaN(p1.magnitude()) && !isNaN(p2.magnitude()) && !isNaN(p3.magnitude()), 'Expected !NaN');
|
133
133
|
}
|
134
134
|
// If there isn't an entering vector (e.g. because this.isFirstCurve), approximate it.
|
@@ -190,7 +190,7 @@ export class StrokeSmoother {
|
|
190
190
|
}
|
191
191
|
return true;
|
192
192
|
};
|
193
|
-
if (this.buffer.length > 3 && this.approxCurrentCurveLength() > this.curveStartWidth) {
|
193
|
+
if (this.buffer.length > 3 && this.approxCurrentCurveLength() > this.curveStartWidth / 2) {
|
194
194
|
if (!curveMatchesPoints(this.currentCurve)) {
|
195
195
|
// Use a curve that better fits the points
|
196
196
|
this.currentCurve = prevCurve;
|
package/dist/src/math/Mat33.d.ts
CHANGED
package/dist/src/math/Path.d.ts
CHANGED
@@ -30,7 +30,7 @@ export interface MoveToPathCommand {
|
|
30
30
|
kind: PathCommandType.MoveTo;
|
31
31
|
point: Point2;
|
32
32
|
}
|
33
|
-
export
|
33
|
+
export type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
|
34
34
|
interface IntersectionResult {
|
35
35
|
curve: LineSegment2 | Bezier;
|
36
36
|
parameterValue: number;
|
package/dist/src/math/Vec2.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import DummyRenderer from '../renderers/DummyRenderer';
|
2
2
|
import RenderingCache from './RenderingCache';
|
3
3
|
import { CacheProps } from './types';
|
4
|
-
|
4
|
+
type RenderAllocCallback = (renderer: DummyRenderer) => void;
|
5
5
|
export declare const createCache: (onRenderAlloc?: RenderAllocCallback, cacheOptions?: Partial<CacheProps>) => {
|
6
6
|
cache: RenderingCache;
|
7
7
|
editor: import("../../Editor").Editor;
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import { Vec2 } from '../../math/Vec2';
|
2
2
|
import AbstractRenderer from '../renderers/AbstractRenderer';
|
3
3
|
import { CacheRecordManager } from './CacheRecordManager';
|
4
|
-
export
|
5
|
-
export
|
4
|
+
export type CacheAddress = number;
|
5
|
+
export type BeforeDeallocCallback = () => void;
|
6
6
|
export interface CacheProps {
|
7
7
|
createRenderer(): AbstractRenderer;
|
8
8
|
isOfCorrectType(renderer: AbstractRenderer): boolean;
|
@@ -2,7 +2,7 @@ import Color4 from '../Color4';
|
|
2
2
|
import { ComponentBuilderFactory } from '../components/builders/types';
|
3
3
|
import { TextStyle } from '../components/TextComponent';
|
4
4
|
import Pen from '../tools/Pen';
|
5
|
-
|
5
|
+
type IconType = SVGSVGElement | HTMLImageElement;
|
6
6
|
export default class IconProvider {
|
7
7
|
makeUndoIcon(): IconType;
|
8
8
|
makeRedoIcon(mirror?: boolean): IconType;
|
@@ -20,7 +20,7 @@ export default class IconProvider {
|
|
20
20
|
makeZoomIcon(): IconType;
|
21
21
|
makeRotationLockIcon(): IconType;
|
22
22
|
makeTextIcon(textStyle: TextStyle): IconType;
|
23
|
-
makePenIcon(tipThickness: number, color: string | Color4): IconType;
|
23
|
+
makePenIcon(tipThickness: number, color: string | Color4, roundedTip?: boolean): IconType;
|
24
24
|
makeIconFromFactory(pen: Pen, factory: ComponentBuilderFactory): IconType;
|
25
25
|
makePipetteIcon(color?: Color4): IconType;
|
26
26
|
makeResizeViewportIcon(): IconType;
|
@@ -302,7 +302,7 @@ export default class IconProvider {
|
|
302
302
|
icon.appendChild(textNode);
|
303
303
|
return icon;
|
304
304
|
}
|
305
|
-
makePenIcon(tipThickness, color) {
|
305
|
+
makePenIcon(tipThickness, color, roundedTip) {
|
306
306
|
if (color instanceof Color4) {
|
307
307
|
color = color.toHexString();
|
308
308
|
}
|
@@ -310,8 +310,16 @@ export default class IconProvider {
|
|
310
310
|
icon.setAttribute('viewBox', '0 0 100 100');
|
311
311
|
const halfThickness = tipThickness / 2;
|
312
312
|
// Draw a pen-like shape
|
313
|
-
const
|
314
|
-
const
|
313
|
+
const penTipLeft = 50 - halfThickness;
|
314
|
+
const penTipRight = 50 + halfThickness;
|
315
|
+
let tipCenterPrimaryPath = `L${penTipLeft},95 L${penTipRight},90`;
|
316
|
+
let tipCenterBackgroundPath = `L${penTipLeft},85 L${penTipRight},83`;
|
317
|
+
if (roundedTip) {
|
318
|
+
tipCenterPrimaryPath = `L${penTipLeft},95 q${halfThickness},10 ${2 * halfThickness},-5`;
|
319
|
+
tipCenterBackgroundPath = `L${penTipLeft},87 q${halfThickness},10 ${2 * halfThickness},-3`;
|
320
|
+
}
|
321
|
+
const primaryStrokeTipPath = `M14,63 ${tipCenterPrimaryPath} L88,60 Z`;
|
322
|
+
const backgroundStrokeTipPath = `M14,63 ${tipCenterBackgroundPath} L88,60 Z`;
|
315
323
|
icon.innerHTML = `
|
316
324
|
<defs>
|
317
325
|
${checkerboardPatternDef}
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import Color4 from '../Color4';
|
2
2
|
import Editor from '../Editor';
|
3
|
-
|
4
|
-
|
3
|
+
type OnColorChangeListener = (color: Color4) => void;
|
4
|
+
type SetColorCallback = (color: Color4 | string) => void;
|
5
5
|
export declare const makeColorInput: (editor: Editor, onColorChange: OnColorChangeListener) => [HTMLInputElement, HTMLElement, SetColorCallback];
|
6
6
|
export default makeColorInput;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Editor from '../../Editor';
|
2
2
|
import { KeyPressEvent } from '../../types';
|
3
3
|
import { ToolbarLocalization } from '../localization';
|
4
|
-
export
|
4
|
+
export type SavedToolbuttonState = Record<string, any>;
|
5
5
|
export default abstract class BaseWidget {
|
6
6
|
#private;
|
7
7
|
protected editor: Editor;
|
@@ -87,7 +87,8 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
87
87
|
// Use a square-root scale to prevent the pen's tip from overflowing.
|
88
88
|
const scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
|
89
89
|
const color = this.tool.getColor();
|
90
|
-
|
90
|
+
const roundedTip = strokeFactory === makeFreehandLineBuilder;
|
91
|
+
return this.editor.icons.makePenIcon(scale, color.toHexString(), roundedTip);
|
91
92
|
}
|
92
93
|
else {
|
93
94
|
const strokeFactory = this.tool.getStrokeFactory();
|
@@ -1,15 +1,15 @@
|
|
1
1
|
import { EditorEventType } from '../types';
|
2
2
|
export default class BaseTool {
|
3
|
+
onPointerDown(_event) { return false; }
|
4
|
+
onPointerMove(_event) { }
|
5
|
+
onPointerUp(_event) { }
|
6
|
+
onGestureCancel() { }
|
3
7
|
constructor(notifier, description) {
|
4
8
|
this.notifier = notifier;
|
5
9
|
this.description = description;
|
6
10
|
this.enabled = true;
|
7
11
|
this.group = null;
|
8
12
|
}
|
9
|
-
onPointerDown(_event) { return false; }
|
10
|
-
onPointerMove(_event) { }
|
11
|
-
onPointerUp(_event) { }
|
12
|
-
onGestureCancel() { }
|
13
13
|
onWheel(_event) {
|
14
14
|
return false;
|
15
15
|
}
|
@@ -115,13 +115,18 @@ export default class PanZoom extends BaseTool {
|
|
115
115
|
updateVelocity(currentCenter) {
|
116
116
|
const deltaPos = currentCenter.minus(this.lastScreenCenter);
|
117
117
|
const deltaTime = ((new Date()).getTime() - this.lastTimestamp) / 1000;
|
118
|
-
|
119
|
-
let smoothedVelocity = currentVelocity;
|
118
|
+
// We divide by deltaTime. Don't divide by zero.
|
120
119
|
if (deltaTime === 0) {
|
121
120
|
return;
|
122
121
|
}
|
122
|
+
// Ignore duplicate events, unless there has been enough time between them.
|
123
|
+
if (deltaPos.magnitude() === 0 && deltaTime < 0.1) {
|
124
|
+
return;
|
125
|
+
}
|
126
|
+
const currentVelocity = deltaPos.times(1 / deltaTime);
|
127
|
+
let smoothedVelocity = currentVelocity;
|
123
128
|
if (this.velocity) {
|
124
|
-
smoothedVelocity = this.velocity.lerp(
|
129
|
+
smoothedVelocity = this.velocity.lerp(currentVelocity, 0.5);
|
125
130
|
}
|
126
131
|
this.velocity = smoothedVelocity;
|
127
132
|
}
|
@@ -181,6 +186,11 @@ export default class PanZoom extends BaseTool {
|
|
181
186
|
};
|
182
187
|
const shouldInertialScroll = event.current.device === PointerDevice.Touch && event.allPointers.length === 1;
|
183
188
|
if (shouldInertialScroll && this.velocity !== null) {
|
189
|
+
// If the user drags the screen, then stops, then lifts the pointer,
|
190
|
+
// we want the final velocity to reflect the stop at the end (so the velocity
|
191
|
+
// should be near zero). Handle this:
|
192
|
+
this.updateVelocity(event.current.screenPos);
|
193
|
+
// Cancel any ongoing inertial scrolling.
|
184
194
|
(_a = this.inertialScroller) === null || _a === void 0 ? void 0 : _a.stop();
|
185
195
|
this.inertialScroller = new InertialScroller(this.velocity, (scrollDelta) => {
|
186
196
|
if (!this.transform) {
|
@@ -2,7 +2,7 @@ import Color4 from '../Color4';
|
|
2
2
|
import Editor from '../Editor';
|
3
3
|
import { PointerEvt } from '../types';
|
4
4
|
import BaseTool from './BaseTool';
|
5
|
-
|
5
|
+
type ColorListener = (color: Color4 | null) => void;
|
6
6
|
export default class PipetteTool extends BaseTool {
|
7
7
|
private editor;
|
8
8
|
private colorPreviewListener;
|
@@ -6,9 +6,9 @@ export declare enum HandleShape {
|
|
6
6
|
Square = 1
|
7
7
|
}
|
8
8
|
export declare const handleSize = 30;
|
9
|
-
export
|
10
|
-
export
|
11
|
-
export
|
9
|
+
export type DragStartCallback = (startPoint: Point2) => void;
|
10
|
+
export type DragUpdateCallback = (canvasPoint: Point2) => void;
|
11
|
+
export type DragEndCallback = () => void;
|
12
12
|
export default class SelectionHandle {
|
13
13
|
readonly shape: HandleShape;
|
14
14
|
private readonly parentSide;
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import Editor from '../Editor';
|
2
2
|
import { KeyPressEvent } from '../types';
|
3
3
|
import BaseTool from './BaseTool';
|
4
|
-
|
4
|
+
type KeyPressListener = (event: KeyPressEvent) => boolean;
|
5
5
|
export default class ToolbarShortcutHandler extends BaseTool {
|
6
6
|
private listeners;
|
7
7
|
constructor(editor: Editor);
|
package/dist/src/types.d.ts
CHANGED
@@ -68,9 +68,9 @@ export interface PointerMoveEvt extends PointerEvtBase {
|
|
68
68
|
export interface PointerUpEvt extends PointerEvtBase {
|
69
69
|
readonly kind: InputEvtType.PointerUpEvt;
|
70
70
|
}
|
71
|
-
export
|
72
|
-
export
|
73
|
-
export
|
71
|
+
export type PointerEvt = PointerDownEvt | PointerMoveEvt | PointerUpEvt;
|
72
|
+
export type InputEvt = KeyPressEvent | KeyUpEvent | WheelEvt | GestureCancelEvt | PointerEvt | CopyEvent | PasteEvent;
|
73
|
+
export type EditorNotifier = EventDispatcher<EditorEventType, EditorEventDataType>;
|
74
74
|
export declare enum EditorEventType {
|
75
75
|
ToolEnabled = 0,
|
76
76
|
ToolDisabled = 1,
|
@@ -85,7 +85,7 @@ export declare enum EditorEventType {
|
|
85
85
|
ColorPickerColorSelected = 10,
|
86
86
|
ToolbarDropdownShown = 11
|
87
87
|
}
|
88
|
-
|
88
|
+
type EditorToolEventType = EditorEventType.ToolEnabled | EditorEventType.ToolDisabled | EditorEventType.ToolUpdated;
|
89
89
|
export interface EditorToolEvent {
|
90
90
|
readonly kind: EditorToolEventType;
|
91
91
|
readonly tool: BaseTool;
|
@@ -128,10 +128,10 @@ export interface ToolbarDropdownShownEvent {
|
|
128
128
|
readonly kind: EditorEventType.ToolbarDropdownShown;
|
129
129
|
readonly parentWidget: BaseWidget;
|
130
130
|
}
|
131
|
-
export
|
132
|
-
export
|
133
|
-
export
|
134
|
-
export
|
131
|
+
export type EditorEventDataType = EditorToolEvent | EditorObjectEvent | EditorViewportChangedEvent | DisplayResizedEvent | EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent | ColorPickerToggled | ColorPickerColorSelected | ToolbarDropdownShownEvent;
|
132
|
+
export type OnProgressListener = (amountProcessed: number, totalToProcess: number) => Promise<void> | null;
|
133
|
+
export type ComponentAddedListener = (component: AbstractComponent) => void;
|
134
|
+
export type OnDetermineExportRectListener = (exportRect: Rect2) => void;
|
135
135
|
export interface ImageLoader {
|
136
136
|
start(onAddComponent: ComponentAddedListener, onProgressListener: OnProgressListener, onDetermineExportRect?: OnDetermineExportRectListener): Promise<void>;
|
137
137
|
}
|
package/package.json
CHANGED
@@ -55,7 +55,7 @@ export default class FreehandLineBuilder implements ComponentBuilder {
|
|
55
55
|
fill: Color4.transparent,
|
56
56
|
stroke: {
|
57
57
|
color: this.startPoint.color,
|
58
|
-
width: this.roundDistance(this.averageWidth
|
58
|
+
width: this.roundDistance(this.averageWidth),
|
59
59
|
}
|
60
60
|
};
|
61
61
|
}
|
@@ -108,7 +108,7 @@ export default class FreehandLineBuilder implements ComponentBuilder {
|
|
108
108
|
}
|
109
109
|
|
110
110
|
private getMinFit(): number {
|
111
|
-
let minFit = Math.min(this.minFitAllowed, this.averageWidth /
|
111
|
+
let minFit = Math.min(this.minFitAllowed, this.averageWidth / 3);
|
112
112
|
|
113
113
|
if (minFit < 1e-10) {
|
114
114
|
minFit = this.minFitAllowed;
|
@@ -135,7 +135,8 @@ export default class FreehandLineBuilder implements ComponentBuilder {
|
|
135
135
|
return [];
|
136
136
|
}
|
137
137
|
|
138
|
-
|
138
|
+
// Make the circle small -- because of the stroke style, we'll be drawing a stroke around it.
|
139
|
+
const width = Viewport.roundPoint(this.averageWidth / 10, Math.min(this.minFitAllowed, this.averageWidth / 10));
|
139
140
|
const center = this.roundPoint(this.startPoint.pos);
|
140
141
|
|
141
142
|
// Start on the right, cycle clockwise:
|
@@ -290,7 +290,7 @@ export default class PressureSensitiveFreehandLineBuilder implements ComponentBu
|
|
290
290
|
return;
|
291
291
|
}
|
292
292
|
|
293
|
-
const width = Viewport.roundPoint(this.startPoint.width /
|
293
|
+
const width = Viewport.roundPoint(this.startPoint.width / 2.2, Math.min(this.minFitAllowed, this.startPoint.width / 4));
|
294
294
|
const center = this.roundPoint(this.startPoint.pos);
|
295
295
|
|
296
296
|
// Start on the right, cycle clockwise:
|
@@ -161,7 +161,7 @@ export class StrokeSmoother {
|
|
161
161
|
this.lastPoint = newPoint;
|
162
162
|
|
163
163
|
this.buffer.push(newPoint.pos);
|
164
|
-
const pointRadius = newPoint.width
|
164
|
+
const pointRadius = newPoint.width;
|
165
165
|
const prevEndWidth = this.curveEndWidth;
|
166
166
|
this.curveEndWidth = pointRadius;
|
167
167
|
|
@@ -182,7 +182,7 @@ export class StrokeSmoother {
|
|
182
182
|
this.currentCurve = new Bezier(
|
183
183
|
p1.xy, p2.xy, p3.xy
|
184
184
|
);
|
185
|
-
this.curveStartWidth = lastPoint.width
|
185
|
+
this.curveStartWidth = lastPoint.width;
|
186
186
|
console.assert(!isNaN(p1.magnitude()) && !isNaN(p2.magnitude()) && !isNaN(p3.magnitude()), 'Expected !NaN');
|
187
187
|
}
|
188
188
|
|
@@ -271,7 +271,7 @@ export class StrokeSmoother {
|
|
271
271
|
return true;
|
272
272
|
};
|
273
273
|
|
274
|
-
if (this.buffer.length > 3 && this.approxCurrentCurveLength() > this.curveStartWidth) {
|
274
|
+
if (this.buffer.length > 3 && this.approxCurrentCurveLength() > this.curveStartWidth / 2) {
|
275
275
|
if (!curveMatchesPoints(this.currentCurve)) {
|
276
276
|
// Use a curve that better fits the points
|
277
277
|
this.currentCurve = prevCurve;
|
@@ -347,7 +347,7 @@ export default class IconProvider {
|
|
347
347
|
return icon;
|
348
348
|
}
|
349
349
|
|
350
|
-
public makePenIcon(tipThickness: number, color: string|Color4): IconType {
|
350
|
+
public makePenIcon(tipThickness: number, color: string|Color4, roundedTip?: boolean): IconType {
|
351
351
|
if (color instanceof Color4) {
|
352
352
|
color = color.toHexString();
|
353
353
|
}
|
@@ -358,8 +358,20 @@ export default class IconProvider {
|
|
358
358
|
const halfThickness = tipThickness / 2;
|
359
359
|
|
360
360
|
// Draw a pen-like shape
|
361
|
-
const
|
362
|
-
const
|
361
|
+
const penTipLeft = 50 - halfThickness;
|
362
|
+
const penTipRight = 50 + halfThickness;
|
363
|
+
|
364
|
+
let tipCenterPrimaryPath = `L${penTipLeft},95 L${penTipRight},90`;
|
365
|
+
let tipCenterBackgroundPath = `L${penTipLeft},85 L${penTipRight},83`;
|
366
|
+
|
367
|
+
if (roundedTip) {
|
368
|
+
tipCenterPrimaryPath = `L${penTipLeft},95 q${halfThickness},10 ${2 * halfThickness},-5`;
|
369
|
+
tipCenterBackgroundPath = `L${penTipLeft},87 q${halfThickness},10 ${2 * halfThickness},-3`;
|
370
|
+
}
|
371
|
+
|
372
|
+
const primaryStrokeTipPath = `M14,63 ${tipCenterPrimaryPath} L88,60 Z`;
|
373
|
+
const backgroundStrokeTipPath = `M14,63 ${tipCenterBackgroundPath} L88,60 Z`;
|
374
|
+
|
363
375
|
icon.innerHTML = `
|
364
376
|
<defs>
|
365
377
|
${checkerboardPatternDef}
|
@@ -122,7 +122,9 @@ export default class PenToolWidget extends BaseToolWidget {
|
|
122
122
|
// Use a square-root scale to prevent the pen's tip from overflowing.
|
123
123
|
const scale = Math.round(Math.sqrt(this.tool.getThickness()) * 4);
|
124
124
|
const color = this.tool.getColor();
|
125
|
-
|
125
|
+
const roundedTip = strokeFactory === makeFreehandLineBuilder;
|
126
|
+
|
127
|
+
return this.editor.icons.makePenIcon(scale, color.toHexString(), roundedTip);
|
126
128
|
} else {
|
127
129
|
const strokeFactory = this.tool.getStrokeFactory();
|
128
130
|
return this.editor.icons.makeIconFromFactory(this.tool, strokeFactory);
|
package/src/tools/PanZoom.ts
CHANGED
@@ -143,15 +143,22 @@ export default class PanZoom extends BaseTool {
|
|
143
143
|
private updateVelocity(currentCenter: Point2) {
|
144
144
|
const deltaPos = currentCenter.minus(this.lastScreenCenter);
|
145
145
|
const deltaTime = ((new Date()).getTime() - this.lastTimestamp) / 1000;
|
146
|
-
const currentVelocity = deltaPos.times(1 / deltaTime);
|
147
|
-
let smoothedVelocity = currentVelocity;
|
148
146
|
|
147
|
+
// We divide by deltaTime. Don't divide by zero.
|
149
148
|
if (deltaTime === 0) {
|
150
149
|
return;
|
151
150
|
}
|
152
151
|
|
152
|
+
// Ignore duplicate events, unless there has been enough time between them.
|
153
|
+
if (deltaPos.magnitude() === 0 && deltaTime < 0.1) {
|
154
|
+
return;
|
155
|
+
}
|
156
|
+
|
157
|
+
const currentVelocity = deltaPos.times(1 / deltaTime);
|
158
|
+
let smoothedVelocity = currentVelocity;
|
159
|
+
|
153
160
|
if (this.velocity) {
|
154
|
-
smoothedVelocity = this.velocity.lerp(
|
161
|
+
smoothedVelocity = this.velocity.lerp(currentVelocity, 0.5);
|
155
162
|
}
|
156
163
|
|
157
164
|
this.velocity = smoothedVelocity;
|
@@ -230,6 +237,12 @@ export default class PanZoom extends BaseTool {
|
|
230
237
|
event.current.device === PointerDevice.Touch && event.allPointers.length === 1;
|
231
238
|
|
232
239
|
if (shouldInertialScroll && this.velocity !== null) {
|
240
|
+
// If the user drags the screen, then stops, then lifts the pointer,
|
241
|
+
// we want the final velocity to reflect the stop at the end (so the velocity
|
242
|
+
// should be near zero). Handle this:
|
243
|
+
this.updateVelocity(event.current.screenPos);
|
244
|
+
|
245
|
+
// Cancel any ongoing inertial scrolling.
|
233
246
|
this.inertialScroller?.stop();
|
234
247
|
|
235
248
|
this.inertialScroller = new InertialScroller(this.velocity, (scrollDelta: Vec2) => {
|