hyperprop-charting-library 0.1.43 → 0.1.45
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/README.md +22 -0
- package/dist/hyperprop-charting-library.cjs +309 -18
- package/dist/hyperprop-charting-library.d.ts +31 -1
- package/dist/hyperprop-charting-library.js +309 -18
- package/dist/index.cjs +309 -18
- package/dist/index.d.cts +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +309 -18
- package/docs/API.md +41 -0
- package/package.json +1 -1
|
@@ -61,6 +61,35 @@ var DEFAULT_DASH_PATTERNS = {
|
|
|
61
61
|
borderDotted: [2, 2],
|
|
62
62
|
borderDashed: [6, 4]
|
|
63
63
|
};
|
|
64
|
+
var DEFAULT_LABELS_OPTIONS = {
|
|
65
|
+
visible: true,
|
|
66
|
+
symbolName: "",
|
|
67
|
+
showSymbolName: false,
|
|
68
|
+
showLastPrice: true,
|
|
69
|
+
showPreviousClose: false,
|
|
70
|
+
previousClosePrice: Number.NaN,
|
|
71
|
+
showHighLow: false,
|
|
72
|
+
showBidAsk: false,
|
|
73
|
+
bidPrice: Number.NaN,
|
|
74
|
+
askPrice: Number.NaN,
|
|
75
|
+
showIndicatorNames: false,
|
|
76
|
+
showIndicatorValues: false,
|
|
77
|
+
showCountdownToBarClose: false,
|
|
78
|
+
noOverlapping: true,
|
|
79
|
+
backgroundColor: "#0b1220",
|
|
80
|
+
textColor: "#cbd5e1",
|
|
81
|
+
mutedTextColor: "#94a3b8",
|
|
82
|
+
symbolNameBackgroundColor: "#1f2937",
|
|
83
|
+
symbolNameTextColor: "#e5e7eb",
|
|
84
|
+
previousCloseColor: "#94a3b8",
|
|
85
|
+
highLowColor: "#a78bfa",
|
|
86
|
+
bidColor: "#ef4444",
|
|
87
|
+
askColor: "#22c55e",
|
|
88
|
+
indicatorTextColor: "#cbd5e1",
|
|
89
|
+
borderRadius: 3,
|
|
90
|
+
labelHeight: 20,
|
|
91
|
+
labelPaddingX: 8
|
|
92
|
+
};
|
|
64
93
|
var DEFAULT_PRICE_LINE_OPTIONS = {
|
|
65
94
|
visible: true,
|
|
66
95
|
style: "solid",
|
|
@@ -150,6 +179,7 @@ var DEFAULT_OPTIONS = {
|
|
|
150
179
|
labelTextColor: "#0b1220",
|
|
151
180
|
labelBorderRadius: 3
|
|
152
181
|
},
|
|
182
|
+
labels: DEFAULT_LABELS_OPTIONS,
|
|
153
183
|
dashPatterns: DEFAULT_DASH_PATTERNS,
|
|
154
184
|
indicators: []
|
|
155
185
|
};
|
|
@@ -189,6 +219,10 @@ var mergeChartOptions = (baseOptions, options = {}) => ({
|
|
|
189
219
|
...baseOptions.tickerLine,
|
|
190
220
|
...options.tickerLine ?? {}
|
|
191
221
|
},
|
|
222
|
+
labels: {
|
|
223
|
+
...baseOptions.labels,
|
|
224
|
+
...options.labels ?? {}
|
|
225
|
+
},
|
|
192
226
|
dashPatterns: {
|
|
193
227
|
...baseOptions.dashPatterns,
|
|
194
228
|
...options.dashPatterns ?? {}
|
|
@@ -1255,6 +1289,14 @@ function createChart(element, options = {}) {
|
|
|
1255
1289
|
ctx.textBaseline = baseline;
|
|
1256
1290
|
ctx.fillText(text, x, y);
|
|
1257
1291
|
};
|
|
1292
|
+
const formatDuration = (ms) => {
|
|
1293
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
1294
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
1295
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
1296
|
+
const seconds = totalSeconds % 60;
|
|
1297
|
+
const pad = (value) => String(value).padStart(2, "0");
|
|
1298
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
1299
|
+
};
|
|
1258
1300
|
const drawPriceLine = (line, yFromPrice, chartLeft, chartTop, chartRight, chartBottom) => {
|
|
1259
1301
|
const mergedLine = { ...DEFAULT_PRICE_LINE_OPTIONS, ...line };
|
|
1260
1302
|
if (!mergedLine.visible || !Number.isFinite(mergedLine.price)) {
|
|
@@ -2014,14 +2056,39 @@ function createChart(element, options = {}) {
|
|
|
2014
2056
|
drawText(formatPrice(price), chartRight + 6, y, "left", "middle", yAxis.textColor);
|
|
2015
2057
|
ctx.font = prevFont;
|
|
2016
2058
|
}
|
|
2059
|
+
const labels = { ...DEFAULT_LABELS_OPTIONS, ...mergedOptions.labels ?? {} };
|
|
2060
|
+
const priceAxisLabels = [];
|
|
2061
|
+
const addPriceAxisLabel = (label) => {
|
|
2062
|
+
if (!labels.visible || !Number.isFinite(label.price) || label.text.length === 0) {
|
|
2063
|
+
return;
|
|
2064
|
+
}
|
|
2065
|
+
priceAxisLabels.push(label);
|
|
2066
|
+
};
|
|
2067
|
+
const drawReferenceLine = (price, color, style = "dotted") => {
|
|
2068
|
+
if (!Number.isFinite(price)) {
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
const y = clamp(yFromPrice(price), chartTop + 1, chartBottom - 1);
|
|
2072
|
+
ctx.save();
|
|
2073
|
+
ctx.strokeStyle = color;
|
|
2074
|
+
ctx.lineWidth = 1;
|
|
2075
|
+
applyDashPattern(style, dashPatterns.dotted, dashPatterns.dashed);
|
|
2076
|
+
ctx.beginPath();
|
|
2077
|
+
ctx.moveTo(crisp(chartLeft), crisp(y));
|
|
2078
|
+
ctx.lineTo(crisp(chartRight), crisp(y));
|
|
2079
|
+
ctx.stroke();
|
|
2080
|
+
ctx.restore();
|
|
2081
|
+
};
|
|
2017
2082
|
const ticker = mergedOptions.tickerLine ?? DEFAULT_OPTIONS.tickerLine;
|
|
2018
2083
|
const lastPoint = data[data.length - 1];
|
|
2084
|
+
let tickerPrice = null;
|
|
2085
|
+
let tickerColor = null;
|
|
2019
2086
|
if ((ticker.visible ?? true) && lastPoint) {
|
|
2020
|
-
|
|
2087
|
+
tickerPrice = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice : lastPoint.c;
|
|
2021
2088
|
const tickerY = yFromPrice(tickerPrice);
|
|
2022
2089
|
const lineY = clamp(tickerY, chartTop + 1, chartBottom - 1);
|
|
2023
2090
|
const lastDirection = ticker.smoothing && smoothedTickerPrice !== null ? smoothedTickerPrice >= lastPoint.o ? "up" : "down" : getCandleDirectionByIndex(data.length - 1);
|
|
2024
|
-
|
|
2091
|
+
tickerColor = ticker.color ?? (lastDirection === "up" ? mergedOptions.upColor : mergedOptions.downColor);
|
|
2025
2092
|
const tickerThickness = Math.max(1, ticker.thickness ?? 1);
|
|
2026
2093
|
const tickerStyle = ticker.style ?? "solid";
|
|
2027
2094
|
ctx.save();
|
|
@@ -2034,24 +2101,122 @@ function createChart(element, options = {}) {
|
|
|
2034
2101
|
ctx.stroke();
|
|
2035
2102
|
ctx.setLineDash([]);
|
|
2036
2103
|
ctx.restore();
|
|
2037
|
-
|
|
2104
|
+
}
|
|
2105
|
+
if ((ticker.visible ?? true) && labels.showLastPrice && tickerPrice !== null && tickerColor !== null) {
|
|
2106
|
+
addPriceAxisLabel({
|
|
2107
|
+
text: formatPrice(tickerPrice),
|
|
2108
|
+
price: tickerPrice,
|
|
2109
|
+
backgroundColor: ticker.labelBackgroundColor ?? tickerColor,
|
|
2110
|
+
textColor: ticker.labelTextColor ?? "#0b1220",
|
|
2111
|
+
color: tickerColor,
|
|
2112
|
+
priority: 100
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
if (labels.showSymbolName && labels.symbolName.trim().length > 0 && tickerPrice !== null) {
|
|
2116
|
+
addPriceAxisLabel({
|
|
2117
|
+
text: labels.symbolName.trim(),
|
|
2118
|
+
price: tickerPrice,
|
|
2119
|
+
backgroundColor: labels.symbolNameBackgroundColor,
|
|
2120
|
+
textColor: labels.symbolNameTextColor,
|
|
2121
|
+
color: labels.symbolNameBackgroundColor,
|
|
2122
|
+
priority: 95
|
|
2123
|
+
});
|
|
2124
|
+
}
|
|
2125
|
+
if (labels.showPreviousClose) {
|
|
2126
|
+
const previousCloseCandidate = Number.isFinite(labels.previousClosePrice) ? labels.previousClosePrice : data.length > 1 ? data[data.length - 2]?.c : Number.NaN;
|
|
2127
|
+
if (previousCloseCandidate !== void 0 && Number.isFinite(previousCloseCandidate)) {
|
|
2128
|
+
const previousClose = previousCloseCandidate;
|
|
2129
|
+
drawReferenceLine(previousClose, labels.previousCloseColor, "dashed");
|
|
2130
|
+
addPriceAxisLabel({
|
|
2131
|
+
text: `PDC ${formatPrice(previousClose)}`,
|
|
2132
|
+
price: previousClose,
|
|
2133
|
+
backgroundColor: labels.backgroundColor,
|
|
2134
|
+
textColor: labels.mutedTextColor,
|
|
2135
|
+
color: labels.previousCloseColor,
|
|
2136
|
+
priority: 50
|
|
2137
|
+
});
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
if (labels.showHighLow && visibleData.length > 0) {
|
|
2141
|
+
const visibleHigh = Math.max(...visibleData.map((point) => point.h));
|
|
2142
|
+
const visibleLow = Math.min(...visibleData.map((point) => point.l));
|
|
2143
|
+
addPriceAxisLabel({
|
|
2144
|
+
text: `H ${formatPrice(visibleHigh)}`,
|
|
2145
|
+
price: visibleHigh,
|
|
2146
|
+
backgroundColor: labels.backgroundColor,
|
|
2147
|
+
textColor: labels.textColor,
|
|
2148
|
+
color: labels.highLowColor,
|
|
2149
|
+
priority: 40
|
|
2150
|
+
});
|
|
2151
|
+
addPriceAxisLabel({
|
|
2152
|
+
text: `L ${formatPrice(visibleLow)}`,
|
|
2153
|
+
price: visibleLow,
|
|
2154
|
+
backgroundColor: labels.backgroundColor,
|
|
2155
|
+
textColor: labels.textColor,
|
|
2156
|
+
color: labels.highLowColor,
|
|
2157
|
+
priority: 40
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
2160
|
+
if (labels.showBidAsk) {
|
|
2161
|
+
if (Number.isFinite(labels.bidPrice)) {
|
|
2162
|
+
drawReferenceLine(labels.bidPrice, labels.bidColor, "dotted");
|
|
2163
|
+
addPriceAxisLabel({
|
|
2164
|
+
text: `B ${formatPrice(labels.bidPrice)}`,
|
|
2165
|
+
price: labels.bidPrice,
|
|
2166
|
+
backgroundColor: labels.bidColor,
|
|
2167
|
+
textColor: "#0b1220",
|
|
2168
|
+
color: labels.bidColor,
|
|
2169
|
+
priority: 80
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
if (Number.isFinite(labels.askPrice)) {
|
|
2173
|
+
drawReferenceLine(labels.askPrice, labels.askColor, "dotted");
|
|
2174
|
+
addPriceAxisLabel({
|
|
2175
|
+
text: `A ${formatPrice(labels.askPrice)}`,
|
|
2176
|
+
price: labels.askPrice,
|
|
2177
|
+
backgroundColor: labels.askColor,
|
|
2178
|
+
textColor: "#0b1220",
|
|
2179
|
+
color: labels.askColor,
|
|
2180
|
+
priority: 80
|
|
2181
|
+
});
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
if (priceAxisLabels.length > 0) {
|
|
2185
|
+
const labelPaddingX = Math.max(4, labels.labelPaddingX);
|
|
2186
|
+
const labelHeight = Math.max(14, labels.labelHeight);
|
|
2187
|
+
const labelRadius = Math.max(0, labels.borderRadius);
|
|
2038
2188
|
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
2039
|
-
const
|
|
2040
|
-
|
|
2041
|
-
|
|
2189
|
+
const positionedLabels = priceAxisLabels.map((label) => {
|
|
2190
|
+
const labelTextWidth = label.text === formatPrice(label.price) ? getPriceLabelWidth(label.text, labelPaddingX) : Math.ceil(ctx.measureText(label.text).width) + labelPaddingX * 2;
|
|
2191
|
+
return {
|
|
2192
|
+
...label,
|
|
2193
|
+
width: labelTextWidth,
|
|
2194
|
+
targetY: clamp(yFromPrice(label.price), chartTop + 1, chartBottom - 1) - labelHeight / 2,
|
|
2195
|
+
y: 0
|
|
2196
|
+
};
|
|
2197
|
+
}).sort((a, b) => a.targetY - b.targetY || b.priority - a.priority);
|
|
2198
|
+
const minY = chartTop;
|
|
2199
|
+
const maxY = chartBottom - labelHeight;
|
|
2200
|
+
let cursorY = minY;
|
|
2201
|
+
for (const label of positionedLabels) {
|
|
2202
|
+
label.y = labels.noOverlapping ? Math.max(clamp(label.targetY, minY, maxY), cursorY) : clamp(label.targetY, minY, maxY);
|
|
2203
|
+
cursorY = label.y + labelHeight + 2;
|
|
2204
|
+
}
|
|
2205
|
+
if (labels.noOverlapping && positionedLabels.length > 0) {
|
|
2206
|
+
const lastLabel = positionedLabels[positionedLabels.length - 1];
|
|
2207
|
+
const overflow = lastLabel ? lastLabel.y + labelHeight - maxY : 0;
|
|
2208
|
+
if (overflow > 0) {
|
|
2209
|
+
for (const label of positionedLabels) {
|
|
2210
|
+
label.y = Math.max(minY, label.y - overflow);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
}
|
|
2042
2214
|
const labelX = chartRight + 4;
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
tickerLabel,
|
|
2049
|
-
labelX + labelPaddingX,
|
|
2050
|
-
labelY + labelHeight / 2,
|
|
2051
|
-
"left",
|
|
2052
|
-
"middle",
|
|
2053
|
-
ticker.labelTextColor ?? "#0b1220"
|
|
2054
|
-
);
|
|
2215
|
+
for (const label of positionedLabels) {
|
|
2216
|
+
ctx.fillStyle = label.backgroundColor;
|
|
2217
|
+
fillRoundedRect(Math.round(labelX), Math.round(label.y), label.width, labelHeight, labelRadius);
|
|
2218
|
+
drawText(label.text, labelX + labelPaddingX, label.y + labelHeight / 2, "left", "middle", label.textColor);
|
|
2219
|
+
}
|
|
2055
2220
|
}
|
|
2056
2221
|
for (const priceLine of priceLines) {
|
|
2057
2222
|
drawPriceLine(priceLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
@@ -2059,6 +2224,25 @@ function createChart(element, options = {}) {
|
|
|
2059
2224
|
for (const orderLine of orderLines) {
|
|
2060
2225
|
drawOrderLine(orderLine, yFromPrice, chartLeft, chartTop, chartRight, chartBottom);
|
|
2061
2226
|
}
|
|
2227
|
+
if (labels.visible && (labels.showIndicatorNames || labels.showIndicatorValues)) {
|
|
2228
|
+
const labelEntries = [...activeOverlayIndicators, ...activeSeparateIndicators].map(({ indicator, plugin }) => {
|
|
2229
|
+
const inputValues = Object.entries(indicator.inputs).filter(([, value]) => typeof value === "number" || typeof value === "string" || typeof value === "boolean").slice(0, 2).map(([, value]) => String(value));
|
|
2230
|
+
if (labels.showIndicatorNames && labels.showIndicatorValues && inputValues.length > 0) {
|
|
2231
|
+
return `${plugin.name} ${inputValues.join(" ")}`;
|
|
2232
|
+
}
|
|
2233
|
+
if (labels.showIndicatorNames) {
|
|
2234
|
+
return plugin.name;
|
|
2235
|
+
}
|
|
2236
|
+
return inputValues.join(" ");
|
|
2237
|
+
}).filter((entry) => entry.length > 0);
|
|
2238
|
+
if (labelEntries.length > 0) {
|
|
2239
|
+
const prevFont = ctx.font;
|
|
2240
|
+
ctx.font = `${Math.max(8, axis.fontSize)}px ${mergedOptions.fontFamily}`;
|
|
2241
|
+
const legendText = labelEntries.join(" ");
|
|
2242
|
+
drawText(legendText, chartLeft + 10, chartTop + 10, "left", "top", labels.indicatorTextColor);
|
|
2243
|
+
ctx.font = prevFont;
|
|
2244
|
+
}
|
|
2245
|
+
}
|
|
2062
2246
|
for (let index = tickStartIndex; index <= visibleTickEnd; index += xStep) {
|
|
2063
2247
|
const tickTime = getTimeForIndex(index);
|
|
2064
2248
|
if (!tickTime) {
|
|
@@ -2074,6 +2258,16 @@ function createChart(element, options = {}) {
|
|
|
2074
2258
|
drawText(timeLabel, x, fullChartBottom + 8, "center", "top", xAxis.textColor);
|
|
2075
2259
|
ctx.font = prevFont;
|
|
2076
2260
|
}
|
|
2261
|
+
if (labels.visible && labels.showCountdownToBarClose && lastPoint) {
|
|
2262
|
+
const stepMs = getTimeStepMs();
|
|
2263
|
+
const rawRemainingMs = lastPoint.time.getTime() + stepMs - Date.now();
|
|
2264
|
+
const countdownMs = rawRemainingMs >= 0 && rawRemainingMs <= stepMs * 2 ? rawRemainingMs : stepMs - Date.now() % stepMs;
|
|
2265
|
+
const countdownText = formatDuration(countdownMs);
|
|
2266
|
+
const prevFont = ctx.font;
|
|
2267
|
+
ctx.font = `${xAxisFontSize}px ${mergedOptions.fontFamily}`;
|
|
2268
|
+
drawText(countdownText, chartRight + 6, fullChartBottom + 8, "left", "top", xAxis.textColor);
|
|
2269
|
+
ctx.font = prevFont;
|
|
2270
|
+
}
|
|
2077
2271
|
if (crosshair.visible && crosshairPoint) {
|
|
2078
2272
|
const cx = clamp(crosshairPoint.x, chartLeft, chartRight);
|
|
2079
2273
|
const cy = clamp(crosshairPoint.y, chartTop, chartBottom);
|
|
@@ -2512,8 +2706,75 @@ function createChart(element, options = {}) {
|
|
|
2512
2706
|
let lastPointerX = 0;
|
|
2513
2707
|
let lastPointerY = 0;
|
|
2514
2708
|
let activePointerId = null;
|
|
2709
|
+
const touchPointers = /* @__PURE__ */ new Map();
|
|
2710
|
+
let pinchZoomState = null;
|
|
2711
|
+
const getTouchPair = () => {
|
|
2712
|
+
const points = Array.from(touchPointers.values());
|
|
2713
|
+
const first = points[0];
|
|
2714
|
+
const second = points[1];
|
|
2715
|
+
return first && second ? [first, second] : null;
|
|
2716
|
+
};
|
|
2717
|
+
const getPointerDistance = (first, second) => {
|
|
2718
|
+
return Math.hypot(second.x - first.x, second.y - first.y);
|
|
2719
|
+
};
|
|
2720
|
+
const getMidpoint = (first, second) => {
|
|
2721
|
+
return {
|
|
2722
|
+
x: (first.x + second.x) / 2,
|
|
2723
|
+
y: (first.y + second.y) / 2
|
|
2724
|
+
};
|
|
2725
|
+
};
|
|
2726
|
+
const beginPinchZoom = (first, second) => {
|
|
2727
|
+
if (!drawState || data.length === 0) {
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2730
|
+
const midpoint = getMidpoint(first, second);
|
|
2731
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2732
|
+
pinchZoomState = {
|
|
2733
|
+
startDistance: Math.max(1, getPointerDistance(first, second)),
|
|
2734
|
+
startSpan: xSpan,
|
|
2735
|
+
anchorIndex: drawState.xStart + anchorRatio * xSpan
|
|
2736
|
+
};
|
|
2737
|
+
isDragging = false;
|
|
2738
|
+
dragMode = null;
|
|
2739
|
+
activePointerId = null;
|
|
2740
|
+
pointerDownInfo = null;
|
|
2741
|
+
orderDragState = null;
|
|
2742
|
+
actionDragState = null;
|
|
2743
|
+
canvas.style.cursor = "default";
|
|
2744
|
+
setCrosshairPoint(null);
|
|
2745
|
+
};
|
|
2746
|
+
const applyPinchZoom = (first, second) => {
|
|
2747
|
+
if (!drawState || !pinchZoomState || data.length === 0) {
|
|
2748
|
+
return;
|
|
2749
|
+
}
|
|
2750
|
+
const distance = getPointerDistance(first, second);
|
|
2751
|
+
if (distance <= 0) {
|
|
2752
|
+
return;
|
|
2753
|
+
}
|
|
2754
|
+
const minSpan = minVisibleBars;
|
|
2755
|
+
const maxSpan = Math.min(maxVisibleBars, Math.max(minSpan, data.length + maxPanBars * 2));
|
|
2756
|
+
const nextSpan = clamp(pinchZoomState.startSpan * (pinchZoomState.startDistance / distance), minSpan, maxSpan);
|
|
2757
|
+
const midpoint = getMidpoint(first, second);
|
|
2758
|
+
const anchorRatio = clamp((midpoint.x - drawState.chartLeft) / drawState.chartWidth, 0, 1);
|
|
2759
|
+
const nextStart = pinchZoomState.anchorIndex - anchorRatio * nextSpan;
|
|
2760
|
+
xSpan = nextSpan;
|
|
2761
|
+
xCenter = nextStart + nextSpan / 2;
|
|
2762
|
+
clampXViewport();
|
|
2763
|
+
updateFollowLatest(false);
|
|
2764
|
+
emitViewportChange();
|
|
2765
|
+
draw();
|
|
2766
|
+
};
|
|
2515
2767
|
const onPointerDown = (event) => {
|
|
2516
2768
|
const point = getCanvasPoint(event);
|
|
2769
|
+
if (event.pointerType === "touch") {
|
|
2770
|
+
touchPointers.set(event.pointerId, point);
|
|
2771
|
+
canvas.setPointerCapture(event.pointerId);
|
|
2772
|
+
const touchPair = getTouchPair();
|
|
2773
|
+
if (touchPair) {
|
|
2774
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2775
|
+
return;
|
|
2776
|
+
}
|
|
2777
|
+
}
|
|
2517
2778
|
const crosshairButtonRegion = getCrosshairPriceActionRegion(point.x, point.y);
|
|
2518
2779
|
if (crosshairButtonRegion) {
|
|
2519
2780
|
crosshairPriceActionHandler?.({
|
|
@@ -2575,6 +2836,20 @@ function createChart(element, options = {}) {
|
|
|
2575
2836
|
};
|
|
2576
2837
|
const onPointerMove = (event) => {
|
|
2577
2838
|
const point = getCanvasPoint(event);
|
|
2839
|
+
if (event.pointerType === "touch" && touchPointers.has(event.pointerId)) {
|
|
2840
|
+
touchPointers.set(event.pointerId, point);
|
|
2841
|
+
const touchPair = getTouchPair();
|
|
2842
|
+
if (touchPair) {
|
|
2843
|
+
if (!pinchZoomState) {
|
|
2844
|
+
beginPinchZoom(touchPair[0], touchPair[1]);
|
|
2845
|
+
}
|
|
2846
|
+
applyPinchZoom(touchPair[0], touchPair[1]);
|
|
2847
|
+
return;
|
|
2848
|
+
}
|
|
2849
|
+
if (pinchZoomState) {
|
|
2850
|
+
return;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2578
2853
|
if (pointerDownInfo && pointerDownInfo.pointerId === event.pointerId && !pointerDownInfo.moved) {
|
|
2579
2854
|
const dx = point.x - pointerDownInfo.x;
|
|
2580
2855
|
const dy = point.y - pointerDownInfo.y;
|
|
@@ -2691,6 +2966,22 @@ function createChart(element, options = {}) {
|
|
|
2691
2966
|
lastPointerY = point.y;
|
|
2692
2967
|
};
|
|
2693
2968
|
const endPointerDrag = (event) => {
|
|
2969
|
+
if (event?.pointerType === "touch") {
|
|
2970
|
+
touchPointers.delete(event.pointerId);
|
|
2971
|
+
if (pinchZoomState) {
|
|
2972
|
+
pinchZoomState = null;
|
|
2973
|
+
isDragging = false;
|
|
2974
|
+
dragMode = null;
|
|
2975
|
+
activePointerId = null;
|
|
2976
|
+
pointerDownInfo = null;
|
|
2977
|
+
setCrosshairPoint(null);
|
|
2978
|
+
canvas.style.cursor = "default";
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
} else if (!event) {
|
|
2982
|
+
touchPointers.clear();
|
|
2983
|
+
pinchZoomState = null;
|
|
2984
|
+
}
|
|
2694
2985
|
if (event && activePointerId !== null && event.pointerId !== activePointerId) {
|
|
2695
2986
|
return;
|
|
2696
2987
|
}
|