Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
561 views
in Technique[技术] by (71.8m points)

javascript - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...