Situation
I have a grid-like unordered list with 144 (90px x 90px) images (12x12) that may be rotated. My end goal is to take the 144 image grid and save it as 1 image.
Current Solution
My current solution has me following these steps:
- Create a canvas that is one image width x 12 wide and one image height x 12 high. This is to represent the end product image.
- Loop through the list items(images), extracting the image src from the item and drawing it onto its own canvas that is the size of the image.
- Rotate the new small canvas however it's image has been rotated on the grid.
- Draw the new small canvas onto the end-result canvas at the x and y of the current pointer.
Things to note
As I loop through the images, I keep track of a pointer (where I am currently on the canvas). I do this by maintaining a row and a col number. They represent the current row and column of images I am drawing. I use them, multiplied by the width and height of a single image, to get the exact x and y coordinates on the canvas to draw the next image.
Current Problem
When I call the function to create, draw and generate the base64 of the canvas, I receive the following error message: "InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable.". If this error was occurring 100% of the time, I'd assume its because the image I'm drawing to the canvas, either isn't loaded yet or is not being loaded at all, but, I only receive this error once for every new image I load. For example, if I have a 144 image grid, that is 2 different images each drawn 72 times, I will receive InvalidStateError twice and then the third time I call the function, it will succeed.
Current Code
Please keep in mind this is simply spike code to test saving the image, I am aware some refactoring is required.
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();
}
Situation
I have a grid-like unordered list with 144 (90px x 90px) images (12x12) that may be rotated. My end goal is to take the 144 image grid and save it as 1 image.
Current Solution
My current solution has me following these steps:
- Create a canvas that is one image width x 12 wide and one image height x 12 high. This is to represent the end product image.
- Loop through the list items(images), extracting the image src from the item and drawing it onto its own canvas that is the size of the image.
- Rotate the new small canvas however it's image has been rotated on the grid.
- Draw the new small canvas onto the end-result canvas at the x and y of the current pointer.
Things to note
As I loop through the images, I keep track of a pointer (where I am currently on the canvas). I do this by maintaining a row and a col number. They represent the current row and column of images I am drawing. I use them, multiplied by the width and height of a single image, to get the exact x and y coordinates on the canvas to draw the next image.
Current Problem
When I call the function to create, draw and generate the base64 of the canvas, I receive the following error message: "InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable.". If this error was occurring 100% of the time, I'd assume its because the image I'm drawing to the canvas, either isn't loaded yet or is not being loaded at all, but, I only receive this error once for every new image I load. For example, if I have a 144 image grid, that is 2 different images each drawn 72 times, I will receive InvalidStateError twice and then the third time I call the function, it will succeed.
Current Code
Please keep in mind this is simply spike code to test saving the image, I am aware some refactoring is required.
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();
}
Share
Improve this question
edited Jun 20, 2020 at 9:12
CommunityBot
11 silver badge
asked Oct 28, 2013 at 14:21
Bryan AshleyBryan Ashley
9717 silver badges22 bronze badges
6
- Can you use console print statements to narrow down where the error occurs, or do you already know which line it occurs at? – abiessu Commented Oct 28, 2013 at 14:32
- The error output does not include a line number. I can create a jsfiddle to reproduce the issue for testing purposes. – Bryan Ashley Commented Oct 28, 2013 at 14:38
-
I know, it's one of the things that frustrates me with a lot of consoles. But I meant to put some
console.print
statements sprinkled throughout the code especially before and after certain "critical" sections to see if the error can be narrowed down. Is it possible that this code is invoked before the page has finished loading? – abiessu Commented Oct 28, 2013 at 14:40 - I have been using the console to call this function well after the page has loaded. I can narrow down where it fails. It fails when calling "context.drawImage(buffer, x, y, width, height);". – Bryan Ashley Commented Oct 28, 2013 at 14:42
-
After some research, I'm going to suggest that an
image.onload
callback be used. – abiessu Commented Oct 28, 2013 at 14:48
2 Answers
Reset to default 4I was able to use @abiessu's suggestion with an onload, paired with a closure to save state of the function. My solution, that works, is:
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;
}
Use an image.onload
callback:
image.onload = function(){
bufferctx.drawImage(image,0,0);
}
More information can be found from the Mozilla pages, I used this one and this search.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744789748a4593841.html
评论列表(0条)