Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Continuous gradient along a HTML5 canvas path

I am trying to draw a continous gradient along a path of points, where each point has a it's own color, using the HTML5 canvas API.

See http://bl.ocks.org/rveciana/10743959 for inspiration, where that effect is achieved with D3.

There doesn't seem to be a way to add multiple linear gradients for a single canvas path, so I resorted to something like this: http://jsfiddle.net/51toapv2/

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var pts = [[100, 100, "red"], [150, 150, "green"], [200, 100, "yellow"]];

ctx.lineWidth = 20;
ctx.lineJoin = "round";
ctx.lineCap = "round";

for (var i = 0; i < pts.length - 1; i++) {
    var begin = pts[i];
    var end = pts[i + 1];

    ctx.beginPath();
    var grad = ctx.createLinearGradient(begin[0], begin[1], end[0], end[1]);
    grad.addColorStop(0, begin[2]);
    grad.addColorStop(1, end[2]);
    ctx.strokeStyle = grad;
    ctx.moveTo(begin[0], begin[1]);
    ctx.lineTo(end[0], end[1]);
    ctx.stroke();
}

As you can see it produces a subpar effect as the paths aren't merged and the "line joins" are clearly visible.

Is it possible to achieve the effect I'm looking for with the canvas API?

like image 554
SiimKallas Avatar asked Jun 03 '15 21:06

SiimKallas


People also ask

How do you make a radial gradient in HTML5 canvas?

The createRadialGradient() method creates a radial/circular gradient object. The gradient can be used to fill rectangles, circles, lines, text, etc. Tip: Use this object as the value to the strokeStyle or fillStyle properties.

Can a canvas tag contain a gradient shape?

In HTML5 canvas, you can fill or stroke shapes and text using stroke() or fill() method. When we draw on text or shapes it uses the current stroke or fill style. The stroke or fill style can be set to a color, a pattern, or a gradient.

What method is used to create a linear gradient object in HTML5?

The createLinearGradient() method creates a linear gradient object. The gradient can be used to fill rectangles, circles, lines, text, etc.


1 Answers

Here's a slight modification of your original idea that makes the joins blend nicely.

enter image description here

Original: Draw a gradient line from the start to end of a line segment.

This causes the line joins to overlap and produces a noticeable & undesired transition.

enter image description here

Modification: Draw a gradient line that doesn't extend to the start / endpoints.

With this modification, the line joins will always be solid colors rather than be partially gradiented. As a result, the line joins will transition nicely between line segments.

enter image description here

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

var lines = [
  {x:100, y:050,color:'red'},
  {x:150, y:100,color:'green'},
  {x:200, y:050,color:'gold'},
  {x:275, y:150,color:'blue'}
];
var linewidth=20;

ctx.lineCap='round';
ctx.lineJoint='round';

for(var i=1;i<lines.length;i++){

  // calculate the smaller part of the line segment over
  //     which the gradient will run
  var p0=lines[i-1];
  var p1=lines[i];
  var dx=p1.x-p0.x;
  var dy=p1.y-p0.y;
  var angle=Math.atan2(dy,dx);
  var p0x=p0.x+linewidth*Math.cos(angle);
  var p0y=p0.y+linewidth*Math.sin(angle);
  var p1x=p1.x+linewidth*Math.cos(angle+Math.PI);
  var p1y=p1.y+linewidth*Math.sin(angle+Math.PI);

  // determine where the gradient starts and ends
  if(i==1){
    var g=ctx.createLinearGradient(p0.x,p0.y,p1x,p1y);   
  }else if(i==lines.length-1){
    var g=ctx.createLinearGradient(p0x,p0y,p1.x,p1.y);
  }else{
    var g=ctx.createLinearGradient(p0x,p0y,p1x,p1y);
  }

  // add the gradient color stops
  // and draw the gradient line from p0 to p1
  g.addColorStop(0,p0.color);
  g.addColorStop(1,p1.color);
  ctx.beginPath();
  ctx.moveTo(p0.x,p0.y);
  ctx.lineTo(p1.x,p1.y);
  ctx.strokeStyle=g;
  ctx.lineWidth=linewidth;
  ctx.stroke();
}
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=350 height=200></canvas>
like image 196
markE Avatar answered Sep 22 '22 10:09

markE