我有一个使用Chart.js库构建的简单线性图表。
我希望允许用户在图表上拖动点以动态更改它的数据。我绑了chartjs-plugin-draggable,但它只适用于注释。我需要这样的图形:
https://www.rgraph.net/canvas/docs/adjusting-line.html
但是在项目中使用新的图形库并不是一个好的解决方案:(
我也试着玩点活动。
更新:
也许如果没有办法添加拖放到点数,那么将会有一个黑客将“滑块”放在点位置上的图表上的绝对位置。我也没有找到任何信息:(
答案 0 :(得分:4)
更新:我之前的回答被删除了,因为它只是一个解决问题的插件的链接,但是这里有解释它的作用:
如何实现所需行为的一般程序是
getElementAtEvent
函数axis.getValueForPixel
函数将新的Y-Pixel值转换为数据坐标chart.update(0)
正如本Chart.js issue所指出的那样。
为了拦截mousedown,mousemove和mouseup事件(拖动手势),需要创建所述事件的事件侦听器。为了简化监听器的创建,在这种情况下可以使用d3 library,如下所示:
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', getElement)
.on('drag', updateData)
.on('end', callback)
);
在mousedown(此处为'start'
事件)上,可以调用函数(getElement
),将最接近的图表元素提取到指针位置并获取Y-Scale的ID
function getElement () {
var e = d3.event.sourceEvent
element = chartInstance.getElementAtEvent(e)[0]
scale = element['_yScale'].id
}
在mousemove('drag'
)上,应该根据指针的当前Y-Pixel值更新图表数据。因此,我们可以创建一个updateData
函数来获取图表数据数组中点击的数据点的位置以及像这样的相应数据集
function updateData () {
var e = d3.event.sourceEvent
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.scales[scale].getValueForPixel(e.clientY)
chartInstance.data.datasets[datasetIndex].data[index] = value
chartInstance.update(0)
}
那就是它!如果您需要在拖动后存储结果值,您还可以指定callback
这样的函数
function callback () {
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.data.datasets[datasetIndex].data[index]
// e.g. store value in database
}
以上是上述代码的working fiddle。该功能也是Chart.js Plugin dragData的核心,在许多情况下可能更容易实现。
答案 1 :(得分:2)
以下是我通过将事件屏幕坐标包装在更多"泛型"中来固定使用上述优秀d3示例的触摸屏或鼠标事件x,y坐标的方法。 x,y对象。
(可能d3类似于处理这两种类型的事件但需要大量阅读才能找到...)
//Get an class of {points: [{x, y},], type: event.type} clicked or touched
function getEventPoints(event)
{
var retval = {point: [], type: event.type};
//Get x,y of mouse point or touch event
if (event.type.startsWith("touch")) {
//Return x,y of one or more touches
//Note 'changedTouches' has missing iterators and can not be iterated with forEach
for (var i = 0; i < event.changedTouches.length; i++) {
var touch = event.changedTouches.item(i);
retval.point.push({ x: touch.clientX, y: touch.clientY })
}
}
else if (event.type.startsWith("mouse")) {
//Return x,y of mouse event
retval.point.push({ x: event.layerX, y: event.layerY })
}
return retval;
}
..以下是我将如何在上面的d3示例中使用它来存储初始抓点Y.并且适用于鼠标和触摸。
检查 Fiddle
这里我是如何解决使用d3并希望在移动设备或触摸屏上拖动文档的问题。不知何故,对于d3事件订阅,所有Chart区域事件都已被阻止冒泡DOM。
无法确定d3是否可以配置为在不触摸它的情况下传递画布事件。因此,在抗议活动中,我刚刚删除了d3,因为除了订阅活动之外没有太多参与。
不是Javascript大师这是一些有趣的代码,以旧的方式订阅事件。为了防止图表触摸仅在绘制图表点时拖动屏幕,每个处理程序只需返回 true 并调用 event.preventDefault()以保持事发给你自己。
//ChartJs event handler attaching events to chart canvas
const chartEventHandler = {
//Call init with a ChartJs Chart instance to apply mouse and touch events to its canvas.
init(chartInstance) {
//Event handler for event types subscribed
var evtHandler =
function myeventHandler(evt) {
var cancel = false;
switch (evt.type) {
case "mousedown":
case "touchstart":
cancel = beginDrag(evt);
break;
case "mousemove":
case "touchmove":
cancel = duringDrag(evt);
break;
case "mouseup":
case "touchend":
cancel = endDrag(evt);
break;
default:
//handleDefault(evt);
}
if (cancel) {
//Prevent the event e from bubbling up the DOM
if (evt.cancelable) {
if (evt.preventDefault) {
evt.preventDefault();
}
if (evt.cancelBubble != null) {
evt.cancelBubble = true;
}
}
}
};
//Events to subscribe to
var events = ['mousedown', 'touchstart', 'mousemove', 'touchmove', 'mouseup', 'touchend'];
//Subscribe events
events.forEach(function (evtName) {
chartInstance.canvas.addEventListener(evtName, evtHandler);
});
}
};
上面的处理程序与现有的Chart.js对象一样启动:
chartEventHandler.init(chartAcTune);
beginDrag(evt), duringDrag(evt)和 endDrag(evt)具有与上述d3示例相同的基本功能。只想在使用事件时返回true,而不是为了文档平移和类似事件而使用它。
使用触摸屏在此Fiddle中尝试使用它。除非您触摸选择图表点,否则图表的其余部分对触摸/鼠标事件将是透明的,并允许平移页面。
答案 2 :(得分:0)
万一有人在寻找不需要使用插件的解决方案,那么在原始chart.js
上这样做很简单。
这是一个简单的工作示例-只需单击并拖动数据点
// some data to be plotted
var x_data = [1500,1600,1700,1750,1800,1850,1900,1950,1999,2050];
var y_data_1 = [86,114,106,106,107,111,133,221,783,2478];
var y_data_2 = [2000,700,200,100,100,100,100,50,25,0];
// globals
var activePoint = null;
var canvas = null;
// draw a line chart on the canvas context
window.onload = function () {
// Draw a line chart with two data sets
var ctx = document.getElementById("canvas").getContext("2d");
canvas = document.getElementById("canvas");
window.myChart = Chart.Line(ctx, {
data: {
labels: x_data,
datasets: [
{
data: y_data_1,
label: "Data 1",
borderColor: "#3e95cd",
fill: false
},
{
data: y_data_2,
label: "Data 2",
borderColor: "#cd953e",
fill: false
}
]
},
options: {
animation: {
duration: 0
},
tooltips: {
mode: 'nearest'
}
}
});
// set pointer event handlers for canvas element
canvas.onpointerdown = down_handler;
canvas.onpointerup = up_handler;
canvas.onpointermove = null;
};
function down_handler(event) {
// check for data point near event location
const points = window.myChart.getElementAtEvent(event, {intersect: false});
if (points.length > 0) {
// grab nearest point, start dragging
activePoint = points[0];
canvas.onpointermove = move_handler;
};
};
function up_handler(event) {
// release grabbed point, stop dragging
activePoint = null;
canvas.onpointermove = null;
};
function move_handler(event)
{
// locate grabbed point in chart data
if (activePoint != null) {
var data = activePoint._chart.data;
var datasetIndex = activePoint._datasetIndex;
// read mouse position
const helpers = Chart.helpers;
var position = helpers.getRelativePosition(event, myChart);
// convert mouse position to chart y axis value
var chartArea = window.myChart.chartArea;
var yAxis = window.myChart.scales["y-axis-0"];
var yValue = map(position.y, chartArea.bottom, chartArea.top, yAxis.min, yAxis.max);
// update y value of active data point
data.datasets[datasetIndex].data[activePoint._index] = yValue;
window.myChart.update();
};
};
// map value to other coordinate system
function map(value, start1, stop1, start2, stop2) {
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1))
};
body {
font-family: Helvetica Neue, Arial, sans-serif;
text-align: center;
}
.wrapper {
max-width: 800px;
margin: 50px auto;
}
h1 {
font-weight: 200;
font-size: 3em;
margin: 0 0 0.1em 0;
}
h2 {
font-weight: 200;
font-size: 0.9em;
margin: 0 0 50px;
color: #555;
}
a {
margin-top: 50px;
display: block;
color: #3e95cd;
}
<!DOCTYPE html>
<html>
<!-- HEAD element: load the stylesheet and the chart.js library -->
<head>
<title>Draggable Points</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<!-- BODY element: create a canvas and render a chart on it -->
<body>
<!-- canvas element in a container -->
<div class="wrapper">
<canvas id="canvas" width="1600" height="900"></canvas>
</div>
<!-- call external script to create and render a chart on the canvas -->
<script src="script.js"></script>
</body>
</html>