@fieldnotes/core 0.41.0 → 0.42.0

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 CHANGED
@@ -1554,6 +1554,81 @@ var KeyboardHandler = class {
1554
1554
  }
1555
1555
  };
1556
1556
 
1557
+ // src/canvas/pan-inertia.ts
1558
+ var VELOCITY_WINDOW_MS = 60;
1559
+ var FRICTION = 0.92;
1560
+ var MIN_START_SPEED = 2;
1561
+ var MIN_STOP_SPEED = 0.3;
1562
+ var PanInertia = class {
1563
+ constructor(deps) {
1564
+ this.deps = deps;
1565
+ }
1566
+ samples = [];
1567
+ vx = 0;
1568
+ vy = 0;
1569
+ rafId = null;
1570
+ sample(dx, dy) {
1571
+ const t = this.deps.now();
1572
+ this.samples.push({ dx, dy, t });
1573
+ this.prune(t);
1574
+ }
1575
+ release() {
1576
+ if (!this.deps.enabled()) {
1577
+ this.reset();
1578
+ return;
1579
+ }
1580
+ const t = this.deps.now();
1581
+ this.prune(t);
1582
+ const recent = this.samples;
1583
+ if (recent.length === 0) {
1584
+ this.reset();
1585
+ return;
1586
+ }
1587
+ let sx = 0;
1588
+ let sy = 0;
1589
+ for (const s of recent) {
1590
+ sx += s.dx;
1591
+ sy += s.dy;
1592
+ }
1593
+ this.vx = sx / recent.length;
1594
+ this.vy = sy / recent.length;
1595
+ this.samples = [];
1596
+ if (Math.hypot(this.vx, this.vy) < MIN_START_SPEED) {
1597
+ this.vx = 0;
1598
+ this.vy = 0;
1599
+ return;
1600
+ }
1601
+ this.rafId = this.deps.requestFrame(this.step);
1602
+ }
1603
+ cancel() {
1604
+ this.reset();
1605
+ }
1606
+ step = () => {
1607
+ if (this.rafId === null) return;
1608
+ this.deps.pan(this.vx, this.vy);
1609
+ this.vx *= FRICTION;
1610
+ this.vy *= FRICTION;
1611
+ if (Math.hypot(this.vx, this.vy) >= MIN_STOP_SPEED) {
1612
+ this.rafId = this.deps.requestFrame(this.step);
1613
+ } else {
1614
+ this.reset();
1615
+ }
1616
+ };
1617
+ prune(now) {
1618
+ const cutoff = now - VELOCITY_WINDOW_MS;
1619
+ this.samples = this.samples.filter((s) => s.t >= cutoff);
1620
+ }
1621
+ reset() {
1622
+ if (this.rafId !== null) {
1623
+ this.deps.cancelFrame(this.rafId);
1624
+ this.rafId = null;
1625
+ }
1626
+ this.samples = [];
1627
+ this.vx = 0;
1628
+ this.vy = 0;
1629
+ }
1630
+ };
1631
+
1557
1632
  // src/canvas/input-handler.ts
1558
1633
  var ZOOM_SENSITIVITY = 1e-3;
1559
1634
  var MIDDLE_BUTTON = 1;
@@ -1579,6 +1654,16 @@ var InputHandler = class {
1579
1654
  getLastPointerWorld: () => this.lastPointerWorld()
1580
1655
  });
1581
1656
  this.openContextMenu = options.openContextMenu;
1657
+ this.panInertiaEnabled = options.panInertia ?? true;
1658
+ this.panInertia = new PanInertia({
1659
+ pan: (dx, dy) => this.camera.pan(dx, dy),
1660
+ now: () => typeof performance !== "undefined" ? performance.now() : Date.now(),
1661
+ requestFrame: (cb) => typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame(cb) : 0,
1662
+ cancelFrame: (id) => {
1663
+ if (typeof cancelAnimationFrame !== "undefined") cancelAnimationFrame(id);
1664
+ },
1665
+ enabled: () => this.panInertiaEnabled
1666
+ });
1582
1667
  this.scope = options.shortcuts?.scope ?? "focus";
1583
1668
  this.keyboard = new KeyboardHandler({
1584
1669
  element: this.element,
@@ -1628,6 +1713,8 @@ var InputHandler = class {
1628
1713
  keyboard;
1629
1714
  scope;
1630
1715
  openContextMenu;
1716
+ panInertia;
1717
+ panInertiaEnabled;
1631
1718
  setToolManager(toolManager, toolContext) {
1632
1719
  this.toolManager = toolManager;
1633
1720
  this.toolContext = toolContext;
@@ -1639,6 +1726,7 @@ var InputHandler = class {
1639
1726
  return this.keyboard.shortcuts;
1640
1727
  }
1641
1728
  destroy() {
1729
+ this.panInertia.cancel();
1642
1730
  this.actions.dispose();
1643
1731
  this.abortController.abort();
1644
1732
  this.inputFilter.reset();
@@ -1662,6 +1750,7 @@ var InputHandler = class {
1662
1750
  }
1663
1751
  onWheel = (e) => {
1664
1752
  e.preventDefault();
1753
+ this.panInertia.cancel();
1665
1754
  const rect = this.element.getBoundingClientRect();
1666
1755
  const zoomFactor = 1 - e.deltaY * ZOOM_SENSITIVITY;
1667
1756
  const newZoom = this.camera.zoom * zoomFactor;
@@ -1671,6 +1760,7 @@ var InputHandler = class {
1671
1760
  });
1672
1761
  };
1673
1762
  onPointerDown = (e) => {
1763
+ this.panInertia.cancel();
1674
1764
  this.focusSelf();
1675
1765
  this.activePointers.set(e.pointerId, { x: e.clientX, y: e.clientY });
1676
1766
  this.element.setPointerCapture?.(e.pointerId);
@@ -1711,6 +1801,7 @@ var InputHandler = class {
1711
1801
  const dy = e.clientY - this.lastPointer.y;
1712
1802
  this.lastPointer = { x: e.clientX, y: e.clientY };
1713
1803
  this.camera.pan(dx, dy);
1804
+ this.panInertia.sample(dx, dy);
1714
1805
  return;
1715
1806
  }
1716
1807
  if (e.pointerType === "pen" && !this.activePointers.has(e.pointerId)) {
@@ -1741,6 +1832,7 @@ var InputHandler = class {
1741
1832
  }
1742
1833
  if (this.isPanning && this.activePointers.size === 0) {
1743
1834
  this.isPanning = false;
1835
+ this.panInertia.release();
1744
1836
  }
1745
1837
  const upResult = this.inputFilter.filterUp(e);
1746
1838
  if (this.isToolActive) {
@@ -1761,6 +1853,7 @@ var InputHandler = class {
1761
1853
  return this.actions.hasClipboard();
1762
1854
  }
1763
1855
  startPinch() {
1856
+ this.panInertia.cancel();
1764
1857
  this.cancelLongPress();
1765
1858
  this.inputFilter.reset();
1766
1859
  this.deferredDown = null;
@@ -1782,6 +1875,7 @@ var InputHandler = class {
1782
1875
  const dx = center2.x - this.lastPointer.x;
1783
1876
  const dy = center2.y - this.lastPointer.y;
1784
1877
  this.camera.pan(dx, dy);
1878
+ this.panInertia.sample(dx, dy);
1785
1879
  this.lastPinchDistance = dist;
1786
1880
  this.lastPinchCenter = center2;
1787
1881
  this.lastPointer = { ...center2 };
@@ -6883,7 +6977,8 @@ var Viewport = class {
6883
6977
  shortcuts: options.shortcuts,
6884
6978
  addImage: (src, world) => this.addImage(src, world),
6885
6979
  getCenteredWorld: () => this.centeredPosition({ w: 300, h: 200 }),
6886
- onPaste: options.onPaste
6980
+ onPaste: options.onPaste,
6981
+ panInertia: options.panInertia
6887
6982
  });
6888
6983
  if (options.contextMenu !== false) {
6889
6984
  this.contextMenu = new ContextMenu({
@@ -9798,7 +9893,7 @@ var LaserTool = class {
9798
9893
  };
9799
9894
 
9800
9895
  // src/index.ts
9801
- var VERSION = "0.41.0";
9896
+ var VERSION = "0.42.0";
9802
9897
  // Annotate the CommonJS export names for ESM import in node:
9803
9898
  0 && (module.exports = {
9804
9899
  ArrowTool,