@tsdraw/core 0.8.2 → 0.8.4
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/dist/index.cjs +120 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -24
- package/dist/index.d.ts +50 -24
- package/dist/index.js +118 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -225,34 +225,25 @@ var StateNode = class {
|
|
|
225
225
|
|
|
226
226
|
// src/canvas/viewport.ts
|
|
227
227
|
function createViewport() {
|
|
228
|
-
return { x: 0, y: 0, zoom: 1
|
|
228
|
+
return { x: 0, y: 0, zoom: 1 };
|
|
229
229
|
}
|
|
230
230
|
function screenToPage(viewport, screenX, screenY) {
|
|
231
|
-
const tx = screenX - viewport.x;
|
|
232
|
-
const ty = screenY - viewport.y;
|
|
233
|
-
const cos = Math.cos(viewport.rotation);
|
|
234
|
-
const sin = Math.sin(viewport.rotation);
|
|
235
231
|
return {
|
|
236
|
-
x: (
|
|
237
|
-
y: (-
|
|
232
|
+
x: (screenX - viewport.x) / viewport.zoom,
|
|
233
|
+
y: (screenY - viewport.y) / viewport.zoom
|
|
238
234
|
};
|
|
239
235
|
}
|
|
240
236
|
function pageToScreen(viewport, pageX, pageY) {
|
|
241
|
-
const scaledX = pageX * viewport.zoom;
|
|
242
|
-
const scaledY = pageY * viewport.zoom;
|
|
243
|
-
const cos = Math.cos(viewport.rotation);
|
|
244
|
-
const sin = Math.sin(viewport.rotation);
|
|
245
237
|
return {
|
|
246
|
-
x:
|
|
247
|
-
y:
|
|
238
|
+
x: pageX * viewport.zoom + viewport.x,
|
|
239
|
+
y: pageY * viewport.zoom + viewport.y
|
|
248
240
|
};
|
|
249
241
|
}
|
|
250
242
|
function setViewport(viewport, updater) {
|
|
251
243
|
return {
|
|
252
244
|
x: updater.x ?? viewport.x,
|
|
253
245
|
y: updater.y ?? viewport.y,
|
|
254
|
-
zoom: updater.zoom ?? viewport.zoom
|
|
255
|
-
rotation: updater.rotation ?? viewport.rotation
|
|
246
|
+
zoom: updater.zoom ?? viewport.zoom
|
|
256
247
|
};
|
|
257
248
|
}
|
|
258
249
|
function panViewport(viewport, dx, dy) {
|
|
@@ -264,23 +255,92 @@ function zoomViewport(viewport, factor, centerX, centerY) {
|
|
|
264
255
|
return { ...viewport, zoom };
|
|
265
256
|
}
|
|
266
257
|
const pageBefore = screenToPage(viewport, centerX, centerY);
|
|
267
|
-
const
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
258
|
+
const x = centerX - pageBefore.x * zoom;
|
|
259
|
+
const y = centerY - pageBefore.y * zoom;
|
|
260
|
+
return { x, y, zoom };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// src/canvas/cameraPan.ts
|
|
264
|
+
var VELOCITY_LERP_FACTOR = 0.5;
|
|
265
|
+
var VELOCITY_ZERO_THRESHOLD = 0.01;
|
|
266
|
+
function beginCameraPan(viewport, screenX, screenY) {
|
|
267
|
+
return {
|
|
268
|
+
initialViewportX: viewport.x,
|
|
269
|
+
initialViewportY: viewport.y,
|
|
270
|
+
originScreenX: screenX,
|
|
271
|
+
originScreenY: screenY,
|
|
272
|
+
velocityX: 0,
|
|
273
|
+
velocityY: 0,
|
|
274
|
+
previousScreenX: screenX,
|
|
275
|
+
previousScreenY: screenY,
|
|
276
|
+
lastMoveTime: performance.now()
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
function moveCameraPan(session, currentScreenX, currentScreenY) {
|
|
280
|
+
const now = performance.now();
|
|
281
|
+
const elapsed = now - session.lastMoveTime;
|
|
282
|
+
if (elapsed > 0) {
|
|
283
|
+
const moveDx = currentScreenX - session.previousScreenX;
|
|
284
|
+
const moveDy = currentScreenY - session.previousScreenY;
|
|
285
|
+
const moveLen = Math.hypot(moveDx, moveDy);
|
|
286
|
+
if (moveLen > 0) {
|
|
287
|
+
const dirX = moveDx / moveLen;
|
|
288
|
+
const dirY = moveDy / moveLen;
|
|
289
|
+
const speed = moveLen / elapsed;
|
|
290
|
+
session.velocityX += (dirX * speed - session.velocityX) * VELOCITY_LERP_FACTOR;
|
|
291
|
+
session.velocityY += (dirY * speed - session.velocityY) * VELOCITY_LERP_FACTOR;
|
|
292
|
+
}
|
|
293
|
+
if (Math.abs(session.velocityX) < VELOCITY_ZERO_THRESHOLD) session.velocityX = 0;
|
|
294
|
+
if (Math.abs(session.velocityY) < VELOCITY_ZERO_THRESHOLD) session.velocityY = 0;
|
|
277
295
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
296
|
+
session.previousScreenX = currentScreenX;
|
|
297
|
+
session.previousScreenY = currentScreenY;
|
|
298
|
+
session.lastMoveTime = now;
|
|
299
|
+
return {
|
|
300
|
+
x: session.initialViewportX + (currentScreenX - session.originScreenX),
|
|
301
|
+
y: session.initialViewportY + (currentScreenY - session.originScreenY)
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
var SLIDE_FRICTION = 0.92;
|
|
305
|
+
var SLIDE_MIN_SPEED = 0.01;
|
|
306
|
+
var SLIDE_MAX_SPEED = 2;
|
|
307
|
+
var SLIDE_MIN_VELOCITY_TO_START = 0.1;
|
|
308
|
+
function startCameraSlide(session, applyPan, onFrame) {
|
|
309
|
+
const timeSinceLastMove = performance.now() - session.lastMoveTime;
|
|
310
|
+
const FRAME_DURATION = 16;
|
|
311
|
+
const decayFactor = Math.pow(1 - VELOCITY_LERP_FACTOR, timeSinceLastMove / FRAME_DURATION);
|
|
312
|
+
const effectiveVx = session.velocityX * decayFactor;
|
|
313
|
+
const effectiveVy = session.velocityY * decayFactor;
|
|
314
|
+
const speed = Math.hypot(effectiveVx, effectiveVy);
|
|
315
|
+
const clampedSpeed = Math.min(speed, SLIDE_MAX_SPEED);
|
|
316
|
+
if (clampedSpeed < SLIDE_MIN_VELOCITY_TO_START) return null;
|
|
317
|
+
const dirX = effectiveVx / speed;
|
|
318
|
+
const dirY = effectiveVy / speed;
|
|
319
|
+
let currentSpeed = clampedSpeed;
|
|
320
|
+
let lastTime = performance.now();
|
|
321
|
+
let rafId = 0;
|
|
322
|
+
const tick = () => {
|
|
323
|
+
const now = performance.now();
|
|
324
|
+
const elapsed = now - lastTime;
|
|
325
|
+
lastTime = now;
|
|
326
|
+
applyPan(dirX * currentSpeed * elapsed, dirY * currentSpeed * elapsed);
|
|
327
|
+
onFrame();
|
|
328
|
+
currentSpeed *= SLIDE_FRICTION;
|
|
329
|
+
if (currentSpeed < SLIDE_MIN_SPEED) {
|
|
330
|
+
rafId = 0;
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
rafId = requestAnimationFrame(tick);
|
|
334
|
+
};
|
|
335
|
+
rafId = requestAnimationFrame(tick);
|
|
336
|
+
return {
|
|
337
|
+
stop() {
|
|
338
|
+
if (rafId !== 0) {
|
|
339
|
+
cancelAnimationFrame(rafId);
|
|
340
|
+
rafId = 0;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
284
344
|
}
|
|
285
345
|
|
|
286
346
|
// src/utils/colors.ts
|
|
@@ -313,7 +373,6 @@ var CanvasRenderer = class {
|
|
|
313
373
|
render(ctx, viewport, shapes) {
|
|
314
374
|
ctx.save();
|
|
315
375
|
ctx.translate(viewport.x, viewport.y);
|
|
316
|
-
ctx.rotate(viewport.rotation);
|
|
317
376
|
ctx.scale(viewport.zoom, viewport.zoom);
|
|
318
377
|
for (const shape of shapes) {
|
|
319
378
|
if (shape.type === "draw") {
|
|
@@ -806,7 +865,7 @@ var PenDrawingState = class extends StateNode {
|
|
|
806
865
|
const z = this._startInfo?.point?.z ?? 0.5;
|
|
807
866
|
this._isPenDevice = penActive;
|
|
808
867
|
this._hasPressure = penActive || z !== 0.5;
|
|
809
|
-
const pressure = this._hasPressure ? toFixed(z) : 0.5;
|
|
868
|
+
const pressure = this._hasPressure ? toFixed(z * 1.25) : 0.5;
|
|
810
869
|
this._phase = inputs.getShiftKey() ? "straight" : "free";
|
|
811
870
|
this._extending = false;
|
|
812
871
|
this._lastSample = { ...origin };
|
|
@@ -903,13 +962,12 @@ var PenDrawingState = class extends StateNode {
|
|
|
903
962
|
const curPt = inputs.getCurrentPagePoint();
|
|
904
963
|
if (!this._hasPressure) {
|
|
905
964
|
const liveZ = curPt.z ?? 0.5;
|
|
906
|
-
if (liveZ !== 0.5 || inputs.getIsPen()) {
|
|
965
|
+
if (liveZ > 0 && liveZ !== 0.5 || inputs.getIsPen()) {
|
|
907
966
|
this._hasPressure = true;
|
|
908
|
-
this.editor.updateShapes([{ id, type: "draw", props: { isPen: true } }]);
|
|
909
967
|
}
|
|
910
968
|
}
|
|
911
969
|
const local = this.editor.getPointInShapeSpace(shape, curPt);
|
|
912
|
-
const pressure = this._hasPressure ? toFixed(curPt.z ?? 0.5) : 0.5;
|
|
970
|
+
const pressure = this._hasPressure ? toFixed((curPt.z ?? 0.5) * 1.25) : 0.5;
|
|
913
971
|
const pt = { x: toFixed(local.x), y: toFixed(local.y), z: pressure };
|
|
914
972
|
switch (this._phase) {
|
|
915
973
|
case "starting_straight": {
|
|
@@ -1055,7 +1113,7 @@ var PenDrawingState = class extends StateNode {
|
|
|
1055
1113
|
const firstPt = {
|
|
1056
1114
|
x: 0,
|
|
1057
1115
|
y: 0,
|
|
1058
|
-
z: this._hasPressure ? toFixed(curPage.z ?? 0.5) : 0.5
|
|
1116
|
+
z: this._hasPressure ? toFixed((curPage.z ?? 0.5) * 1.25) : 0.5
|
|
1059
1117
|
};
|
|
1060
1118
|
this._activePts = [firstPt];
|
|
1061
1119
|
this.editor.createShape({
|
|
@@ -1087,7 +1145,7 @@ var PenDrawingState = class extends StateNode {
|
|
|
1087
1145
|
endStroke() {
|
|
1088
1146
|
if (!this._target) return;
|
|
1089
1147
|
this.editor.updateShapes([
|
|
1090
|
-
{ id: this._target.id, type: "draw", props: { isComplete: true } }
|
|
1148
|
+
{ id: this._target.id, type: "draw", props: { isComplete: true, isPen: this._hasPressure } }
|
|
1091
1149
|
]);
|
|
1092
1150
|
this.ctx.transition("pen_idle");
|
|
1093
1151
|
}
|
|
@@ -1517,16 +1575,29 @@ var HandIdleState = class extends StateNode {
|
|
|
1517
1575
|
// src/tools/hand/states/HandDraggingState.ts
|
|
1518
1576
|
var HandDraggingState = class extends StateNode {
|
|
1519
1577
|
static id = "hand_dragging";
|
|
1578
|
+
panSession = null;
|
|
1579
|
+
onEnter(info) {
|
|
1580
|
+
const downInfo = info;
|
|
1581
|
+
const screenX = downInfo?.screenX ?? 0;
|
|
1582
|
+
const screenY = downInfo?.screenY ?? 0;
|
|
1583
|
+
this.panSession = beginCameraPan(this.editor.viewport, screenX, screenY);
|
|
1584
|
+
}
|
|
1520
1585
|
onPointerMove(info) {
|
|
1521
|
-
|
|
1522
|
-
const
|
|
1523
|
-
const
|
|
1524
|
-
|
|
1525
|
-
this.editor.
|
|
1586
|
+
if (!this.panSession) return;
|
|
1587
|
+
const screenX = info?.screenX ?? 0;
|
|
1588
|
+
const screenY = info?.screenY ?? 0;
|
|
1589
|
+
const target = moveCameraPan(this.panSession, screenX, screenY);
|
|
1590
|
+
this.editor.setViewport({ x: target.x, y: target.y });
|
|
1591
|
+
}
|
|
1592
|
+
getPanSession() {
|
|
1593
|
+
return this.panSession;
|
|
1526
1594
|
}
|
|
1527
1595
|
onPointerUp() {
|
|
1528
1596
|
this.ctx.transition("hand_idle");
|
|
1529
1597
|
}
|
|
1598
|
+
onExit() {
|
|
1599
|
+
this.panSession = null;
|
|
1600
|
+
}
|
|
1530
1601
|
onCancel() {
|
|
1531
1602
|
this.ctx.transition("hand_idle");
|
|
1532
1603
|
}
|
|
@@ -1754,8 +1825,7 @@ var Editor = class {
|
|
|
1754
1825
|
this.viewport = {
|
|
1755
1826
|
x: partial.x ?? this.viewport.x,
|
|
1756
1827
|
y: partial.y ?? this.viewport.y,
|
|
1757
|
-
zoom: Math.max(0.1, Math.min(4, rawZoom))
|
|
1758
|
-
rotation: partial.rotation ?? this.viewport.rotation
|
|
1828
|
+
zoom: Math.max(0.1, Math.min(4, rawZoom))
|
|
1759
1829
|
};
|
|
1760
1830
|
this.emitChange();
|
|
1761
1831
|
}
|
|
@@ -1769,10 +1839,6 @@ var Editor = class {
|
|
|
1769
1839
|
this.viewport = zoomViewport(this.viewport, factor, screenX, screenY);
|
|
1770
1840
|
this.emitChange();
|
|
1771
1841
|
}
|
|
1772
|
-
rotateAt(delta, screenX, screenY) {
|
|
1773
|
-
this.viewport = rotateViewport(this.viewport, delta, screenX, screenY);
|
|
1774
|
-
this.emitChange();
|
|
1775
|
-
}
|
|
1776
1842
|
deleteShapes(ids) {
|
|
1777
1843
|
if (ids.length === 0) return;
|
|
1778
1844
|
this.store.deleteShapes(ids);
|
|
@@ -1795,8 +1861,7 @@ var Editor = class {
|
|
|
1795
1861
|
viewport: {
|
|
1796
1862
|
x: this.viewport.x,
|
|
1797
1863
|
y: this.viewport.y,
|
|
1798
|
-
zoom: this.viewport.zoom
|
|
1799
|
-
rotation: this.viewport.rotation
|
|
1864
|
+
zoom: this.viewport.zoom
|
|
1800
1865
|
},
|
|
1801
1866
|
currentToolId: this.getCurrentToolId(),
|
|
1802
1867
|
drawStyle: this.getCurrentDrawStyle(),
|
|
@@ -1804,10 +1869,7 @@ var Editor = class {
|
|
|
1804
1869
|
};
|
|
1805
1870
|
}
|
|
1806
1871
|
loadSessionStateSnapshot(snapshot) {
|
|
1807
|
-
this.setViewport(
|
|
1808
|
-
...snapshot.viewport,
|
|
1809
|
-
rotation: snapshot.viewport.rotation ?? 0
|
|
1810
|
-
});
|
|
1872
|
+
this.setViewport(snapshot.viewport);
|
|
1811
1873
|
this.setCurrentDrawStyle({
|
|
1812
1874
|
color: snapshot.drawStyle.color,
|
|
1813
1875
|
dash: snapshot.drawStyle.dash,
|
|
@@ -2220,6 +2282,7 @@ exports.ToolManager = ToolManager;
|
|
|
2220
2282
|
exports.applyMove = applyMove;
|
|
2221
2283
|
exports.applyResize = applyResize;
|
|
2222
2284
|
exports.applyRotation = applyRotation;
|
|
2285
|
+
exports.beginCameraPan = beginCameraPan;
|
|
2223
2286
|
exports.boundsContainPoint = boundsContainPoint;
|
|
2224
2287
|
exports.boundsIntersect = boundsIntersect;
|
|
2225
2288
|
exports.boundsOf = boundsOf;
|
|
@@ -2240,6 +2303,7 @@ exports.getShapesInBounds = getShapesInBounds;
|
|
|
2240
2303
|
exports.getTopShapeAtPoint = getTopShapeAtPoint;
|
|
2241
2304
|
exports.isSelectTool = isSelectTool;
|
|
2242
2305
|
exports.minDistanceToPolyline = minDistanceToPolyline;
|
|
2306
|
+
exports.moveCameraPan = moveCameraPan;
|
|
2243
2307
|
exports.normalizeSelectionBounds = normalizeSelectionBounds;
|
|
2244
2308
|
exports.padBounds = padBounds;
|
|
2245
2309
|
exports.pageToScreen = pageToScreen;
|
|
@@ -2248,13 +2312,13 @@ exports.pointHitsShape = pointHitsShape;
|
|
|
2248
2312
|
exports.recordsToDocumentSnapshot = recordsToDocumentSnapshot;
|
|
2249
2313
|
exports.resolveThemeColor = resolveThemeColor;
|
|
2250
2314
|
exports.rotatePoint = rotatePoint;
|
|
2251
|
-
exports.rotateViewport = rotateViewport;
|
|
2252
2315
|
exports.screenToPage = screenToPage;
|
|
2253
2316
|
exports.segmentHitsShape = segmentHitsShape;
|
|
2254
2317
|
exports.segmentTouchesPolyline = segmentTouchesPolyline;
|
|
2255
2318
|
exports.setViewport = setViewport;
|
|
2256
2319
|
exports.shapePagePoints = shapePagePoints;
|
|
2257
2320
|
exports.sqDistance = sqDistance;
|
|
2321
|
+
exports.startCameraSlide = startCameraSlide;
|
|
2258
2322
|
exports.zoomViewport = zoomViewport;
|
|
2259
2323
|
//# sourceMappingURL=index.cjs.map
|
|
2260
2324
|
//# sourceMappingURL=index.cjs.map
|