Chart.js 在 Y 上没有零值的情况下交集不起作用
Chart.js intersection not working without zero value on Y
这是我从另一个主题中提取的代码:
var ORDER_STATS = {
"2016": [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001],
"Source": [330865, 332865, 318865, 332865, 320865, 334865, 322865, 320865, 340865],
"Moving average LONG": [304493, 315040, 325809, 329532, 332643, 330421, 329754, 327309, 326865]
};
var colors = ['206,191,26', '119,206,26', '26,200,206', '236,124,98', '206,26,140', '26,77,206', '236,124,98', '206,26,140', '26,77,206'];
// Definning X
var ordersChartData = {
labels: ['2022-02-10', '2022-02-11', '2022-02-12', '2022-02-13', '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17', '2022-02-18'],
datasets: []
}
Object.keys(ORDER_STATS).forEach(function(key) {
color = colors.shift();
ordersChartData.datasets.push({
label: key,
lineTension: 0,
type: 'line',
backgroundColor: "rgba(" + color + ",0.1)",
borderColor: "rgba(" + color + ",1)",
borderWidth: 2,
pointBackgroundColor: "rgba(" + color + ",1)",
pointBorderColor: "#fff",
pointBorderWidth: 1,
pointRadius: 4,
pointHoverBackgroundColor: "#fff",
pointHoverBorderColor: "rgba(" + color + ",1)",
pointHoverBorderWidth: 1,
data: ORDER_STATS[key]
});
});
var ctx = document.getElementById("myChart").getContext("2d");
Chart.defaults.global.defaultFontColor = 'grey';
Chart.defaults.global.defaultFontFamily = "Tahoma";
Chart.defaults.global.defaultFontSize = 11;
Chart.defaults.global.defaultFontStyle = 'normal';
var myChart = new Chart(ctx, {
type: 'line',
data: ordersChartData,
defaultFontSize: 11,
options: {
responsive: true,
title: {
display: true,
text: 'Intersection realization',
fontColor: "#444",
fontFamily: 'Tahoma',
padding: 0
},
legend: {
display: true,
labels: {
fontColor: 'grey',
usePointStyle: true
}
},
tooltips: {
mode: "index",
intersect: true,
position: 'nearest',
bodySpacing: 4
}
}
});
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = Y.top + ((Y.bottom - Y.top) / (Y.ticks.length - 1) * Y.zeroLineIndex);
zeroPointX = Y.right;
yScale = (Y.bottom - Y.top) / (Y.end - Y.start);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function(result, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result.x * xScale) + zeroPointX, (Y.end - Y.start) - (result.y * yScale) - ((Y.end - Y.start) - zeroPointY), 3, 0, 2 * Math.PI, true);
context.fill();
});
}
});
function findIntersects(line1, line2) {
var intersects = [];
line1.forEach(function(val, idx) {
var line1StartX = idx;
var line1StartY = line1[idx];
var line1EndX = idx + 1;
var line1EndY = line1[idx + 1];
var line2StartX = idx;
var line2StartY = line2[idx];
var line2EndX = idx + 1;
var line2EndY = line2[idx + 1];
result = checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY);
if (result.onLine1 && result.onLine2) {
intersects.push(result);
}
});
return intersects;
}
function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
var denominator, a, b, numerator1, numerator2, result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
if (denominator == 0) {
return result;
}
a = line1StartY - line2StartY;
b = line1StartX - line2StartX;
numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
a = numerator1 / denominator;
b = numerator2 / denominator;
// if we cast these lines infinitely in both directions, they intersect here:
result.x = line1StartX + (a * (line1EndX - line1StartX));
result.y = line1StartY + (a * (line1EndY - line1StartY));
// it is worth noting that this should be the same as:
x = line2StartX + (b * (line2EndX - line2StartX));
y = line2StartX + (b * (line2EndY - line2StartY));
// if line1 is a segment and line2 is infinite, they intersect if:
if (a > 0 && a < 1) {
result.onLine1 = true;
}
// if line2 is a segment and line1 is infinite, they intersect if:
if (b > 0 && b < 1) {
result.onLine2 = true;
}
// if line1 and line2 are segments, they intersect if both of the above are true
return result;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="myChart" width="650" height="241" style="display: block; width: 650px; height: 241px;"></canvas>
它运行良好,但如果在这里:
"2016" : [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001]
我在 300000
上更改了第一个值 0
,代码无法再显示交集。
我认为问题出在 Y.zeroLineIndex
。
我尝试了很多变体,记录了几乎所有的值并注意到当第一个值为零时(如示例),Y.zeroLineIndex
为 9。但是如果你将第一个值更改为 300000,它将变为 - 1.
在尝试检测问题并修复它数小时后,我不确定如何修复它。没有任何帮助。
我在 JS 方面不太好,所以请求帮助
编辑:
[演示][1]
[1]: https://i.stack.imgur.com/0t3Gu.png
我对你的问题进行了一些讨论,发现你在根据 Y-axis 设置高度时遇到了问题。使用此代码,您会发现它在任何情况下都有效。我对 context.arc 函数的 zeroPointY、yScale 和 Y 值进行了更改。
Chart.plugins.register({
afterDatasetsDraw: function (chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = (Y.bottom - Y.top)/(Y.ticks.length-1);
zeroPointX = Y.right;
yScale = (Y.end - Y.start)/ (Y.ticks.length - 1);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function (result1, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result1.x * xScale) + zeroPointX, Y.top + (Y.end - result1.y)/yScale*zeroPointY, 3, 0, Math.PI * 2, true);
context.fill();
});
}
});
这是我从另一个主题中提取的代码:
var ORDER_STATS = {
"2016": [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001],
"Source": [330865, 332865, 318865, 332865, 320865, 334865, 322865, 320865, 340865],
"Moving average LONG": [304493, 315040, 325809, 329532, 332643, 330421, 329754, 327309, 326865]
};
var colors = ['206,191,26', '119,206,26', '26,200,206', '236,124,98', '206,26,140', '26,77,206', '236,124,98', '206,26,140', '26,77,206'];
// Definning X
var ordersChartData = {
labels: ['2022-02-10', '2022-02-11', '2022-02-12', '2022-02-13', '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17', '2022-02-18'],
datasets: []
}
Object.keys(ORDER_STATS).forEach(function(key) {
color = colors.shift();
ordersChartData.datasets.push({
label: key,
lineTension: 0,
type: 'line',
backgroundColor: "rgba(" + color + ",0.1)",
borderColor: "rgba(" + color + ",1)",
borderWidth: 2,
pointBackgroundColor: "rgba(" + color + ",1)",
pointBorderColor: "#fff",
pointBorderWidth: 1,
pointRadius: 4,
pointHoverBackgroundColor: "#fff",
pointHoverBorderColor: "rgba(" + color + ",1)",
pointHoverBorderWidth: 1,
data: ORDER_STATS[key]
});
});
var ctx = document.getElementById("myChart").getContext("2d");
Chart.defaults.global.defaultFontColor = 'grey';
Chart.defaults.global.defaultFontFamily = "Tahoma";
Chart.defaults.global.defaultFontSize = 11;
Chart.defaults.global.defaultFontStyle = 'normal';
var myChart = new Chart(ctx, {
type: 'line',
data: ordersChartData,
defaultFontSize: 11,
options: {
responsive: true,
title: {
display: true,
text: 'Intersection realization',
fontColor: "#444",
fontFamily: 'Tahoma',
padding: 0
},
legend: {
display: true,
labels: {
fontColor: 'grey',
usePointStyle: true
}
},
tooltips: {
mode: "index",
intersect: true,
position: 'nearest',
bodySpacing: 4
}
}
});
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = Y.top + ((Y.bottom - Y.top) / (Y.ticks.length - 1) * Y.zeroLineIndex);
zeroPointX = Y.right;
yScale = (Y.bottom - Y.top) / (Y.end - Y.start);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function(result, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result.x * xScale) + zeroPointX, (Y.end - Y.start) - (result.y * yScale) - ((Y.end - Y.start) - zeroPointY), 3, 0, 2 * Math.PI, true);
context.fill();
});
}
});
function findIntersects(line1, line2) {
var intersects = [];
line1.forEach(function(val, idx) {
var line1StartX = idx;
var line1StartY = line1[idx];
var line1EndX = idx + 1;
var line1EndY = line1[idx + 1];
var line2StartX = idx;
var line2StartY = line2[idx];
var line2EndX = idx + 1;
var line2EndY = line2[idx + 1];
result = checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY);
if (result.onLine1 && result.onLine2) {
intersects.push(result);
}
});
return intersects;
}
function checkLineIntersection(line1StartX, line1StartY, line1EndX, line1EndY, line2StartX, line2StartY, line2EndX, line2EndY) {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
var denominator, a, b, numerator1, numerator2, result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = ((line2EndY - line2StartY) * (line1EndX - line1StartX)) - ((line2EndX - line2StartX) * (line1EndY - line1StartY));
if (denominator == 0) {
return result;
}
a = line1StartY - line2StartY;
b = line1StartX - line2StartX;
numerator1 = ((line2EndX - line2StartX) * a) - ((line2EndY - line2StartY) * b);
numerator2 = ((line1EndX - line1StartX) * a) - ((line1EndY - line1StartY) * b);
a = numerator1 / denominator;
b = numerator2 / denominator;
// if we cast these lines infinitely in both directions, they intersect here:
result.x = line1StartX + (a * (line1EndX - line1StartX));
result.y = line1StartY + (a * (line1EndY - line1StartY));
// it is worth noting that this should be the same as:
x = line2StartX + (b * (line2EndX - line2StartX));
y = line2StartX + (b * (line2EndY - line2StartY));
// if line1 is a segment and line2 is infinite, they intersect if:
if (a > 0 && a < 1) {
result.onLine1 = true;
}
// if line2 is a segment and line1 is infinite, they intersect if:
if (b > 0 && b < 1) {
result.onLine2 = true;
}
// if line1 and line2 are segments, they intersect if both of the above are true
return result;
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="myChart" width="650" height="241" style="display: block; width: 650px; height: 241px;"></canvas>
它运行良好,但如果在这里:
"2016" : [0, 400010, 400110, 400110, 401000, 401000, 400100, 401000, 400001]
我在 300000
上更改了第一个值 0
,代码无法再显示交集。
我认为问题出在 Y.zeroLineIndex
。
我尝试了很多变体,记录了几乎所有的值并注意到当第一个值为零时(如示例),Y.zeroLineIndex
为 9。但是如果你将第一个值更改为 300000,它将变为 - 1.
在尝试检测问题并修复它数小时后,我不确定如何修复它。没有任何帮助。
我在 JS 方面不太好,所以请求帮助
编辑: [演示][1] [1]: https://i.stack.imgur.com/0t3Gu.png
我对你的问题进行了一些讨论,发现你在根据 Y-axis 设置高度时遇到了问题。使用此代码,您会发现它在任何情况下都有效。我对 context.arc 函数的 zeroPointY、yScale 和 Y 值进行了更改。
Chart.plugins.register({
afterDatasetsDraw: function (chartInstance, easing) {
var Y = chartInstance.scales['y-axis-0'];
var X = chartInstance.scales['x-axis-0'];
zeroPointY = (Y.bottom - Y.top)/(Y.ticks.length-1);
zeroPointX = Y.right;
yScale = (Y.end - Y.start)/ (Y.ticks.length - 1);
xScale = (X.right - X.left) / (X.ticks.length - 1);
console.log("aaa1", Y.top, Y.bottom, Y.ticks.length, Y.zeroLineIndex, zeroPointY);
console.log("aaa2", Y.bottom, Y.top, Y.end, Y.start, yScale);
var intersects = findIntersects(ORDER_STATS['Source'], ORDER_STATS['Moving average LONG']);
var context = chartInstance.chart.ctx;
intersects.forEach(function (result1, idx) {
context.fillStyle = 'red';
context.beginPath();
context.arc((result1.x * xScale) + zeroPointX, Y.top + (Y.end - result1.y)/yScale*zeroPointY, 3, 0, Math.PI * 2, true);
context.fill();
});
}
});