hyperprop-charting-library 0.1.42 → 0.1.44
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/hyperprop-charting-library.cjs +98 -1
- package/dist/hyperprop-charting-library.js +98 -1
- package/dist/index.cjs +98 -1
- package/dist/index.js +98 -1
- package/package.json +1 -1
|
@@ -2536,8 +2536,75 @@ function createChart(element, options = {}) {
|
|
|
2536
2536
|
let lastPointerX = 0;
|
|
2537
2537
|
let lastPointerY = 0;
|
|
2538
2538
|
let activePointerId = null;
|
|
2539
|
+
const touchPointers = /* @__PURE__ */ new Map();
|
|
2540
|
+
let pinchZoomState = null;
|
|
2541
|
+
const getTouchPair = () => {
|
|
2542
|
+
const points = Array.from(touchPointers.values());
|
|
2543
|
+
const first = points[0];
|
|
2544
|
+
const second = points[1];
|
|
2545
|
+
return first && second ? [first, second] : null;
|
|
2546
|
+
};
|
|
2547
|
+
const getPointerDistance = (first, second) => {
|
|
2548
|
+
return Math.hypot(second.x - first.x, second.y - first.y);
|
|
2549
|
+
};
|
|
2550
|
+
const getMidpoint = (first, second) => {
|
|
2551
|
+
return {
|
|
2552
|
+
x: (first.x + second.x) / 2,
|
|
2553
|
+
y: (first.y + second.y) / 2
|
|
2554
|
+
};
|
|
2555
|
+
};
|
|
2556
|
+
const beginPinchZoom = (first, second) => {
|
|
2557
|
+
if (!drawState || data.length === 0) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
const midpoint = getMidpoint(first, second);
|
|
2561
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2562
|
+
pinchZoomState = {
|
|
2563
|
+
startDistance: Math.max(1, getPointerDistance(first, second)),
|
|
2564
|
+
startSpan: xSpan,
|
|
2565
|
+
anchorIndex: drawState.xStart + anchorRatio * xSpan
|
|
2566
|
+
};
|
|
2567
|
+
isDragging = false;
|
|
2568
|
+
dragMode = null;
|
|
2569
|
+
activePointerId = null;
|
|
2570
|
+
pointerDownInfo = null;
|
|
2571
|
+
orderDragState = null;
|
|
2572
|
+
actionDragState = null;
|
|
2573
|
+
canvas.style.cursor = "default";
|
|
2574
|
+
setCrosshairPoint(null);
|
|
2575
|
+
};
|
|
2576
|
+
const applyPinchZoom = (first, second) => {
|
|
2577
|
+
if (!drawState || !pinchZoomState || data.length === 0) {
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
const distance = getPointerDistance(first, second);
|
|
2581
|
+
if (distance <= 0) {
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
const minSpan = minVisibleBars;
|
|
2585
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2586
|
+
const nextSpan = clamp(pinchZoomState.startSpan * (pinchZoomState.startDistance / distance), minSpan, maxSpan);
|
|
2587
|
+
const midpoint = getMidpoint(first, second);
|
|
2588
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2589
|
+
const nextStart = pinchZoomState.anchorIndex - anchorRatio * nextSpan;
|
|
2590
|
+
xSpan = nextSpan;
|
|
2591
|
+
xCenter = nextStart + nextSpan / 2;
|
|
2592
|
+
clampXViewport();
|
|
2593
|
+
updateFollowLatest(false);
|
|
2594
|
+
emitViewportChange();
|
|
2595
|
+
draw();
|
|
2596
|
+
};
|
|
2539
2597
|
const onPointerDown = (event) => {
|
|
2540
2598
|
const point = getCanvasPoint(event);
|
|
2599
|
+
if (event.pointerType === "touch") {
|
|
2600
|
+
touchPointers.set(event.pointerId, point);
|
|
2601
|
+
canvas.setPointerCapture(event.pointerId);
|
|
2602
|
+
const touchPair = getTouchPair();
|
|
2603
|
+
if (touchPair) {
|
|
2604
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2541
2608
|
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
2542
2609
|
if (crosshairButtonRegion) {
|
|
2543
2610
|
crosshairPriceActionHandler?.({
|
|
@@ -2599,6 +2666,20 @@ function createChart(element, options = {}) {
|
|
|
2599
2666
|
};
|
|
2600
2667
|
const onPointerMove = (event) => {
|
|
2601
2668
|
const point = getCanvasPoint(event);
|
|
2669
|
+
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
2670
|
+
touchPointers.set(event.pointerId, point);
|
|
2671
|
+
const touchPair = getTouchPair();
|
|
2672
|
+
if (touchPair) {
|
|
2673
|
+
if (!pinchZoomState) {
|
|
2674
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2675
|
+
}
|
|
2676
|
+
applyPinchZoom(touchPair[0], touchPair[1]);
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
if (pinchZoomState) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2602
2683
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
2603
2684
|
const dx = point.x - pointerDownInfo.x;
|
|
2604
2685
|
const dy = point.y - pointerDownInfo.y;
|
|
@@ -2703,7 +2784,7 @@ function createChart(element, options = {}) {
|
|
|
2703
2784
|
setCrosshairPoint(null);
|
|
2704
2785
|
} else if (dragMode === "x-axis") {
|
|
2705
2786
|
canvas.style.cursor = "ew-resize";
|
|
2706
|
-
zoomXToLatest(Math.exp(
|
|
2787
|
+
zoomXToLatest(Math.exp(deltaX * 6e-3));
|
|
2707
2788
|
setCrosshairPoint(null);
|
|
2708
2789
|
} else if (dragMode === "y-axis") {
|
|
2709
2790
|
canvas.style.cursor = "ns-resize";
|
|
@@ -2715,6 +2796,22 @@ function createChart(element, options = {}) {
|
|
|
2715
2796
|
lastPointerY = point.y;
|
|
2716
2797
|
};
|
|
2717
2798
|
const endPointerDrag = (event) => {
|
|
2799
|
+
if (event?.pointerType === "touch") {
|
|
2800
|
+
touchPointers.delete(event.pointerId);
|
|
2801
|
+
if (pinchZoomState) {
|
|
2802
|
+
pinchZoomState = null;
|
|
2803
|
+
isDragging = false;
|
|
2804
|
+
dragMode = null;
|
|
2805
|
+
activePointerId = null;
|
|
2806
|
+
pointerDownInfo = null;
|
|
2807
|
+
setCrosshairPoint(null);
|
|
2808
|
+
canvas.style.cursor = "default";
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
} else if (!event) {
|
|
2812
|
+
touchPointers.clear();
|
|
2813
|
+
pinchZoomState = null;
|
|
2814
|
+
}
|
|
2718
2815
|
if (event && activePointerId !== null && event.pointerId !== activePointerId) {
|
|
2719
2816
|
return;
|
|
2720
2817
|
}
|
|
@@ -2512,8 +2512,75 @@ function createChart(element, options = {}) {
|
|
|
2512
2512
|
let lastPointerX = 0;
|
|
2513
2513
|
let lastPointerY = 0;
|
|
2514
2514
|
let activePointerId = null;
|
|
2515
|
+
const touchPointers = /* @__PURE__ */ new Map();
|
|
2516
|
+
let pinchZoomState = null;
|
|
2517
|
+
const getTouchPair = () => {
|
|
2518
|
+
const points = Array.from(touchPointers.values());
|
|
2519
|
+
const first = points[0];
|
|
2520
|
+
const second = points[1];
|
|
2521
|
+
return first && second ? [first, second] : null;
|
|
2522
|
+
};
|
|
2523
|
+
const getPointerDistance = (first, second) => {
|
|
2524
|
+
return Math.hypot(second.x - first.x, second.y - first.y);
|
|
2525
|
+
};
|
|
2526
|
+
const getMidpoint = (first, second) => {
|
|
2527
|
+
return {
|
|
2528
|
+
x: (first.x + second.x) / 2,
|
|
2529
|
+
y: (first.y + second.y) / 2
|
|
2530
|
+
};
|
|
2531
|
+
};
|
|
2532
|
+
const beginPinchZoom = (first, second) => {
|
|
2533
|
+
if (!drawState || data.length === 0) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const midpoint = getMidpoint(first, second);
|
|
2537
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2538
|
+
pinchZoomState = {
|
|
2539
|
+
startDistance: Math.max(1, getPointerDistance(first, second)),
|
|
2540
|
+
startSpan: xSpan,
|
|
2541
|
+
anchorIndex: drawState.xStart + anchorRatio * xSpan
|
|
2542
|
+
};
|
|
2543
|
+
isDragging = false;
|
|
2544
|
+
dragMode = null;
|
|
2545
|
+
activePointerId = null;
|
|
2546
|
+
pointerDownInfo = null;
|
|
2547
|
+
orderDragState = null;
|
|
2548
|
+
actionDragState = null;
|
|
2549
|
+
canvas.style.cursor = "default";
|
|
2550
|
+
setCrosshairPoint(null);
|
|
2551
|
+
};
|
|
2552
|
+
const applyPinchZoom = (first, second) => {
|
|
2553
|
+
if (!drawState || !pinchZoomState || data.length === 0) {
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
const distance = getPointerDistance(first, second);
|
|
2557
|
+
if (distance <= 0) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
const minSpan = minVisibleBars;
|
|
2561
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2562
|
+
const nextSpan = clamp(pinchZoomState.startSpan * (pinchZoomState.startDistance / distance), minSpan, maxSpan);
|
|
2563
|
+
const midpoint = getMidpoint(first, second);
|
|
2564
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2565
|
+
const nextStart = pinchZoomState.anchorIndex - anchorRatio * nextSpan;
|
|
2566
|
+
xSpan = nextSpan;
|
|
2567
|
+
xCenter = nextStart + nextSpan / 2;
|
|
2568
|
+
clampXViewport();
|
|
2569
|
+
updateFollowLatest(false);
|
|
2570
|
+
emitViewportChange();
|
|
2571
|
+
draw();
|
|
2572
|
+
};
|
|
2515
2573
|
const onPointerDown = (event) => {
|
|
2516
2574
|
const point = getCanvasPoint(event);
|
|
2575
|
+
if (event.pointerType === "touch") {
|
|
2576
|
+
touchPointers.set(event.pointerId, point);
|
|
2577
|
+
canvas.setPointerCapture(event.pointerId);
|
|
2578
|
+
const touchPair = getTouchPair();
|
|
2579
|
+
if (touchPair) {
|
|
2580
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2517
2584
|
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
2518
2585
|
if (crosshairButtonRegion) {
|
|
2519
2586
|
crosshairPriceActionHandler?.({
|
|
@@ -2575,6 +2642,20 @@ function createChart(element, options = {}) {
|
|
|
2575
2642
|
};
|
|
2576
2643
|
const onPointerMove = (event) => {
|
|
2577
2644
|
const point = getCanvasPoint(event);
|
|
2645
|
+
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
2646
|
+
touchPointers.set(event.pointerId, point);
|
|
2647
|
+
const touchPair = getTouchPair();
|
|
2648
|
+
if (touchPair) {
|
|
2649
|
+
if (!pinchZoomState) {
|
|
2650
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2651
|
+
}
|
|
2652
|
+
applyPinchZoom(touchPair[0], touchPair[1]);
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (pinchZoomState) {
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2578
2659
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
2579
2660
|
const dx = point.x - pointerDownInfo.x;
|
|
2580
2661
|
const dy = point.y - pointerDownInfo.y;
|
|
@@ -2679,7 +2760,7 @@ function createChart(element, options = {}) {
|
|
|
2679
2760
|
setCrosshairPoint(null);
|
|
2680
2761
|
} else if (dragMode === "x-axis") {
|
|
2681
2762
|
canvas.style.cursor = "ew-resize";
|
|
2682
|
-
zoomXToLatest(Math.exp(
|
|
2763
|
+
zoomXToLatest(Math.exp(deltaX * 6e-3));
|
|
2683
2764
|
setCrosshairPoint(null);
|
|
2684
2765
|
} else if (dragMode === "y-axis") {
|
|
2685
2766
|
canvas.style.cursor = "ns-resize";
|
|
@@ -2691,6 +2772,22 @@ function createChart(element, options = {}) {
|
|
|
2691
2772
|
lastPointerY = point.y;
|
|
2692
2773
|
};
|
|
2693
2774
|
const endPointerDrag = (event) => {
|
|
2775
|
+
if (event?.pointerType === "touch") {
|
|
2776
|
+
touchPointers.delete(event.pointerId);
|
|
2777
|
+
if (pinchZoomState) {
|
|
2778
|
+
pinchZoomState = null;
|
|
2779
|
+
isDragging = false;
|
|
2780
|
+
dragMode = null;
|
|
2781
|
+
activePointerId = null;
|
|
2782
|
+
pointerDownInfo = null;
|
|
2783
|
+
setCrosshairPoint(null);
|
|
2784
|
+
canvas.style.cursor = "default";
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
} else if (!event) {
|
|
2788
|
+
touchPointers.clear();
|
|
2789
|
+
pinchZoomState = null;
|
|
2790
|
+
}
|
|
2694
2791
|
if (event && activePointerId !== null && event.pointerId !== activePointerId) {
|
|
2695
2792
|
return;
|
|
2696
2793
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -2536,8 +2536,75 @@ function createChart(element, options = {}) {
|
|
|
2536
2536
|
let lastPointerX = 0;
|
|
2537
2537
|
let lastPointerY = 0;
|
|
2538
2538
|
let activePointerId = null;
|
|
2539
|
+
const touchPointers = /* @__PURE__ */ new Map();
|
|
2540
|
+
let pinchZoomState = null;
|
|
2541
|
+
const getTouchPair = () => {
|
|
2542
|
+
const points = Array.from(touchPointers.values());
|
|
2543
|
+
const first = points[0];
|
|
2544
|
+
const second = points[1];
|
|
2545
|
+
return first && second ? [first, second] : null;
|
|
2546
|
+
};
|
|
2547
|
+
const getPointerDistance = (first, second) => {
|
|
2548
|
+
return Math.hypot(second.x - first.x, second.y - first.y);
|
|
2549
|
+
};
|
|
2550
|
+
const getMidpoint = (first, second) => {
|
|
2551
|
+
return {
|
|
2552
|
+
x: (first.x + second.x) / 2,
|
|
2553
|
+
y: (first.y + second.y) / 2
|
|
2554
|
+
};
|
|
2555
|
+
};
|
|
2556
|
+
const beginPinchZoom = (first, second) => {
|
|
2557
|
+
if (!drawState || data.length === 0) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
const midpoint = getMidpoint(first, second);
|
|
2561
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2562
|
+
pinchZoomState = {
|
|
2563
|
+
startDistance: Math.max(1, getPointerDistance(first, second)),
|
|
2564
|
+
startSpan: xSpan,
|
|
2565
|
+
anchorIndex: drawState.xStart + anchorRatio * xSpan
|
|
2566
|
+
};
|
|
2567
|
+
isDragging = false;
|
|
2568
|
+
dragMode = null;
|
|
2569
|
+
activePointerId = null;
|
|
2570
|
+
pointerDownInfo = null;
|
|
2571
|
+
orderDragState = null;
|
|
2572
|
+
actionDragState = null;
|
|
2573
|
+
canvas.style.cursor = "default";
|
|
2574
|
+
setCrosshairPoint(null);
|
|
2575
|
+
};
|
|
2576
|
+
const applyPinchZoom = (first, second) => {
|
|
2577
|
+
if (!drawState || !pinchZoomState || data.length === 0) {
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
const distance = getPointerDistance(first, second);
|
|
2581
|
+
if (distance <= 0) {
|
|
2582
|
+
return;
|
|
2583
|
+
}
|
|
2584
|
+
const minSpan = minVisibleBars;
|
|
2585
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2586
|
+
const nextSpan = clamp(pinchZoomState.startSpan * (pinchZoomState.startDistance / distance), minSpan, maxSpan);
|
|
2587
|
+
const midpoint = getMidpoint(first, second);
|
|
2588
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2589
|
+
const nextStart = pinchZoomState.anchorIndex - anchorRatio * nextSpan;
|
|
2590
|
+
xSpan = nextSpan;
|
|
2591
|
+
xCenter = nextStart + nextSpan / 2;
|
|
2592
|
+
clampXViewport();
|
|
2593
|
+
updateFollowLatest(false);
|
|
2594
|
+
emitViewportChange();
|
|
2595
|
+
draw();
|
|
2596
|
+
};
|
|
2539
2597
|
const onPointerDown = (event) => {
|
|
2540
2598
|
const point = getCanvasPoint(event);
|
|
2599
|
+
if (event.pointerType === "touch") {
|
|
2600
|
+
touchPointers.set(event.pointerId, point);
|
|
2601
|
+
canvas.setPointerCapture(event.pointerId);
|
|
2602
|
+
const touchPair = getTouchPair();
|
|
2603
|
+
if (touchPair) {
|
|
2604
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2541
2608
|
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
2542
2609
|
if (crosshairButtonRegion) {
|
|
2543
2610
|
crosshairPriceActionHandler?.({
|
|
@@ -2599,6 +2666,20 @@ function createChart(element, options = {}) {
|
|
|
2599
2666
|
};
|
|
2600
2667
|
const onPointerMove = (event) => {
|
|
2601
2668
|
const point = getCanvasPoint(event);
|
|
2669
|
+
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
2670
|
+
touchPointers.set(event.pointerId, point);
|
|
2671
|
+
const touchPair = getTouchPair();
|
|
2672
|
+
if (touchPair) {
|
|
2673
|
+
if (!pinchZoomState) {
|
|
2674
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2675
|
+
}
|
|
2676
|
+
applyPinchZoom(touchPair[0], touchPair[1]);
|
|
2677
|
+
return;
|
|
2678
|
+
}
|
|
2679
|
+
if (pinchZoomState) {
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2602
2683
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
2603
2684
|
const dx = point.x - pointerDownInfo.x;
|
|
2604
2685
|
const dy = point.y - pointerDownInfo.y;
|
|
@@ -2703,7 +2784,7 @@ function createChart(element, options = {}) {
|
|
|
2703
2784
|
setCrosshairPoint(null);
|
|
2704
2785
|
} else if (dragMode === "x-axis") {
|
|
2705
2786
|
canvas.style.cursor = "ew-resize";
|
|
2706
|
-
zoomXToLatest(Math.exp(
|
|
2787
|
+
zoomXToLatest(Math.exp(deltaX * 6e-3));
|
|
2707
2788
|
setCrosshairPoint(null);
|
|
2708
2789
|
} else if (dragMode === "y-axis") {
|
|
2709
2790
|
canvas.style.cursor = "ns-resize";
|
|
@@ -2715,6 +2796,22 @@ function createChart(element, options = {}) {
|
|
|
2715
2796
|
lastPointerY = point.y;
|
|
2716
2797
|
};
|
|
2717
2798
|
const endPointerDrag = (event) => {
|
|
2799
|
+
if (event?.pointerType === "touch") {
|
|
2800
|
+
touchPointers.delete(event.pointerId);
|
|
2801
|
+
if (pinchZoomState) {
|
|
2802
|
+
pinchZoomState = null;
|
|
2803
|
+
isDragging = false;
|
|
2804
|
+
dragMode = null;
|
|
2805
|
+
activePointerId = null;
|
|
2806
|
+
pointerDownInfo = null;
|
|
2807
|
+
setCrosshairPoint(null);
|
|
2808
|
+
canvas.style.cursor = "default";
|
|
2809
|
+
return;
|
|
2810
|
+
}
|
|
2811
|
+
} else if (!event) {
|
|
2812
|
+
touchPointers.clear();
|
|
2813
|
+
pinchZoomState = null;
|
|
2814
|
+
}
|
|
2718
2815
|
if (event && activePointerId !== null && event.pointerId !== activePointerId) {
|
|
2719
2816
|
return;
|
|
2720
2817
|
}
|
package/dist/index.js
CHANGED
|
@@ -2512,8 +2512,75 @@ function createChart(element, options = {}) {
|
|
|
2512
2512
|
let lastPointerX = 0;
|
|
2513
2513
|
let lastPointerY = 0;
|
|
2514
2514
|
let activePointerId = null;
|
|
2515
|
+
const touchPointers = /* @__PURE__ */ new Map();
|
|
2516
|
+
let pinchZoomState = null;
|
|
2517
|
+
const getTouchPair = () => {
|
|
2518
|
+
const points = Array.from(touchPointers.values());
|
|
2519
|
+
const first = points[0];
|
|
2520
|
+
const second = points[1];
|
|
2521
|
+
return first && second ? [first, second] : null;
|
|
2522
|
+
};
|
|
2523
|
+
const getPointerDistance = (first, second) => {
|
|
2524
|
+
return Math.hypot(second.x - first.x, second.y - first.y);
|
|
2525
|
+
};
|
|
2526
|
+
const getMidpoint = (first, second) => {
|
|
2527
|
+
return {
|
|
2528
|
+
x: (first.x + second.x) / 2,
|
|
2529
|
+
y: (first.y + second.y) / 2
|
|
2530
|
+
};
|
|
2531
|
+
};
|
|
2532
|
+
const beginPinchZoom = (first, second) => {
|
|
2533
|
+
if (!drawState || data.length === 0) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
const midpoint = getMidpoint(first, second);
|
|
2537
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2538
|
+
pinchZoomState = {
|
|
2539
|
+
startDistance: Math.max(1, getPointerDistance(first, second)),
|
|
2540
|
+
startSpan: xSpan,
|
|
2541
|
+
anchorIndex: drawState.xStart + anchorRatio * xSpan
|
|
2542
|
+
};
|
|
2543
|
+
isDragging = false;
|
|
2544
|
+
dragMode = null;
|
|
2545
|
+
activePointerId = null;
|
|
2546
|
+
pointerDownInfo = null;
|
|
2547
|
+
orderDragState = null;
|
|
2548
|
+
actionDragState = null;
|
|
2549
|
+
canvas.style.cursor = "default";
|
|
2550
|
+
setCrosshairPoint(null);
|
|
2551
|
+
};
|
|
2552
|
+
const applyPinchZoom = (first, second) => {
|
|
2553
|
+
if (!drawState || !pinchZoomState || data.length === 0) {
|
|
2554
|
+
return;
|
|
2555
|
+
}
|
|
2556
|
+
const distance = getPointerDistance(first, second);
|
|
2557
|
+
if (distance <= 0) {
|
|
2558
|
+
return;
|
|
2559
|
+
}
|
|
2560
|
+
const minSpan = minVisibleBars;
|
|
2561
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2562
|
+
const nextSpan = clamp(pinchZoomState.startSpan * (pinchZoomState.startDistance / distance), minSpan, maxSpan);
|
|
2563
|
+
const midpoint = getMidpoint(first, second);
|
|
2564
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2565
|
+
const nextStart = pinchZoomState.anchorIndex - anchorRatio * nextSpan;
|
|
2566
|
+
xSpan = nextSpan;
|
|
2567
|
+
xCenter = nextStart + nextSpan / 2;
|
|
2568
|
+
clampXViewport();
|
|
2569
|
+
updateFollowLatest(false);
|
|
2570
|
+
emitViewportChange();
|
|
2571
|
+
draw();
|
|
2572
|
+
};
|
|
2515
2573
|
const onPointerDown = (event) => {
|
|
2516
2574
|
const point = getCanvasPoint(event);
|
|
2575
|
+
if (event.pointerType === "touch") {
|
|
2576
|
+
touchPointers.set(event.pointerId, point);
|
|
2577
|
+
canvas.setPointerCapture(event.pointerId);
|
|
2578
|
+
const touchPair = getTouchPair();
|
|
2579
|
+
if (touchPair) {
|
|
2580
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
}
|
|
2517
2584
|
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
2518
2585
|
if (crosshairButtonRegion) {
|
|
2519
2586
|
crosshairPriceActionHandler?.({
|
|
@@ -2575,6 +2642,20 @@ function createChart(element, options = {}) {
|
|
|
2575
2642
|
};
|
|
2576
2643
|
const onPointerMove = (event) => {
|
|
2577
2644
|
const point = getCanvasPoint(event);
|
|
2645
|
+
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
2646
|
+
touchPointers.set(event.pointerId, point);
|
|
2647
|
+
const touchPair = getTouchPair();
|
|
2648
|
+
if (touchPair) {
|
|
2649
|
+
if (!pinchZoomState) {
|
|
2650
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2651
|
+
}
|
|
2652
|
+
applyPinchZoom(touchPair[0], touchPair[1]);
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (pinchZoomState) {
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2578
2659
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
2579
2660
|
const dx = point.x - pointerDownInfo.x;
|
|
2580
2661
|
const dy = point.y - pointerDownInfo.y;
|
|
@@ -2679,7 +2760,7 @@ function createChart(element, options = {}) {
|
|
|
2679
2760
|
setCrosshairPoint(null);
|
|
2680
2761
|
} else if (dragMode === "x-axis") {
|
|
2681
2762
|
canvas.style.cursor = "ew-resize";
|
|
2682
|
-
zoomXToLatest(Math.exp(
|
|
2763
|
+
zoomXToLatest(Math.exp(deltaX * 6e-3));
|
|
2683
2764
|
setCrosshairPoint(null);
|
|
2684
2765
|
} else if (dragMode === "y-axis") {
|
|
2685
2766
|
canvas.style.cursor = "ns-resize";
|
|
@@ -2691,6 +2772,22 @@ function createChart(element, options = {}) {
|
|
|
2691
2772
|
lastPointerY = point.y;
|
|
2692
2773
|
};
|
|
2693
2774
|
const endPointerDrag = (event) => {
|
|
2775
|
+
if (event?.pointerType === "touch") {
|
|
2776
|
+
touchPointers.delete(event.pointerId);
|
|
2777
|
+
if (pinchZoomState) {
|
|
2778
|
+
pinchZoomState = null;
|
|
2779
|
+
isDragging = false;
|
|
2780
|
+
dragMode = null;
|
|
2781
|
+
activePointerId = null;
|
|
2782
|
+
pointerDownInfo = null;
|
|
2783
|
+
setCrosshairPoint(null);
|
|
2784
|
+
canvas.style.cursor = "default";
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
} else if (!event) {
|
|
2788
|
+
touchPointers.clear();
|
|
2789
|
+
pinchZoomState = null;
|
|
2790
|
+
}
|
|
2694
2791
|
if (event && activePointerId !== null && event.pointerId !== activePointerId) {
|
|
2695
2792
|
return;
|
|
2696
2793
|
}
|