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.
- package/CHANGELOG.md +6 -0
- package/dist/bundle.js +1 -1
- package/dist/src/components/AbstractComponent.d.ts +1 -0
- package/dist/src/components/AbstractComponent.js +6 -0
- package/dist/src/components/ImageComponent.d.ts +1 -0
- package/dist/src/components/ImageComponent.js +4 -0
- package/dist/src/components/Stroke.d.ts +2 -0
- package/dist/src/components/Stroke.js +5 -0
- package/dist/src/components/TextComponent.d.ts +1 -0
- package/dist/src/components/TextComponent.js +3 -0
- package/dist/src/components/util/StrokeSmoother.js +9 -5
- package/dist/src/math/Mat33.d.ts +2 -0
- package/dist/src/math/Mat33.js +7 -0
- package/dist/src/math/Path.js +3 -0
- package/dist/src/rendering/Display.js +5 -2
- package/dist/src/rendering/caching/RenderingCache.js +5 -1
- package/dist/src/rendering/caching/RenderingCacheNode.js +5 -2
- package/dist/src/rendering/caching/testUtils.js +1 -1
- package/dist/src/rendering/caching/types.d.ts +2 -2
- package/dist/src/toolbar/IconProvider.d.ts +1 -0
- package/dist/src/toolbar/IconProvider.js +12 -0
- package/dist/src/toolbar/localization.d.ts +1 -0
- package/dist/src/toolbar/localization.js +1 -0
- package/dist/src/tools/PanZoom.d.ts +2 -1
- package/dist/src/tools/PanZoom.js +33 -17
- package/package.json +11 -11
- package/src/components/AbstractComponent.ts +8 -0
- package/src/components/ImageComponent.ts +5 -0
- package/src/components/Stroke.ts +11 -0
- package/src/components/TextComponent.ts +4 -0
- package/src/components/util/StrokeSmoother.ts +12 -9
- package/src/math/Mat33.ts +9 -0
- package/src/math/Path.ts +4 -0
- package/src/rendering/Display.ts +7 -2
- package/src/rendering/caching/RenderingCache.ts +10 -2
- package/src/rendering/caching/RenderingCacheNode.ts +6 -2
- package/src/rendering/caching/testUtils.ts +2 -2
- package/src/rendering/caching/types.ts +2 -2
- package/src/toolbar/IconProvider.ts +13 -0
- package/src/toolbar/localization.ts +3 -0
- 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(
|
52
|
-
|
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
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
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
|
-
|
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}%`,
|
package/src/tools/PanZoom.ts
CHANGED
@@ -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
|
-
|
48
|
+
this.currentVelocity = this.initialVelocity;
|
48
49
|
let lastTime = (new Date()).getTime();
|
49
50
|
this.running = true;
|
50
51
|
|
51
|
-
const maxSpeed =
|
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
|
-
|
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
|
-
//
|
153
|
-
|
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
|
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,
|