score:3
Here is how I fixed using both touchscreen or mouse event x,y coordinates for the excellent d3 example above by wrapping event screen coordinates in a more "generic" x,y object.
(Probably d3 has something similar to handle both types of events but lot of reading to find out..)
//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;
}
.. and here is how I would use it in the above d3 example to store the initial grab point Y. And works for both mouse and touch.
Check the Fiddle
Here how I solved the problem with using d3 and wanting to drag the document on mobile or touch screens. Somehow with the d3 event subscription all Chart area events where already blocked from bubbling up the DOM.
Was not able to figure out if d3 could be configured to pass canvas events on without touching them. So in a protest I just eliminated d3 as it was not much involved other than subscribing events.
Not being a Javascript master this is some fun code that subscribes the events the old way. To prevent chart touches from dragging the screen only when a chart point is grabed each of the handlers just have to return true and the event.preventDefault() is called to keep the event to your self.
//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);
});
}
};
The handler above is initiated like this with an existing Chart.js object:
chartEventHandler.init(chartAcTune);
The beginDrag(evt), duringDrag(evt) and endDrag(evt) have the same basic function as in the d3 example above. Just returns true when wanting to consume the event and not pasing it on for document panning and similar.
Try it in this Fiddle using a touch screen. Unless you touch close to select a chart point the rest of the chart will be transparent to touch/mouse events and allow panning the page.
score:10
Update: My previous answer got deleted because it only featured a link to a plugin solving the issue, however here comes the explanation to what it does:
The general procedure on how to achieve the desired behaviour is to
- Intercept a mousedown (and check if it's a dragging gesture) on a given chart
- Check if the mousedown was over a data point using the
getElementAtEvent
function - On mousemove, translate the new Y-Pixel value into a data coordinate using the
axis.getValueForPixel
function - Synchronously update the chart data using
chart.update(0)
as pointed out in this Chart.js issue.
In order to intercept the mousedown, mousemove and mouseup events (the dragging gesture), event listeners for said events need to be created. In order to simplify the creation of the listeners one may use the d3 library in this case as follows:
d3.select(chartInstance.chart.canvas).call(
d3.drag().container(chartInstance.chart.canvas)
.on('start', getElement)
.on('drag', updateData)
.on('end', callback)
);
On mousedown (the 'start'
event here), a function (getElement
) may be called thatfetches the closest chart element to the pointers location and gets the ID of the Y-Scale
function getElement () {
var e = d3.event.sourceEvent
element = chartInstance.getElementAtEvent(e)[0]
scale = element['_yScale'].id
}
On mousemove ('drag'
), the chart data is supposed to be updated according to the current Y-Pixel value of the pointer. We can therefore create an updateData
function that gets the position of the clicked data point in the charts data array and the according dataset like this
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)
}
And that's it! If you need to store the resulting value after dragging, you may also specify a callback
function like this
function callback () {
var datasetIndex = element['_datasetIndex']
var index = element['_index']
var value = chartInstance.data.datasets[datasetIndex].data[index]
// e.g. store value in database
}
Here is a working fiddle of the above code. The functionality is also the core of the Chart.js Plugin dragData, which may be easier to implement in many cases.
score:12
In case anyone is looking for a solution that doesn't require the use of plugins, it's pretty straightforward to do it in vanilla chart.js
.
Here's a simple working example - just click and drag a data point
// 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>
Source: stackoverflow.com
Related Query
- Chart.js drag points on linear chart
- Chart JS show multiple data points for the same label
- ChartJs line chart - display permanent icon above some data points with text on hover
- Chart.js - Bar chart with linear cartesian (numerical) X axis?
- ChartJS - handling of overlapping points in line chart
- ChartJS line chart drag and zoom
- ChartJS: create linear x-axis on bar chart
- High and low points on chart getting cut off
- Is it possible to make points in a line chart of Chart JS 3.7.0 look like a donut?
- How can I achieve displaying data points on chart but Dotted? Chart.js V3.7.0
- add info for points in line chart (js)
- How to get chart from data points (arrays) with inconsistent time intervals and chart.js?
- Chart JS pass in custom data for points
- I have 10 points inn x-axis, ranging [-25,-20,,-15,-10,0,10,20,30,40,50]. But I want my line chart to start from -15 of x-axis. How we can achieve?
- Chart.js -> line chart -> multiple points with the same X
- how can i use chart.js to create a chart that has one time series line and one linear line on it at the same time?
- How to reduce the number of points shown on line chartjs chart with a large dataset?
- Chart.js 3.5: linear gradient doesn't apply properly when multiple bars in the same chart
- Linear x axis for bar chart chartjs
- Issue with chartjs linear gradient for the mixed bar chart in ReactJS is not calculated for each individual Bars
- Draw points and lines inside a bar in bar chart
- Plotting jqplot points to a chart.js chart
- ChartJs(Java) Can I remove only specific gridLines for a Linear Chart withouth removing the labels on xAxis?
- Chart.js combined line and bar chart with differing data points
- what is wrong with my code ? java script chart position
- How to print a chart rendered by code
- chart js draw line chart by points instead of by lines
- Bar chart (chart.js) with only 2 points does not show one of the bars
- Chart Js - Limit points on graph without limiting data
- How to plot a line chart which has both the start and points outside the canvas area using Chart.js
More Query from same tag
- ChartJs - Round borders on a doughnut chart with multiple datasets
- Change legend style from bar to line chart.js 2.4.0
- Show center tick value radar chart- Chart.js
- TypeError: Cannot read property 'defaults' of undefined when using the react wrapper of chartjs
- Difficulty in using php json_encode data received in javascript to display graph using chart.js library
- Displaying JSON data in Chartjs
- ChartJS Datalabels plugin and negative numbers
- How to add custom tooltips to only one label in Chart.js?
- ng2-charts & chart.js - Change background color
- Change label color Y and X axis chart.js
- Adding object data to data structure of chart.js chart does not show any data
- Multiple Charts of the Same Type in ChartJS2 ReactJS - Buggy Tooltips
- Chart.js Bar Chart - how to chart bars from 0
- How to set min Value a step down of min value received from database in Y Axis in ChartJS
- multiple charts js in page but with same tooltips (about last chart)
- Chartjs: Is it possible to hide the data labels on the axis but show up on the graph on hover?
- Change color of one value in the x-axis charts.js
- chartjs is issue with large amount data
- how to update chartjs2 option (scale.tick.max)
- To remove error message when user clicked on legend label
- Maintain Y axis scale in graph with scroll in the X axis when the dataset grows bigger. Aspect-ratio problem
- 'Chart.js' time chart not displaying properly
- Utils package in Chart.js
- How to set vertical lines for new day on x-axis in ChartJS v3.x
- How to stack time charts vertically
- Load data from Google Analytics in a Chart.js chart
- Change font size and font color in Chartjs Angular 5
- Chart.js: Minimum value for x-axis at horizontal stacked bar chart
- Equal distance grouped bar chart irrespective of scale - chart.js
- How to use Angular-Charts JS?