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.
Files changed (40) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/Editor.d.ts +2 -2
  4. package/dist/src/EditorImage.d.ts +1 -1
  5. package/dist/src/EventDispatcher.d.ts +1 -1
  6. package/dist/src/SVGLoader.d.ts +2 -2
  7. package/dist/src/UndoRedoHistory.d.ts +2 -2
  8. package/dist/src/Viewport.d.ts +1 -1
  9. package/dist/src/commands/SerializableCommand.d.ts +1 -1
  10. package/dist/src/components/AbstractComponent.d.ts +3 -3
  11. package/dist/src/components/SVGGlobalAttributesObject.d.ts +1 -1
  12. package/dist/src/components/builders/FreehandLineBuilder.js +4 -3
  13. package/dist/src/components/builders/PressureSensitiveFreehandLineBuilder.js +1 -1
  14. package/dist/src/components/builders/types.d.ts +1 -1
  15. package/dist/src/components/util/StrokeSmoother.d.ts +1 -1
  16. package/dist/src/components/util/StrokeSmoother.js +3 -3
  17. package/dist/src/math/Mat33.d.ts +1 -1
  18. package/dist/src/math/Path.d.ts +1 -1
  19. package/dist/src/math/Vec2.d.ts +2 -2
  20. package/dist/src/rendering/caching/testUtils.d.ts +1 -1
  21. package/dist/src/rendering/caching/types.d.ts +2 -2
  22. package/dist/src/toolbar/IconProvider.d.ts +2 -2
  23. package/dist/src/toolbar/IconProvider.js +11 -3
  24. package/dist/src/toolbar/makeColorInput.d.ts +2 -2
  25. package/dist/src/toolbar/widgets/BaseWidget.d.ts +1 -1
  26. package/dist/src/toolbar/widgets/PenToolWidget.js +2 -1
  27. package/dist/src/tools/BaseTool.js +4 -4
  28. package/dist/src/tools/PanZoom.js +13 -3
  29. package/dist/src/tools/PipetteTool.d.ts +1 -1
  30. package/dist/src/tools/SelectionTool/SelectionHandle.d.ts +3 -3
  31. package/dist/src/tools/ToolbarShortcutHandler.d.ts +1 -1
  32. package/dist/src/types.d.ts +8 -8
  33. package/package.json +1 -1
  34. package/src/components/builders/FreehandLineBuilder.ts +4 -3
  35. package/src/components/builders/PressureSensitiveFreehandLineBuilder.ts +1 -1
  36. package/src/components/util/StrokeSmoother.ts +3 -3
  37. package/src/toolbar/IconProvider.ts +15 -3
  38. package/src/toolbar/widgets/PenToolWidget.ts +3 -1
  39. package/src/tools/PanZoom.ts +16 -3
  40. package/.firebase/hosting.ZG9jcw.cache +0 -338
@@ -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
- declare type HTMLPointerEventType = 'pointerdown' | 'pointermove' | 'pointerup' | 'pointercancel';
33
- declare type HTMLPointerEventFilter = (eventName: HTMLPointerEventType, event: PointerEvent) => boolean;
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
- declare type TooSmallToRenderCheck = (rect: Rect2) => boolean;
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
- declare type CallbackHandler<EventType> = (data: EventType) => void;
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();
@@ -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 declare type SVGLoaderUnknownAttribute = [string, string];
7
- export declare type SVGLoaderUnknownStyleAttribute = {
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
- declare type AnnounceRedoCallback = (command: Command) => void;
4
- declare type AnnounceUndoCallback = (command: Command) => void;
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;
@@ -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
- declare type PointDataType<T extends Point2 | StrokeDataPoint | number> = T extends Point2 ? Point2 : number;
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 declare type DeserializationCallback = (data: Record<string, any> | any[], editor: Editor) => SerializableCommand;
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 declare type LoadSaveData = (string[] | Record<symbol, string | number>);
8
- export declare type LoadSaveDataTable = Record<string, Array<LoadSaveData>>;
9
- export declare type DeserializeCallback = (data: string) => AbstractComponent;
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
- declare type GlobalAttrsList = Array<[string, string | null]>;
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 / 2),
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 / 5);
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
- const width = Viewport.roundPoint(this.startPoint.width / 9, Math.min(this.minFitAllowed, this.startPoint.width / 5));
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 / 3.5, Math.min(this.minFitAllowed, this.startPoint.width / 4));
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 declare type ComponentBuilderFactory = (startPoint: StrokeDataPoint, viewport: Viewport) => ComponentBuilder;
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
- declare type OnCurveAddedCallback = (curve: Curve | null) => void;
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 / 2;
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 / 2;
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;
@@ -1,6 +1,6 @@
1
1
  import { Point2, Vec2 } from './Vec2';
2
2
  import Vec3 from './Vec3';
3
- export declare type Mat33Array = [
3
+ export type Mat33Array = [
4
4
  number,
5
5
  number,
6
6
  number,
@@ -30,7 +30,7 @@ export interface MoveToPathCommand {
30
30
  kind: PathCommandType.MoveTo;
31
31
  point: Point2;
32
32
  }
33
- export declare type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
33
+ export type PathCommand = CubicBezierPathCommand | LinePathCommand | QuadraticBezierPathCommand | MoveToPathCommand;
34
34
  interface IntersectionResult {
35
35
  curve: LineSegment2 | Bezier;
36
36
  parameterValue: number;
@@ -9,5 +9,5 @@ export declare namespace Vec2 {
9
9
  const unitY: Vec3;
10
10
  const zero: Vec3;
11
11
  }
12
- export declare type Point2 = Vec3;
13
- export declare type Vec2 = Vec3;
12
+ export type Point2 = Vec3;
13
+ export type Vec2 = Vec3;
@@ -1,7 +1,7 @@
1
1
  import DummyRenderer from '../renderers/DummyRenderer';
2
2
  import RenderingCache from './RenderingCache';
3
3
  import { CacheProps } from './types';
4
- declare type RenderAllocCallback = (renderer: DummyRenderer) => void;
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 declare type CacheAddress = number;
5
- export declare type BeforeDeallocCallback = () => void;
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
- declare type IconType = SVGSVGElement | HTMLImageElement;
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 primaryStrokeTipPath = `M14,63 L${50 - halfThickness},95 L${50 + halfThickness},90 L88,60 Z`;
314
- const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
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
- declare type OnColorChangeListener = (color: Color4) => void;
4
- declare type SetColorCallback = (color: Color4 | string) => void;
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 declare type SavedToolbuttonState = Record<string, any>;
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
- return this.editor.icons.makePenIcon(scale, color.toHexString());
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
- const currentVelocity = deltaPos.times(1 / deltaTime);
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(smoothedVelocity, 0.5);
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
- declare type ColorListener = (color: Color4 | null) => void;
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 declare type DragStartCallback = (startPoint: Point2) => void;
10
- export declare type DragUpdateCallback = (canvasPoint: Point2) => void;
11
- export declare type DragEndCallback = () => void;
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
- declare type KeyPressListener = (event: KeyPressEvent) => boolean;
4
+ type KeyPressListener = (event: KeyPressEvent) => boolean;
5
5
  export default class ToolbarShortcutHandler extends BaseTool {
6
6
  private listeners;
7
7
  constructor(editor: Editor);
@@ -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 declare type PointerEvt = PointerDownEvt | PointerMoveEvt | PointerUpEvt;
72
- export declare type InputEvt = KeyPressEvent | KeyUpEvent | WheelEvt | GestureCancelEvt | PointerEvt | CopyEvent | PasteEvent;
73
- export declare type EditorNotifier = EventDispatcher<EditorEventType, EditorEventDataType>;
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
- declare type EditorToolEventType = EditorEventType.ToolEnabled | EditorEventType.ToolDisabled | EditorEventType.ToolUpdated;
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 declare type EditorEventDataType = EditorToolEvent | EditorObjectEvent | EditorViewportChangedEvent | DisplayResizedEvent | EditorUndoStackUpdated | CommandDoneEvent | CommandUndoneEvent | ColorPickerToggled | ColorPickerColorSelected | ToolbarDropdownShownEvent;
132
- export declare type OnProgressListener = (amountProcessed: number, totalToProcess: number) => Promise<void> | null;
133
- export declare type ComponentAddedListener = (component: AbstractComponent) => void;
134
- export declare type OnDetermineExportRectListener = (exportRect: Rect2) => void;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-draw",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Draw pictures using a pen, touchscreen, or mouse! JS-draw is a drawing library for JavaScript and TypeScript. ",
5
5
  "main": "./dist/src/lib.d.ts",
6
6
  "types": "./dist/src/lib.js",
@@ -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 / 2),
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 / 5);
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
- const width = Viewport.roundPoint(this.startPoint.width / 9, Math.min(this.minFitAllowed, this.startPoint.width / 5));
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 / 3.5, Math.min(this.minFitAllowed, this.startPoint.width / 4));
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 / 2;
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 / 2;
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 primaryStrokeTipPath = `M14,63 L${50 - halfThickness},95 L${50 + halfThickness},90 L88,60 Z`;
362
- const backgroundStrokeTipPath = `M14,63 L${50 - halfThickness},85 L${50 + halfThickness},83 L88,60 Z`;
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
- return this.editor.icons.makePenIcon(scale, color.toHexString());
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);
@@ -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(smoothedVelocity, 0.5);
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) => {