HOWTO: Create customised adjusting for the Line chart
- 1. The code that creates the chart
- 2. The canvas mousedown event
- 3. The closest() function
- 4. The grow() function
- All of the code put together
The Line chart adjusting can be very useful - but it's not terribly functional on touch-based devices (eg mobiles and tablets). This is because of the requirement to drag a point on the Line to its new location - and dragging isn't terribly intuitive or feasible in these environments.
But the Line chart can still be adjusted in these environments as this example shows you. Custom coding is needed - but as it's all provided to you here it's just a matter of copying and pasting! There's also an example in the download archive that you can examine that provides you with an example of it in action. And you can find that file in the examples folder. But first let's have a look at the code that goes into making this page.
1. The code that creates the chart
<script> // The data for the chart data = [5,14,2,4,1,8,6,5,9,4]; // Create and configure the chart line = new RGraph.Line({ id: 'cvs', data: data, options: { yaxisScaleMax: 20, marginInner: 10, tickmarksStyle: 'circle', xaxisLabels: ['Fred', 'John', 'Kev', 'Lou', 'Pete','Mark','Neil','Indra','Lewis','Bob'], axesColor: '#aaa', yaxisTickmarksCount: 5, xaxis: false, backgroundGridVlines: false, backgroundGridBorder: false, labelsAbove: true, labelsAboveBorder: false, labelsAboveDecimals: 1, labelsAboveSize: 10, textAccessible: false } }).draw(); </script>
So this is the code that goes into making the chart. Nothing should particularly strike you as being massively different but a few things are worthy of note:
-
The
yaxisScaleMax
property has been set so that when you adjust the line the scale definitely won't change. If you didn't set this and the points were moved down below a certain level the scale would change (eg from a maximum value of 20 to a maximum value of 15 - then ten - then 5 etc). -
The
tickmarksStyle
property has been set tocircle
. This is to make it easier for the user to see where the 'grab points' are so they can move them up or down.
2. The canvas mousedown event
<script> // // This is the function that handles adjusting the line when // the chart is touched. // line.canvas.onmousedown = function(e) { var obj = e.target.__object__, newvalue = obj.getValue(e), mouseXY = RGraph.getMouseXY(e), mouseX = mouseXY[0], mouseY = mouseXY[1], coords = obj.coords2[0]; // Determine the closest point to the touch/click This function is defined below var index = closest({ coords: coords, mousex: mouseX, tolerance: 10 // Pixels, default is 10 }); if (index >= 0) { data[index] = newvalue; grow({ object: obj, index: index, value: newvalue }) } }; </script>
This is the canvas mousedown
event that's run whenever the mouse is clicked
on the canvas. Here's a description: It gets the mouse coordinates of
the click and using those works out the closest Line chart point based on the
X position (this is what the closest()
function does). The tolerance
allows a certain amount of leeway on either side of the
point so that you don't have to be exact when touching the chart. Remember
that touching with a finger is far less accurate than clicking with a mouse.
If there is an index (of a point) returned - not null - then that point is
animated to its new position by the grow()
function.
3. The closest() function
<script> // // Finds the closest point to the given mouseX coordinate. It allows a // tolerance of 10 (or so) pixels. // // @param object opt An object consisting of; // o coords The coordinates of the points // o mousex The mouseX coordinate // o tolerance The number of pixels leeway // that is allowed. Default is 10 // function closest (opt) { var coords = opt.coords, mouseX = opt.mousex, tolerance = (typeof opt.tolerance === 'number' ? opt.tolerance : 10), point = null; // Loop through the coordinates looking for the closest // (going by X coordinate) for (var i=0,distance = null; i<coords.length; ++i) { if (mouseX > coords[i][0] - tolerance && mouseX < coords[i][0] + tolerance) { point = i; } } return point; } </script>
This is the function that's used to determine the closest point to the mouse cursor - if any. It simply loops through the object's coordinates and compares the X coordinate to the mouse X position. It uses the tolerance (which by default is 10) to allow a little leeway on each side. If it finds a point it returns the index of it.
4. The grow() function
<script> // // The animation function that makes the point grow to // its new position. // function grow (opt) { var obj = opt.object, idx = opt.index, value = opt.value; // Determine the original value or the point that's being adjusted var original_value = line.original_data[0][idx]; var frames = 15, delay = 16.666; // milliseconds for (var i=0; i<frames; i++) { (function (i) { setTimeout(function () { line.original_data[0][idx] = ((value - original_value) * (i + 1) / frames) + original_value; // Update this so that the above labels are correctly updated line.data_arr = RGraph.arrayLinearize(line.original_data); RGraph.redraw(); }, delay * i); })(i) } } </script>
This is the animation function that makes the point move to the desired new position. Each frame is done by updating the value in the chart and then redrawing the chart. A line that's worthy of note is this:
line.data_arr = RGraph.arrayLinearize(line.original_data);This is not normally necessary however is if you use the
labelsAbove
option - which this chart does. This option uses the data_arr
property
so if it wasn't updated then the values displayed would be incorrect (this
variable is initially set in the constructor - which isn't being repeatedly
called on every frame).
All of the code put together
Here's the code put together. You can see an example by
looking at the line-adjustable-touch-event.html
example
(which is included in the download archive). If
you need the example and are using a prior version of RGraph - simply view-source:
on this page and copy it to your work environment.
<script> // The data for the chart var data = [5,14,2,4,1,8,6,5,9,4]; // Create and configure the chart var line = new RGraph.Line({ id: 'cvs', data: data, options: { yaxisScaleMax: 20, marginInner: 10, tickmarksStyle: 'circle', xaxisLabels: ['Fred', 'John', 'Kev', 'Lou', 'Pete','Mark','Neil','Indra','Lewis','Bob'], axesColor: '#aaa', yaxisTickmarksCount: 5, xaxis: false, backgroundGridVlines: false, backgroundGridBorder: false, labelsAbove: true, labelsAboveBorder: false, labelsAboveDecimals: 1, textAccessible: false } }).draw(); // // This is the function that handles adjusting the line when // the chart is touched. // line.canvas.onmousedown = function(e) { var obj = e.target.__object__, newvalue = obj.getValue(e), mouseXY = RGraph.getMouseXY(e), mouseX = mouseXY[0], mouseY = mouseXY[1], coords = obj.coords2[0]; // Determine the closest point to the touch/click var index = closest({ coords: coords, mousex: mouseX, tolerance: 10 // Pixels, default is 10 }); if (index >= 0) { data[index] = newvalue; grow({ object: obj, index: index, value: newvalue }) } }; // // Finds the closest point to the given mouseX coordinate. It allows a // tolerance of 10 (or so) pixels. // // @param object opt An object consisting of; // o coords The coordinates of the points // o mousex The mouseX coordinate // o tolerance The number of pixels leeway // that is allowed. Default is 10 // function closest (opt) { var coords = opt.coords, mouseX = opt.mousex, tolerance = (typeof opt.tolerance === 'number' ? opt.tolerance : 10), point = null; // Loop through the coordinates looking for the closest // (going by X coordinate) for (var i=0,distance = null; i<coords.length; ++i) { if (mouseX > coords[i][0] - tolerance && mouseX < coords[i][0] + tolerance) { point = i; } } return point; } // // The animation function that makes the point grow to // its new position. // function grow (opt) { var obj = opt.object, idx = opt.index, value = opt.value; // Determine the original value or the point that's being adjusted var original_value = line.original_data[0][idx]; var frames = 15, delay = 16.666; for (var i=0; i<frames; i++) { (function (i) { setTimeout(function () { line.original_data[0][idx] = ((value - original_value) * (i + 1) / frames) + original_value; // Update this so that the above labels are correctly updated line.data_arr = RGraph.arrayLinearize(line.original_data); RGraph.redraw(); }, delay * i); })(i) } } </script>