沿着HTML5画布路径的连续渐变 [英] Continuous gradient along a HTML5 canvas path

查看:252
本文介绍了沿着HTML5画布路径的连续渐变的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图沿着点的路径绘制一个连续的渐变,其中每个点都有自己的颜色,使用HTML5 canvas API。

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.

请参阅 http://bl.ocks.org/rveciana/10743959 的灵感,其中效果是用D3实现的。

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

似乎没有办法为单个画布路径添加多个线性渐变,所以我诉诸这样的: http://jsfiddle.net/51toapv2/

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.

是否可以实现我正在使用canvas API查找的效果?

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

推荐答案

你可以做一个简单的方法沿着一条线插入两种颜色。如果需要两条线以更陡的角度连接的平滑/共享梯度,则需要计算并基本上从(几乎)划痕中实现线条绘制算法。这将是超出范围的SO,所以这里是一个更简单的方法。

You can do a simple approach interpolating two colors along a line. If you need smooth/shared gradients where two lines joins at steeper angles, you would need to calculate and basically implement a line drawing algorithm from (almost) scratch. This would be out of scope for SO, so here is a simpler approach.

这就是说 - 链接中的例子实际上不是一条线,但几个方块不同的颜色。

That being said - the example in the link is not actually a line but several plots of squares of different colors. The issues it would have too is "hidden" by its subtle variations.

此方法需要两个主要功能:

This approach requires two main functions:


  1. 线插入函数,用于绘制从上一个鼠标位置到当前位置的线段

  1. Line interpolate function which draws each segment in a line from previous mouse position to current position

颜色插值函数,根据长度,位置和片段大小,采用颜色数组并在两种当前颜色之间进行插值。

Color interpolate function which takes an array of colors and interpolate between two current colors depending on length, position and segment size.

调整参数,例如区段大小,数组中的颜色数量等,以获得最佳结果。

Tweak parameters such as segment size, number of colors in the array etc. to get the optimal result.

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;
  }



颜色插值函数



Color interpolate function

  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)
};

html, body {background:#777; margin:0; overflow:hidden}
canvas {position:fixed;left:0;top:0;background: #333}
button {position:fixed;left:10px;top:10px}

<canvas></canvas>
<button>Clear</button>

提示:


  • 可以预先填充/缓存渐变值

  • 渐变中的位置步长可以绑定到长度,以获得均匀分布,而与绘制速度无关

  • 您可以轻松地用其他路径/图形/形状替换画笔,甚至可以组合使用与当前颜色合成的基于图像的画笔。

这篇关于沿着HTML5画布路径的连续渐变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆