I have the following graph draw from array data. I want to use plain javascript, no libraries.
How could I curve this so its not straight lines? Basically I am trying to replicate youtube video heatmap graph (most viewable times through video). Nothing fancy, just so its curved.
I am not sure if getMaxY function is accurate.
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext("2d");
var values = [10, 22, 64, 9, 97, 5, 11, 45, 33, 15, 55, 19];
var width = 600;
var height = 50;
c.strokeRect(0, 0, width, height)
c.beginPath();
c.moveTo(0, height);
c.lineTo(getXPixel(0), getYPixel(values[0]));
for(var i = 1; i < values.length; i ++) {
c.lineTo(getXPixel(i), getYPixel(values[i]));
}
c.lineTo(width, height);
c.lineTo(0, height);
c.fillStyle = "rgba(44, 44, 44, 0.2)";
c.fill();
function getMaxY() {
var max = 0;
for(var i = 0; i < values.length; i ++) {
if(values[i] > max) {
max = values[i];
}
}
max += 10 - max % 10;
return max;
}
function getXPixel(val) {
return (width / (values.length-1)) * val;
}
function getYPixel(val) {
return height - ((height / getMaxY()) * val);
}
<canvas id="myCanvas" width="600" height="50" ></canvas>
You can use ctx.quadraticCurveTo
function with a "look forward" control point to make the line curvy.
UPDATE: I added the previous line for comparison, we can see it's not very precise. I'll experiment with another function. Maybe bezierCurveTo
will be better.
UPDATE 2: My lazy solution for a better precision is to use more points (by adding the averages between each points)
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext("2d");
var values = [10, 22, 64, 9, 97, 5, 11, 45, 33, 15, 55, 19];
var width = canvas.width;
var height = canvas.height;
// add more points for better precision
var newValues = [values[0]]
for (var i = 1; i < values.length; i++) {
newValues.push((values[i - 1] + values[i]) / 2)
newValues.push(values[i])
}
values = newValues
c.strokeRect(0, 0, width, height)
c.beginPath();
c.moveTo(0, height);
c.lineTo(getXPixel(0), getYPixel(values[0]));
for (var i = 1; i < values.length - 2; i++) {
const xc = (getXPixel(i + 1) + getXPixel(i)) / 2;
const yc = (getYPixel(values[i + 1]) + getYPixel(values[i])) / 2;
c.quadraticCurveTo(getXPixel(i), getYPixel(values[i]), xc, yc);
}
// Draw the last segment
c.quadraticCurveTo(
getXPixel(values.length - 2),
getYPixel(values[values.length - 2]),
getXPixel(values.length - 1),
getYPixel(values[values.length - 1])
);
c.lineTo(width, height);
c.lineTo(0, height);
c.fillStyle = "rgba(244, 0, 0, 0.82)";
c.fill();
c.closePath()
// old line code, for comparison
c.beginPath();
c.moveTo(getXPixel(0), getYPixel(values[0]));
for (var i = 1; i < values.length; i++) {
c.lineTo(getXPixel(i), getYPixel(values[i]));
}
c.stroke()
c.closePath()
function getMaxY() {
var max = 0;
for (var i = 0; i < values.length; i++) {
if (values[i] > max) {
max = values[i];
}
}
max += 10 - max % 10;
return max;
}
function getXPixel(val) {
return (width / (values.length - 1)) * val;
}
function getYPixel(val) {
return height - ((height / getMaxY()) * val);
}
#myCanvas {
background: gray;
}
<canvas id="myCanvas" width="600" height="150"></canvas>