如何使用可变大小的圆圈填充画布文本并获得最佳性能?

时间:2014-01-24 22:42:27

标签: javascript canvas geometry

我目前想用可变大小的圆圈填充给定文本(实际上......它是任何图像)。

到目前为止,我将文本写入<canvas>,然后我使用:ctx.getImageData(0, 0, w, h)获取图像数据,然后我开始循环遍历数组,寻找imageData中的像素。

我正面临这些问题,我不明白为什么:(

  • 这太慢了(在大文本上),我怎么能提高它的性能呢?
  • 我可以使用哪种算法已经存在,所以我可以研究它吗?
  • 如果我使用更大的画布到getImageData(),表现非常糟糕,无法完成。

到目前为止,这是我的代码:http://codepen.io/Goodwine/pen/xDvLk

function circleCanvas(ctx, minR, maxR, padding) {
  var c = $('<canvas>')[0].getContext('2d');
  var img = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
  var h = c.canvas.height = img.height;
  var w = c.canvas.width = img.width;

  var ban = [];
  for (var i = 0; i < img.data.length; i += 4) {
    if (img.data[i] != 100 || isBanned(ban, i, w))
      continue;
    var r = parseInt((Math.random() * 10000) % (maxR - minR) + minR);
    var x = Math.floor(i % (w * 4) / 4) + r;
    var y = Math.floor(i / (w * 4)) + r;
    drawCircle(c, x, y, r);
    ban.push({xi: x - r - padding, yi: y - r - padding, xf: x + r + padding, yf: y + r + padding});
  }
  return c;
}

// this function is used because I didn't find how else to improve performance
// it checks if there is a point in a "banned" range, and it ignores it in
// case it does.
function isBanned(ban, p, w) {
  var x = Math.floor(p % (w * 4) / 4);
  var y = Math.floor(p / (w * 4));
  for (var i in ban) {
    if (x >= ban[i].xi && x <= ban[i].xf && y >= ban[i].yi && y <= ban[i].yf)
      return true;
  }
  return false;
}

2 个答案:

答案 0 :(得分:2)

以下是一些优化代码的方法,作为开头:

  1. 在路径中添加弧线并在末尾填充而不是填充,每次都设置颜色。
  2. 使用Uint32Arrays迭代并检测像素
  3. 将带有增量的ban()区域替换为计数器
  4. 例如,您可以通过以下方式添加圈子:

    function addCircle(ctx, x, y, r) {
      ctx.moveTo(x + r, y);
      ctx.arc(x, y, r, 0, 2*Math.PI);
    }
    

    然后使用Uint32Array迭代你的循环:

    function circleCanvas(ctx, minR, maxR, padding) {
    
      var h = ctx.canvas.height;
      var w = ctx.canvas.width;
      var img = ctx.getImageData(0, 0, w, h);
    
      /// use an Uint32 buffer instead
      var buffer = new Uint32Array(img.data.buffer);
    
      /// create new path here
      ctx.beginPath();
    
      var ban = [];  /// I'm ignoring this in this example
      var skipLines; 
    
      for (var i = 0; i < buffer.length; i++) {
        if (buffer[i] === 0)
          continue;
    
        var r = (Math.random() * (maxR - minR) + minR)|0;
        var x = i % w;
        var y = Math.floor(i / w);
    
        addCircle(ctx, x, y, r);
    
        i += r * 2;
        if (x === 0) {
          skipLines = (r * 2 * Math.random() + r)|0;
          i += skipLines * w;
        }
      }
    
      /// fill at end
      ctx.fillStyle = '#000';
      ctx.fill();
    }
    

    <强> See update here

答案 1 :(得分:2)

让文字填充随机圆圈的更快方法是使用随机圆圈创建图案,并使用此图案填充文本。
模式创建非常快,只能进行一次 在循环期间,只需使用此模式填充文本(无需重新绘制圆圈)。

我做了一个小型演示,你可以改变模式甚至动画,所以你可以看到它很快。

http://jsbin.com/oLIkAraJ/1/

enter image description here