情况
我有144个(90px x 90px)图像(12x12)可以旋转的网格状无序列表。我的最终目标是拍摄144个图像网格并将其另存为1个图像。
当前解决方案
我当前的解决方案是按照以下步骤操作:

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

  • 注意事项
    当我遍历图像时,我会跟踪一个指针(我当前在 Canvas 上的位置)。我通过保持一行和一个列号来做到这一点。它们代表了我正在绘制的图像的当前行和列。我用它们乘以单个图像的宽度和高度来获得 Canvas 上确切的x和y坐标以绘制下一个图像。
    当前问题
    当我调用该函数来创建,绘制和生成 Canvas 的base64时,会收到以下错误消息:“InvalidStateError:试图使用一个不可用或不再可用的对象。”。如果此错误发生的可能性为100%,则我将其假定为是因为我要绘制到 Canvas 上的图像尚未加载或根本没有加载,但是,我只收到一次此错误,原因是我加载的每个新图像。例如,如果我有一个144个图像网格,即2个不同的图像,每个图像绘制72次,则我将两次收到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();
    }
    

    最佳答案

    我可以将@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;
    }
    

    09-20 20:47