TypeScripter
TypeScripter

Reputation: 909

HTML Canvas: How to color area under a line graph?

I am trying to create a mountain graph (line chart and area underneath is shaded) however, no matter what I try, the shaded area is not covering the whole area. Because my chart is an open path so fill results in area which is going through the chart line.

enter image description here

Below is a sample code that I put on W3School to show the problem.

I also saw some other questions on same lines, but following them too is resulting in same problem.

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(0,150);
ctx.lineTo(100,70);
ctx.lineTo(150,100);
ctx.lineTo(200,140);
ctx.lineTo(250,90);
ctx.lineTo(300,110);
ctx.fillStyle ="red";
ctx.fill();
ctx.stroke();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>

Upvotes: 1

Views: 3302

Answers (2)

Kaiido
Kaiido

Reputation: 136638

You just have to stroke() your path first, then lineTo(lastX, canvasHeight); lineTo(firstX, canvasHeight); before calling fill().

This way your filled area will always cover all the bottom area.
If you want to fill only to the max Y value, instead of to the bottom of the canvas, then you can grab this maxY from your points (commented part in below snippet):

const width = canvas.width;
const height = canvas.height;
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'red';

function plotPoints() {
  const pts = generatePoints(32);
  // first plot the stroke
  pts.forEach((pt) => ctx.lineTo(pt.x, pt.y));
  ctx.stroke();
  // now define the bottom of the filled area
  const maxY = height; //Math.max.apply(null, pts.map(pt=>pt.y));
  // draw the missing parts
  ctx.lineTo(pts[pts.length - 1].x, maxY); // bottom-right
  ctx.lineTo(pts[0].x, maxY); // bottom-left

  ctx.globalCompositeOperation = "destination-over"; // draw behind
  ctx.fill(); // will close the path for us
  ctx.globalCompositeOperation = "source-over"; // normal behavior

}
plotPoints();

function generatePoints(nbOfPoints) {
  const pts = [];
  for (let i = 0; i <= nbOfPoints; i++) {
    pts.push({
      x: i * (width / nbOfPoints),
      y: Math.random() * height
    });
  }
  return pts;
}
canvas {
  border: 1px solid lightgray;
}
<canvas id="canvas"></canvas>

Upvotes: 10

StackSlave
StackSlave

Reputation: 10627

It's like:

//<![CDATA[
/* external.js */
var doc, bod, E; // for use on other loads
addEventListener('load', function(){
doc = document, bod = doc.body;
E = function(id){
  return doc.getElementById(id);
}
var graph = E('graph'), ctx = graph.getContext('2d');
ctx.beginPath(); ctx.moveTo(0,150); ctx.lineTo(100,70);
ctx.lineTo(150,100); ctx.lineTo(200,140); ctx.lineTo(250,90);
ctx.lineTo(300,110); ctx.lineTo(300,110); ctx.lineTo(300,150);
ctx.fillStyle = 'red'; ctx.fill(); ctx.stroke();
});
//]]>
/* external.css */
html,body{
  padding:0; margin:0;
}
.main{
  width:940px; padding:20px; margin:0 auto;
}
#graph{
  width:300px; height:150px;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
  <head>
    <meta http-equiv='content-type' content='text/html;charset=utf-8' />
    <meta name='viewport' content='width=device-width' />
    <title>Graph</title>
    <link type='text/css' rel='stylesheet' href='css/external.css' />
    <script type='text/javascript' src='external.js'></script>
  </head>
  <body>
    <div class='main'>
      <canvas id='graph'></canvas>
    </div>
  </body>
</html>

Upvotes: -1

Related Questions