优化画布瓷砖地图绘制

时间:2014-07-21 00:30:05

标签: javascript jquery canvas

我目前的程序需要在画布上绘制数万个矩形。这些矩形可以是灰色或红色,具体取决于它们所代表的含义。目前大约需要5秒钟来绘制100.000个这样的矩形,而前面的计算只需要不到100毫秒。很高兴我想优化这个程序的绘图部分,因为我觉得我在这里缺少优化的主要部分。

我目前的绘图代码如下:

function colourRect(i) {
    // Those calculations are for the exact coordinates of the squares in the map
    ctx.rect(((1 + (i - 1) * 12) - settingsWidth * 12 * parseInt((i - 1) / settingsWidth)), (1 + (parseInt((i - 1 / settingsStart) / settingsWidth) * 12) - (parseInt((settingsStart - 1 / settingsStart) / settingsWidth) * 12)), 10, 10);
}

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

ctx.beginPath();
var i = 0;
ctx.fillStyle = "#CCC";
while(i <= parseInt(settingsEnd)) {
    if($.inArray(i, grey) == -1) { colourRect(i); }
    i = i + 1;
}
ctx.closePath();
ctx.fill();

ctx.beginPath();
var i = 0;
ctx.fillStyle = "#900";
while(i <= parseInt(settingsEnd)) {
    if($.inArray(i, grey) != -1) { colourRect(i); }
    i = i + 1;
}
ctx.closePath();
ctx.fill();

编辑注释:色彩交换校正仅发生两次。 编辑注释2:纠正数组命名中的小错误(只有一个数组,而不是两个数组)

2 个答案:

答案 0 :(得分:1)

1)避免改变颜色,这是很好的 2)你不应该反复迭代灰色和红色并测试它们的值对0和settingsEnd ??无论如何,我通过使用排序数组优化了处理 3)如果你将网格简化为一个10像素正方形的简单12像素间隔网格,你可以通过画布为你做数学来简化绘图(翻译+缩放12)。

下面的代码应该更快,注意我只改变了fillStyle的两倍:

function fillRect(i) {
       ctx.fillRect(((1 + (i - 1) * 12) - settingsWidth * 12 * Math.floor((i - 1) / settingsWidth)), (1 + (Math.floor((i - 1 / settingsStart) / settingsWidth) * 12) - (Math.floor((settingsStart - 1 / settingsStart) / settingsWidth) * 12)), 10, 10);
}

function drawThings() { 
  var i = 0, j=0;
  settingsEnd = parseInt(settingsEnd);

  ctx.fillStyle = "#CCCCCC"; 
  i=0; 
  if (greyNeedSort) {
      grey.sort(); 
      greyNeedSort = false ;    }
  j=0;
  while(i <= settingsEnd) {
     while(j<grey.length && grey[j]<i) {j++} // find j, a grey index >= i
     if (j<grey.length && grey[j]==i) { fillRect(i); } // draw if found a grey value == i
     else (if j== grey.length-1) break; // exit if no more grey available
     i++;                                               
  }

  ctx.fillStyle = "#990000";   
  i=0; 
  if (redNeedSort) {
     red.sort();  
     redNeedSort = false;    }
  j=0;
  while(i <= settingsEnd) {
       while(j<red.length && red[j]<i) {j++}
       if (j<red.length && red[j]==i) { fillRect(i); }
       else (if j== grey.length-1) break;  // exit if no more red available
       i++;                                             } 
} 

编辑:
1)当问题的代码或其他答案确实经常发生变化时,此代码只进行两次颜色交换。
2)在你的代码中,你按升序绘制所有灰色,所以你甚至可以做得更好:

 if (greyNeedSort) {
     grey.sort(); 
     greyNeedSort = false ;    }
 // just iterate : 
 for (i =0; i<grey.length; i++ ) if (i< settingsEnd) fillRect(i);

(红色相同)。

3)你可以让画布为你做数学运算,你简化了fillRect,它变成了:

function fillRect(i) {
       ctx.fillRect(i, i, 0.8, 0.8);
}

您可以通过以下方式更改绘图方法:

function drawThings() { 
  ctx.save();
  ctx.scale(12, 12);
  ctx.translate(0.2, 0.2); // top-left margin

  /// same code ...

  ctx.restore();
}

答案 1 :(得分:0)

优化代码的一些提示:

  • Don'使用库(例如jQuery)。 Vanilla-js要快得多。
  • 避免在循环条件下进行操作。而是将结果保存在循环之前的变量中。
  • parseInt很慢,在大多数情况下,还有其他选择:
    • Numberunary operator+)转换为数字。
    • 使用0(|0)或Math.trunc的按位OR运算符截断为整数。

此外,当您逐步从一个数字迭代到另一个数字时,您可以使用for循环而不是while循环。

然后,这应该更快:

canvas = document.getElementById("map");
ctx = canvas.getContext("2d");
var loopEnd = settingsEnd | 0;

ctx.beginPath();
for(var i=0; i <= loopEnd; ++i) {
    if(!~grey.indexOf(i)) { colourRect(i, "#CCCCCC"); }
}
ctx.closePath();
ctx.fill();

ctx.beginPath();
for(var i=0; i <= loopEnd; ++i) {
    if(~red.indexOf(i)) { colourRect(i, "#990000"); }
}
ctx.closePath();
ctx.fill();