将图像绘制到画布会为绘制的每个新图像返回InvalidStateError,然后成功

Bry*_*ley 10 javascript html5 canvas

情况

我有一个网格状的无序列表,可以旋转144(90px x 90px)图像(12x12).我的最终目标是采用144图像网格并将其保存为1张图像.

现行解决方案

我目前的解决方案让我遵循以下步骤:

  1. 创建一个图像宽度x 12宽,一个图像高度x 12高的画布.这是为了表示最终产品图像.
  2. 遍历列表项(图像),从项中提取图像src并将其绘制到自己的画布上,该画布是图像的大小.
  3. 旋转新的小画布,但它的图像已在网格上旋转.
  4. 将新的小画布绘制到当前指针的x和y处的最终结果画布上.

注意事项

当我遍历图像时,我会跟踪指针(我当前在画布上的位置).我这样做是通过维护行和列号来实现的.它们代表我正在绘制的图像的当前行和列.我使用它们,乘以单个图像的宽度和高度,以获得画布上的精确x和y坐标以绘制下一个图像.

当前的问题

当我调用函数来创建,绘制和生成画布的base64时,我收到以下错误消息:"InvalidStateError:尝试使用不可用或不再可用的对象.".如果这个错误在100%的时间内发生,我会假设它,因为我正在绘制到画布的图像,要么尚未加载,要么根本没有加载,但是,我只收到一次此错误我加载的每个新图像.例如,如果我有一个144图像网格,即每次绘制72次的2个不同图像,我将收到两次InvalidStateError,然后第三次调用该函数,它将成功.

现行守则

请记住,这只是用于测试保存图像的尖峰代码,我知道需要进行一些重构.

generateThumbnail: function(){
  var builder = this,
      canvas = document.createElement('canvas'),
      content,
      row = 0,
      col = 0;

  // width is single image width (90) x number of tiles wide (usually 12)
  canvas.width = 90 * builder.grid[0];
  // height is single image height (90) x number of tiles high (usually 12)
  canvas.height = 90 * builder.grid[1];
  // get 2d context of new canvas
  context = canvas.getContext("2d");

  // loop through all of the images on the grid
  $.each($(".pattern-grid li"), function(i, tile) {
    var $tile = $(tile),
        image = new Image(),
        src = $tile.find("img").attr("src"),
        width,
        height,
        buffer,
        bufferctx,
        x,
        y;

    // set crossOrigin of image to anonymous as these images are loaded via CORS
    image.crossOrigin = "Anonymous";

    // increase row number by 1 if it has reached the end of the row and its not the first image being drawn
    if(i % builder.grid[0] == 0 && i != 0){
      row++;
    }
    // Set Column to 0 if it is a new row, otherwise increase column by 1 (unless it is the first image being drawn)
    if(col == builder.grid[0]-1){
      col = 0;
    }else if(i != 0){
      col++;
    }

    // determine if there was no image drawn at this location
    if(src != undefined){
      image.src = src;
      // get the width and height the image, to be used for the small canvas and where to draw it
      width = image.width;
      height = image.height;
      // create a new buffer canvas to draw the image to, this will be used to apply any rotations that may exist
      buffer = document.createElement("canvas");
      //set width and height of the buffer to the current images width and height
      buffer.width = width;
      buffer.height = height;
      bufferctx = buffer.getContext("2d");
      //Determine x and y coordinates to draw the small canvas using row and column numbers
      x = col*width;
      y = row*height;
      //Save current state of buffer canvas
      bufferctx.save();
      //translate and then rotate the buffer canvas by the image's rotation
      bufferctx.translate(width/2, height/2);
      bufferctx.rotate($tile.find("img").data("rotation")*Math.PI/180);
      bufferctx.translate(width/2*-1, height/2*-1);
      //draw image to buffer canvas and restore its context
      bufferctx.drawImage(image, 0, 0);
      bufferctx.restore();
      //draw the buffer canvas to the main canvas at predetermined x and y
      context.drawImage(buffer, x, y, width, height);
    }
  });
  return canvas.toDataURL();
}
Run Code Online (Sandbox Code Playgroud)

Bry*_*ley 4

我能够将 @abiessu 的建议与 onload 结合使用,并与闭包配合使用来保存函数的状态。我的有效解决方案是:

generateThumbnail: function(){
  var builder = this,
      canvas = document.createElement('canvas'),
      content,
      row = 0,
      col = 0;
  // width is single image width (90) x number of tiles wide (usually 12)
  canvas.width = 90 * builder.grid[0];
  // height is single image height (90) x number of tiles high (usually 12)
  canvas.height = 90 * builder.grid[1];
  context = canvas.getContext("2d");
  // loop through all of the images on the grid
  $.each($(".pattern-grid li"), function(i, tile) {
    var $tile = $(tile),
        image = new Image(),
        src = $tile.find("img").attr("src");
     // set crossOrigin of image to anonymous as these images are loaded via CORS
    image.crossOrigin = "Anonymous";
    // increase row number by 1 if it has reached the end of the row and its not the first image being drawn
    if(i % builder.grid[0] == 0 && i != 0){
      row++;
    }
    // increase row number by 1 if it has reached the end of the row and its not the first image being drawn
    if(col == builder.grid[0]-1){
      col = 0;
    }else if(i != 0){
      col++;
    }
    image.onload = function(row, col){
      return function(){
        // determine if there was no image drawn at this location
        if(src != undefined){
          var width = image.width,
              height = image.height,
              buffer = document.createElement("canvas"),
              bufferctx,
              x,
              y;
          buffer.width = width;
          buffer.height = height;
          bufferctx = buffer.getContext("2d");
          x = col*width;
          y = row*height;
          bufferctx.save();
          bufferctx.translate(width/2, height/2);
          bufferctx.rotate($tile.find("img").data("rotation")*Math.PI/180);
          bufferctx.translate(width/2*-1, height/2*-1);
          bufferctx.drawImage(image, 0, 0);
          bufferctx.restore();
          context.drawImage(buffer, x, y, width, height);
        }
      }
    }(row, col);
    image.src = $tile.find("img").attr("src");
  });
  window.canvas = canvas;
}
Run Code Online (Sandbox Code Playgroud)