我正在尝试使用HTML5画布API在点的路径上绘制连续渐变,其中每个点都有自己的颜色。
请参阅http://bl.ocks.org/rveciana/10743959获取灵感,使用D3实现该效果。
似乎没有办法为单个画布路径添加多个线性渐变,所以我采用了这样的方法: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();
}
正如您所看到的那样,由于路径未合并且“线路连接”清晰可见,因此会产生一种低效效果。
是否可以使用canvas API实现我正在寻找的效果?
答案 0 :(得分:6)
以下是对原始想法的略微修改,使联接很好地融合。
原文:从线段的开头到结尾绘制一条渐变线。
这会导致线连接重叠并产生明显的&amp;不希望的过渡。
修改:绘制一条未延伸到起点/终点的渐变线。
通过此修改,线连接将始终为纯色而不是部分渐变。因此,线连接将在线段之间很好地转换。
以下是示例代码和演示:
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>
答案 1 :(得分:4)
你可以做一个简单的方法,沿着一条线插入两种颜色。如果你需要平滑/共享渐变,其中两条线以更陡的角度连接,你需要计算并基本上从(几乎)划痕实现线条绘制算法。这将超出SO的范围,因此这是一种更简单的方法。
话虽如此 - 链接中的示例实际上并不是一条线,而是几块不同颜色的正方形。它本来会遇到的问题是&#34;隐藏&#34;通过其微妙的变化。
这种方法需要两个主要功能:
行插值函数,用于绘制从前一个鼠标位置到当前位置的一行中的每个段
颜色插值功能,它根据长度,位置和线段大小,采用一系列颜色并在两种当前颜色之间进行插值。
调整参数,例如段大小,数组中的颜色数等,以获得最佳结果。
function plotLine(ctx, x1, y1, x2, y2) {
var diffX = Math.abs(x2 - x1), // get line length
diffY = Math.abs(y2 - y1),
dist = Math.sqrt(diffX * diffX + diffY * diffY),
step = dist / 10, // define some resolution
i = 0, t, b, x, y;
while (i <= dist) { // render circles along the line
t = Math.min(1, i / dist);
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
ctx.fillStyle = getColor(); // get current color
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI*2);
ctx.fill();
i += step;
}
function getColor() {
var r, g, b, t, c1, c2;
c1 = colors[cIndex]; // get current color from array
c2 = colors[(cIndex + 1) % maxColors]; // get next color
t = Math.min(1, total / segment); // calculate t
if (++total > segment) { // rotate segment
total = 0;
if (++cIndex >= maxColors) cIndex = 0; // rotate color array
}
r = c1.r + (c2.r - c1.r) * t; // interpolate color
g = c1.g + (c2.g - c1.g) * t;
b = c1.b + (c2.b - c1.b) * t;
return "rgb(" + (r|0) + "," + (g|0) + "," + (b|0) + ")";
}
将它们放在一起将允许您绘制渐变线。如果您不想手动绘制它们,只需在需要时调用plotLine()
函数。
// Some setup code
var c = document.querySelector("canvas"),
ctx = c.getContext("2d"),
colors = [
{r: 255, g: 0, b: 0},
{r: 255, g: 255, b: 0},
{r: 0, g: 255, b: 0},
{r: 0, g: 255, b: 255},
{r: 0, g: 0, b: 255},
{r: 255, g: 0, b: 255},
{r: 0, g: 255, b: 255},
{r: 0, g: 255, b: 0},
{r: 255, g: 255, b: 0},
],
cIndex = 0, maxColors = colors.length,
total = 0, segment = 500,
isDown = false, px, py;
setSize();
c.onmousedown = c.ontouchstart = function(e) {
isDown = true;
var pos = getPos(e);
px = pos.x;
py = pos.y;
};
window.onmousemove = window.ontouchmove = function(e) {if (isDown) plot(e)};
window.onmouseup = window.ontouchend = function(e) {
e.preventDefault();
isDown = false
};
function getPos(e) {
e.preventDefault();
if (e.touches) e = e.touches[0];
var r = c.getBoundingClientRect();
return {
x: e.clientX - r.left,
y: e.clientY - r.top
}
}
function plot(e) {
var pos = getPos(e);
plotLine(ctx, px, py, pos.x, pos.y);
px = pos.x;
py = pos.y;
}
function plotLine(ctx, x1, y1, x2, y2) {
var diffX = Math.abs(x2 - x1),
diffY = Math.abs(y2 - y1),
dist = Math.sqrt(diffX * diffX + diffY * diffY),
step = dist / 50,
i = 0,
t, b, x, y;
while (i <= dist) {
t = Math.min(1, i / dist);
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
ctx.fillStyle = getColor();
ctx.beginPath();
ctx.arc(x, y, 10, 0, Math.PI*2);
ctx.fill();
i += step;
}
function getColor() {
var r, g, b, t, c1, c2;
c1 = colors[cIndex];
c2 = colors[(cIndex + 1) % maxColors];
t = Math.min(1, total / segment);
if (++total > segment) {
total = 0;
if (++cIndex >= maxColors) cIndex = 0;
}
r = c1.r + (c2.r - c1.r) * t;
g = c1.g + (c2.g - c1.g) * t;
b = c1.b + (c2.b - c1.b) * t;
return "rgb(" + (r|0) + "," + (g|0) + "," + (b|0) + ")";
}
}
window.onresize = setSize;
function setSize() {
c.width = window.innerWidth;
c.height = window.innerHeight;
}
document.querySelector("button").onclick = function() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
};
&#13;
html, body {background:#777; margin:0; overflow:hidden}
canvas {position:fixed;left:0;top:0;background: #333}
button {position:fixed;left:10px;top:10px}
&#13;
<canvas></canvas>
<button>Clear</button>
&#13;
TIPS: