我已经实现了图表js来绘制由红线和蓝线指示的两条曲线。我想添加一个滑块(黑色垂直线),以便它始终保持在两条曲线内,并且用户可以将其向左和向右移动。当用户向左或向右滑动它时,它会自行调整高度。因此,获取下图的 x 和 y 值,并通过减去第一个图的 y1 和第二个图的 y2 来设置高度,它的行为符合预期。 这是我的代码,
// Initialize the draggable line
$(function() {
$("#draggable").draggable({
axis: "x",
containment: "#myChart",
drag: function(event, ui) {
const canvas = document.getElementById('myChart');
const ctx = canvas.getContext('2d');
const rect = canvas.getBoundingClientRect();
const chartLeft = rect.left;
const xPos = ui.position.left; // Position of the draggable line
const xValue = myChart.scales.x.getValueForPixel(xPos); // X value on the chart
// Find the nearest points on the datasets
const pYValue = getYValueAtX(xValue, myChart.data.datasets[0].data);
const sYValue = getYValueAtX(xValue, myChart.data.datasets[1].data);
const difference = pYValue && sYValue ? (pYValue - sYValue) : null;
// Update the tooltip with the current x, p, and s values
const tooltip = document.getElementById('tooltip');
tooltip.innerHTML = `X: ${xValue.toFixed(2)}<br>P: ${pYValue ? pYValue.toFixed(2) : 'N/A'}<br>S: ${sYValue ? sYValue.toFixed(2) : 'N/A'}<br>Difference: ${difference ? difference.toFixed(2) : 'N/A'}`;
tooltip.style.display = 'block';
tooltip.style.left = `${xPos + chartLeft + 10}px`;
tooltip.style.top = `${rect.top + 10}px`;
const xPixelPos = myChart.scales.x.getPixelForValue(xValue); // Get pixel for xValue
const yPixelPos = myChart.scales.y.getPixelForValue(pYValue); // Get pixel for sYValue
const y1PixelPos = myChart.scales.y.getPixelForValue(sYValue); // Get pixel for sYValue
const height = Math.abs(yPixelPos - y1PixelPos);
const blackLine = document.getElementById('draggable');
blackLine.style.left = `${xPixelPos}px`; // Set the x position of the div
blackLine.style.top = `${yPixelPos}px`;
blackLine.style.height = `${height}px`;
console.log("xpixel:", xPixelPos, "ypixel:", yPixelPos, "y1pixel:", y1PixelPos, "height:", height);
draggableElement.style.height = `${newHeight}px`; // Set height
}
});
});
// Helper function to find Y value for a given X in the dataset
function getYValueAtX(x, data) {
// Find the nearest point in the data for the given x
const point = data.find(p => p.x >= x);
return point ? point.y : null;
}
function interpolateData(data) {
// Create arrays to store the new interpolated p and s values
let interpolatedData = [];
for (let i = 0; i < data.length; i++) {
const currentPoint = data[i];
const nextPoint = data[i + 1];
// Check if "p" or "s" is missing and interpolate if necessary
if (currentPoint.p === "" && nextPoint) {
// Linear interpolation for 'p'
const prevPoint = data[i - 1];
if (prevPoint && nextPoint.p !== "") {
currentPoint.p = prevPoint.p + ((nextPoint.x - prevPoint.x) * (nextPoint.p - prevPoint.p)) / (nextPoint.x - prevPoint.x);
}
}
if (currentPoint.s === "" && nextPoint) {
// Linear interpolation for 's'
const prevPoint = data[i - 1];
if (prevPoint && nextPoint.s !== "") {
currentPoint.s = prevPoint.s + ((nextPoint.x - prevPoint.x) * (nextPoint.s - prevPoint.s)) / (nextPoint.x - prevPoint.x);
}
}
// Push the currentPoint to the interpolatedData
interpolatedData.push(currentPoint);
}
return interpolatedData;
}
// AJAX function to fetch JSON data
function fetchJSONFile(filePath, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', filePath, true);
xhr.responseType = 'json';
xhr.onload = function() {
if (xhr.status === 200) {
const interpolatedData = interpolateData(xhr.response);
callback(interpolatedData);
} else {
console.error('Failed to load JSON file.');
}
};
xhr.send();
}
// Callback to process the data and plot the chart
function plotChart(jsonData) {
const pData = jsonData
.filter(item => item.p !== "")
.map(item => ({
x: item.x,
y: item.p
}));
const sData = jsonData
.filter(item => item.s !== "")
.map(item => ({
x: item.x,
y: item.s
}));
// Chart.js configuration
const ctx = document.getElementById('myChart').getContext('2d');
myChart = new Chart(ctx, {
type: 'line',
data: {
datasets: [
{
label: 'p Values',
data: pData,
borderColor: 'blue',
fill: false,
tension: 0.1,
pointRadius: 0,
showLine: true
},
{
label: 's Values',
data: sData,
borderColor: 'red',
fill: false,
tension: 0.1,
pointRadius: 0,
showLine: true
}
]
},
options: {
scales: {
x: {
type: 'linear',
position: 'bottom',
title: {
display: true,
text: 'X Axis'
}
},
y: {
title: {
display: true,
text: 'Y Axis'
}
}
}
}
});
}
// Fetch and plot the chart using AJAX
fetchJSONFile('https://www.sagarrawal.com.np/csvjson.json', plotChart);
#chart-container {
width: 50%;
height: 90%;
position: relative;
}
canvas {
background-color: white;
}
/* Draggable vertical line */
#draggable {
position: absolute;
width: 2px;
height: 100%;
background-color: black;
z-index: 10;
cursor: pointer;
}
/* Tooltip to show values */
#tooltip {
position: absolute;
background-color: rgba(0, 0, 0, 0.75);
color: white;
padding: 5px;
border-radius: 3px;
font-size: 12px;
display: none;
z-index: 20;
}
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<div id="chart-container">
<div id="draggable"></div>
<canvas id="myChart"></canvas>
<div id="tooltip"></div> <!-- Tooltip to display values -->
</div>
${newHeight}px
;' ,这是可以理解的,因为它没有在代码中的任何地方定义,所以当我删除该线时,黑线滑块就会出现在两个图之外的图的顶部。但只有保留它,绘图才会按预期运行,并且当拖动黑线时会出现在两个绘图中。因此,虽然我按照我想要的方式得到结果,但我无法理解为什么删除上面的行,我的图表无法按预期工作。
首先,您可以将导致错误的行替换为更有意义的内容,例如
throw 'myError'
。无论如何,此时会发生错误
阻止 jquery-ui
'drag'
事件处理程序的正常完成。
将代码调试到
jquery-ui
显示用户处理程序(您的 drag
函数)
在这一行被调用:
if ( this._trigger( "drag", event, ui ) === false ) {.....
如果那里发生错误,则不会执行这一行:
this.helper[ 0 ].style.left = this.position.left + "px";
this.helper[ 0 ].style.top = this.position.top + "px";
第二行是将可拖动元素重新定位在顶部的行。
这提供了干净的解决方案 - 将产生错误的行替换为:
ui.position.top = yPixelPos;
您实际上可以将涉及
const blackLine = document.getElementById('draggable')
的所有四行替换为:
ui.position.top = yPixelPos;
ui.position.left = xPixelPos;
ui.helper[0].style.height = `${height}px`;