js-draw 0.10.1 → 0.10.3

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 (41) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/bundle.js +1 -1
  3. package/dist/src/components/AbstractComponent.d.ts +1 -0
  4. package/dist/src/components/AbstractComponent.js +6 -0
  5. package/dist/src/components/ImageComponent.d.ts +1 -0
  6. package/dist/src/components/ImageComponent.js +4 -0
  7. package/dist/src/components/Stroke.d.ts +2 -0
  8. package/dist/src/components/Stroke.js +5 -0
  9. package/dist/src/components/TextComponent.d.ts +1 -0
  10. package/dist/src/components/TextComponent.js +3 -0
  11. package/dist/src/components/util/StrokeSmoother.js +9 -5
  12. package/dist/src/math/Mat33.d.ts +2 -0
  13. package/dist/src/math/Mat33.js +7 -0
  14. package/dist/src/math/Path.js +3 -0
  15. package/dist/src/rendering/Display.js +5 -2
  16. package/dist/src/rendering/caching/RenderingCache.js +5 -1
  17. package/dist/src/rendering/caching/RenderingCacheNode.js +5 -2
  18. package/dist/src/rendering/caching/testUtils.js +1 -1
  19. package/dist/src/rendering/caching/types.d.ts +2 -2
  20. package/dist/src/toolbar/IconProvider.d.ts +1 -0
  21. package/dist/src/toolbar/IconProvider.js +12 -0
  22. package/dist/src/toolbar/localization.d.ts +1 -0
  23. package/dist/src/toolbar/localization.js +1 -0
  24. package/dist/src/tools/PanZoom.d.ts +2 -1
  25. package/dist/src/tools/PanZoom.js +33 -17
  26. package/package.json +11 -11
  27. package/src/components/AbstractComponent.ts +8 -0
  28. package/src/components/ImageComponent.ts +5 -0
  29. package/src/components/Stroke.ts +11 -0
  30. package/src/components/TextComponent.ts +4 -0
  31. package/src/components/util/StrokeSmoother.ts +12 -9
  32. package/src/math/Mat33.ts +9 -0
  33. package/src/math/Path.ts +4 -0
  34. package/src/rendering/Display.ts +7 -2
  35. package/src/rendering/caching/RenderingCache.ts +10 -2
  36. package/src/rendering/caching/RenderingCacheNode.ts +6 -2
  37. package/src/rendering/caching/testUtils.ts +2 -2
  38. package/src/rendering/caching/types.ts +2 -2
  39. package/src/toolbar/IconProvider.ts +13 -0
  40. package/src/toolbar/localization.ts +3 -0
  41. package/src/tools/PanZoom.ts +34 -14
@@ -48,8 +48,16 @@ export default class RenderingCache {
48
48
 
49
49
  this.rootNode = this.rootNode!.smallestChildContaining(visibleRect) ?? this.rootNode;
50
50
 
51
- const visibleLeaves = image.getLeavesIntersectingRegion(viewport.visibleRect, rect => screenRenderer.isTooSmallToRender(rect));
52
- if (visibleLeaves.length > this.sharedState.props.minComponentsToUseCache) {
51
+ const visibleLeaves = image.getLeavesIntersectingRegion(
52
+ viewport.visibleRect, rect => screenRenderer.isTooSmallToRender(rect)
53
+ );
54
+
55
+ let approxVisibleRenderTime = 0;
56
+ for (const leaf of visibleLeaves) {
57
+ approxVisibleRenderTime += leaf.getContent()!.getProportionalRenderingTime();
58
+ }
59
+
60
+ if (approxVisibleRenderTime > this.sharedState.props.minProportionalRenderTimeToUseCache) {
53
61
  this.rootNode!.renderItems(screenRenderer, [ image ], viewport);
54
62
  } else {
55
63
  image.render(screenRenderer, visibleRect);
@@ -250,9 +250,13 @@ export default class RenderingCacheNode {
250
250
  return;
251
251
  }
252
252
 
253
+ let leafApproxRenderTime = 0;
254
+ for (const leaf of leavesByIds) {
255
+ leafApproxRenderTime += leaf.getContent()!.getProportionalRenderingTime();
256
+ }
257
+
253
258
  // Is it worth it to render the items?
254
- // TODO: Consider replacing this with something performace based.
255
- if (leavesByIds.length > this.cacheState.props.minComponentsPerCache) {
259
+ if (leafApproxRenderTime > this.cacheState.props.minProportionalRenderTimePerCache) {
256
260
  let fullRerenderNeeded = true;
257
261
  if (!this.cachedRenderer) {
258
262
  this.cachedRenderer = this.cacheState.recordManager.allocCanvas(
@@ -23,8 +23,8 @@ export const createCache = (onRenderAlloc?: RenderAllocCallback, cacheOptions?:
23
23
  blockResolution: Vec2.of(500, 500),
24
24
  cacheSize: 500 * 10 * 4,
25
25
  maxScale: 2,
26
- minComponentsPerCache: 0,
27
- minComponentsToUseCache: 0,
26
+ minProportionalRenderTimePerCache: 0,
27
+ minProportionalRenderTimeToUseCache: 0,
28
28
  ...cacheOptions
29
29
  });
30
30
 
@@ -20,11 +20,11 @@ export interface CacheProps {
20
20
  maxScale: number;
21
21
 
22
22
  // Minimum component count to cache, rather than just re-render each time.
23
- minComponentsPerCache: number;
23
+ minProportionalRenderTimePerCache: number;
24
24
 
25
25
  // Minimum number of strokes/etc. to use the cache to render, isntead of
26
26
  // rendering directly.
27
- minComponentsToUseCache: number;
27
+ minProportionalRenderTimeToUseCache: number;
28
28
  }
29
29
 
30
30
  export interface CacheState {
@@ -502,6 +502,19 @@ export default class IconProvider {
502
502
  M 10,25 10,90 70,90 70,60 40,60 40,25 10,25 z
503
503
  `);
504
504
  }
505
+
506
+ public makePasteIcon(): IconType {
507
+ const icon = this.makeIconFromPath(`
508
+ M 50 0 L 50 5 L 35 5 L 40 24.75 L 20 25 L 20 100 L 85 100 L 100 90 L 100 24 L 75.1 24.3 L 80 5 L 65 5 L 65 0 L 50 0 z
509
+ M 10 15 L 10 115 L 110 115 L 110 15 L 85 15 L 83 20 L 105 20 L 105 110 L 15 110 L 15 20 L 32 20 L 30 15 L 10 15 z
510
+ M 25 35 L 90 35 L 90 40 L 25 40 L 25 35 z
511
+ M 25 45 L 90 45 L 90 50 L 25 50 L 25 45 z
512
+ M 25 55 L 85 55 L 85 60 L 25 60 L 25 55 z
513
+ M 25 65 L 90 65 L 90 70 L 25 70 L 25 65 z
514
+ `);
515
+ icon.setAttribute('viewBox', '0 0 120 120');
516
+ return icon;
517
+ }
505
518
 
506
519
  public makeDeleteSelectionIcon(): IconType {
507
520
  const strokeWidth = '5px';
@@ -28,6 +28,7 @@ export interface ToolbarLocalization {
28
28
  zoom: string;
29
29
  resetView: string;
30
30
  selectionToolKeyboardShortcuts: string;
31
+ paste: string;
31
32
 
32
33
  dropdownShown: (toolName: string)=> string;
33
34
  dropdownHidden: (toolName: string)=> string;
@@ -66,6 +67,8 @@ export const defaultToolbarLocalization: ToolbarLocalization = {
66
67
  filledRectanglePen: 'Filled rectangle',
67
68
  lockRotation: 'Lock rotation',
68
69
 
70
+ paste: 'Paste',
71
+
69
72
  dropdownShown: (toolName) => `Dropdown for ${toolName} shown`,
70
73
  dropdownHidden: (toolName) => `Dropdown for ${toolName} hidden`,
71
74
  zoomLevel: (zoomPercent: number) => `Zoom: ${zoomPercent}%`,
@@ -30,6 +30,7 @@ type ScrollByCallback = (delta: Vec2) => void;
30
30
 
31
31
  class InertialScroller {
32
32
  private running: boolean = false;
33
+ private currentVelocity: Vec2;
33
34
 
34
35
  public constructor(
35
36
  private initialVelocity: Vec2,
@@ -44,22 +45,22 @@ class InertialScroller {
44
45
  return;
45
46
  }
46
47
 
47
- let currentVelocity = this.initialVelocity;
48
+ this.currentVelocity = this.initialVelocity;
48
49
  let lastTime = (new Date()).getTime();
49
50
  this.running = true;
50
51
 
51
- const maxSpeed = 8000; // units/s
52
+ const maxSpeed = 5000; // units/s
52
53
  const minSpeed = 200; // units/s
53
- if (currentVelocity.magnitude() > maxSpeed) {
54
- currentVelocity = currentVelocity.normalized().times(maxSpeed);
54
+ if (this.currentVelocity.magnitude() > maxSpeed) {
55
+ this.currentVelocity = this.currentVelocity.normalized().times(maxSpeed);
55
56
  }
56
57
 
57
- while (this.running && currentVelocity.magnitude() > minSpeed) {
58
+ while (this.running && this.currentVelocity.magnitude() > minSpeed) {
58
59
  const nowTime = (new Date()).getTime();
59
60
  const dt = (nowTime - lastTime) / 1000;
60
61
 
61
- currentVelocity = currentVelocity.times(Math.pow(1/8, dt));
62
- this.scrollBy(currentVelocity.times(dt));
62
+ this.currentVelocity = this.currentVelocity.times(Math.pow(1/8, dt));
63
+ this.scrollBy(this.currentVelocity.times(dt));
63
64
 
64
65
  await untilNextAnimationFrame();
65
66
  lastTime = nowTime;
@@ -70,6 +71,14 @@ class InertialScroller {
70
71
  }
71
72
  }
72
73
 
74
+ public getCurrentVelocity(): Vec2|null {
75
+ if (!this.running) {
76
+ return null;
77
+ }
78
+
79
+ return this.currentVelocity;
80
+ }
81
+
73
82
  public stop(): void {
74
83
  if (this.running) {
75
84
  this.running = false;
@@ -85,6 +94,7 @@ export default class PanZoom extends BaseTool {
85
94
  private lastDist: number;
86
95
  private lastScreenCenter: Point2;
87
96
  private lastTimestamp: number;
97
+ private lastPointerDownTimestamp: number = 0;
88
98
 
89
99
  private inertialScroller: InertialScroller|null = null;
90
100
  private velocity: Vec2|null = null;
@@ -108,10 +118,14 @@ export default class PanZoom extends BaseTool {
108
118
  return pointers.every(pointer => pointer.device === kind);
109
119
  }
110
120
 
111
- public onPointerDown({ allPointers: pointers }: PointerEvt): boolean {
121
+ public onPointerDown({ allPointers: pointers, current: currentPointer }: PointerEvt): boolean {
112
122
  let handlingGesture = false;
113
123
 
124
+ const inertialScrollerVelocity = this.inertialScroller?.getCurrentVelocity() ?? Vec2.zero;
114
125
  this.inertialScroller?.stop();
126
+ this.velocity = inertialScrollerVelocity;
127
+
128
+ this.lastPointerDownTimestamp = currentPointer.timeStamp;
115
129
 
116
130
  const allAreTouch = this.allPointersAreOfType(pointers, PointerDevice.Touch);
117
131
  const isRightClick = this.allPointersAreOfType(pointers, PointerDevice.RightButtonMouse);
@@ -142,17 +156,19 @@ export default class PanZoom extends BaseTool {
142
156
 
143
157
  private updateVelocity(currentCenter: Point2) {
144
158
  const deltaPos = currentCenter.minus(this.lastScreenCenter);
145
- const deltaTime = ((new Date()).getTime() - this.lastTimestamp) / 1000;
159
+ let deltaTime = ((new Date()).getTime() - this.lastTimestamp) / 1000;
146
160
 
161
+ // Ignore duplicate events, unless there has been enough time between them.
162
+ if (deltaPos.magnitude() === 0 && deltaTime < 0.1) {
163
+ return;
164
+ }
147
165
  // We divide by deltaTime. Don't divide by zero.
148
166
  if (deltaTime === 0) {
149
167
  return;
150
168
  }
151
169
 
152
- // Ignore duplicate events, unless there has been enough time between them.
153
- if (deltaPos.magnitude() === 0 && deltaTime < 0.1) {
154
- return;
155
- }
170
+ // Don't divide by almost zero, either
171
+ deltaTime = Math.max(deltaTime, 0.01);
156
172
 
157
173
  const currentVelocity = deltaPos.times(1 / deltaTime);
158
174
  let smoothedVelocity = currentVelocity;
@@ -233,8 +249,12 @@ export default class PanZoom extends BaseTool {
233
249
  this.velocity = Vec2.zero;
234
250
  };
235
251
 
252
+ const minInertialScrollDt = 30;
236
253
  const shouldInertialScroll =
237
- event.current.device === PointerDevice.Touch && event.allPointers.length === 1;
254
+ event.current.device === PointerDevice.Touch
255
+ && event.allPointers.length === 1
256
+ && this.velocity !== null
257
+ && event.current.timeStamp - this.lastPointerDownTimestamp > minInertialScrollDt;
238
258
 
239
259
  if (shouldInertialScroll && this.velocity !== null) {
240
260
  // If the user drags the screen, then stops, then lifts the pointer,