How do I draw images in layers on canvas? - javascript

I have a canvas where I use drawImage to draw a bunch of images to the canvas.
How I want the result to be:
I want the first image i draw to be on layer 1, the next image on layer 2 and so on
What really happens:
The images get placed on random layers.
const images = [
'https://attefallsverket.picarioxpo.com/1_series_base.jpg?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_housebase.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_roof_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_windows.pfs?1=1&p.c=71343a&p.tn=&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_door_01.pfs?1=1&p.c=&p.tn=rainsystem_grey.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_01.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_corners.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_windows.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_roof.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_roof_metal_orange.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_rain_system.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1_series_terrace.png?1=1&width=2000',
];
let c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
for(let i=0; i<images.length; i++) {
let img = new Image();
img.crossOrigin = '';
img.src = images[i]
img.onload = () => {
ctx.drawImage(img, 0, 0, c.width, c.height);
}
}
<canvas id="myCanvas" width="280" height="157.5" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>

You would need to ensure that the first image has loaded before launching the load of the next. So make an asynchronous loop:
const images = [
'https://attefallsverket.picarioxpo.com/1_series_base.jpg?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_housebase.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_roof_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_windows.pfs?1=1&p.c=71343a&p.tn=&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_door_01.pfs?1=1&p.c=&p.tn=rainsystem_grey.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_01.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_corners.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_windows.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_roof.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_roof_metal_orange.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_rain_system.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1_series_terrace.png?1=1&width=2000',
];
let c = document.getElementById("myCanvas");
let ctx = c.getContext("2d");
(function loop(i) {
if (i >= images.length) return; // all done
let img = new Image();
img.crossOrigin = '';
img.onload = () => {
ctx.drawImage(img, 0, 0, c.width, c.height);
loop(i+1); // continue with next...
}
img.src = images[i];
})(0); // start loop with first image
<canvas id="myCanvas" width="280" height="157.5"</canvas>

Here's what you really want to do:
addEventListener('load', ()=>{ // page and script load
const images = [
'https://attefallsverket.picarioxpo.com/1_series_base.jpg?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_housebase.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_roof_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_windows.pfs?1=1&p.c=71343a&p.tn=&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_door_01.pfs?1=1&p.c=&p.tn=rainsystem_grey.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_01.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_corners.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_windows.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_roof.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_roof_metal_orange.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_rain_system.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1_series_terrace.png?1=1&width=2000'
];
const canvas = document.getElementById('myCanvas'), ctx = canvas.getContext('2d'), promises = [];
let w = canvas.width, h = canvas.height, p;
for(let m of images){
p = new Promise(r=>{
const im = new Image;
im.onload = ()=>{
r(im);
}
im.src = m;
});
promises.push(p);
}
Promise.all(promises).then(imgs=>{
for(let im of imgs){
ctx.drawImage(im, 0, 0, w, h);
}
});
}); // end page load
<canvas id='myCanvas' width='280' height='157.5'></canvas>

The problem is that you you can't really control how long it will take the browser to download each image. So the first image that fires the onload event might not be the first image in the array - likewise the second picture might be 10th in the array and so on.
To workaround I'd recommend going through your images array one by one and start loading a new image as soon as the last image finished loading.
Here's an example:
const images = [
'https://attefallsverket.picarioxpo.com/1_series_base.jpg?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_housebase.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_roof_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_windows.pfs?1=1&p.c=71343a&p.tn=&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_door_01.pfs?1=1&p.c=&p.tn=rainsystem_grey.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_01.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_facade_corners.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_windows.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_tin_roof.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_roof_metal_orange.png?1=1&width=2000',
'https://attefallsverket.picarioxpo.com/1kp_rain_system.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000',
'https://attefallsverket.picarioxpo.com/1_series_terrace.png?1=1&width=2000',
];
let imagesLoaded = 0;
let c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
function loadImage() {
let img = new Image();
img.crossOrigin = '';
img.onload = () => {
ctx.drawImage(img, 0, 0, c.width, c.height);
if (imagesLoaded + 1 < images.length) {
imagesLoaded++;
loadImage(imagesLoaded);
}
}
img.src = images[imagesLoaded];
}
loadImage(0)
<canvas id="myCanvas" width="280" height="157.5" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>

Existing answers solve the problem, but they're serialized and don't fire off requests simultaneously. If you want to optimize for getting something on the screen and don't care how long it takes to reach the complete image, this is fine, but if your goal is to get the entire image drawn as quickly as possible and/or not show a partially-completed image, one-by-one network requests are suboptimal.
Instead of this:
request image 0
wait for request 0 over the wire or file IO
draw image 0
request image 1
wait for request 1 over the wire or file IO
draw image 1
...
request image n
wait for request n over the wire or file IO
draw image n
It might make sense to:
request/load all images at once
wait for all images to be received
draw all images in order
The idea is to exploit parallelism and only wait for one image (the slowest) to arrive, overlapping all other requests within the slowest load time, rather than incurring the cost of loading all n images one at a time.
A good way to do this is with promises. You can promisify the onload and onerror callbacks to resolve and reject respectively, then use Promise.all to wait for all images to arrive, at which point you can apply a traditional, synchronous loop to draw the layers in order.
const images = ['https://attefallsverket.picarioxpo.com/1_series_base.jpg?1=1&width=2000','https://attefallsverket.picarioxpo.com/1kp_housebase.png?1=1&width=2000','https://attefallsverket.picarioxpo.com/1kp_facade_roof_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_windows.pfs?1=1&p.c=71343a&p.tn=&width=2000','https://attefallsverket.picarioxpo.com/1kp_door_01.pfs?1=1&p.c=&p.tn=rainsystem_grey.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_facade_01.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_facade_panels.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_facade_corners.pfs?1=1&p.c=&p.tn=wooden_summer_green.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_tin_windows.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_tin_roof.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000','https://attefallsverket.picarioxpo.com/1kp_roof_metal_orange.png?1=1&width=2000','https://attefallsverket.picarioxpo.com/1kp_rain_system.pfs?1=1&p.c=&p.tn=rainsystem_white.jpg&width=2000','https://attefallsverket.picarioxpo.com/1_series_terrace.png?1=1&width=2000',];
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
Promise.all(images.map(url =>
new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = "";
img.onerror = e => reject(`${url} failed to load`);
img.onload = function () {
resolve(this);
};
img.src = url;
})))
.then(images =>
images.forEach(e =>
ctx.drawImage(e, 0, 0, c.width, c.height)
)
)
.catch(err => console.error(err))
;
<canvas id="myCanvas" width="280" height="157.5" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.
</canvas>
If your goal is to get something on screen as quickly as possible, you could combine the two approaches, doing one fast, serial request for the background, then doing the rest in parallel, or even in batches. But this feels like overkill for this case; I mention the technique for completeness.

Related

Loading images before rendering JS canvas

I'm writing one of those simple games to learn JS and I'm learning HTML5 in the process so I need to draw things on canvas.
Here's the code:
let paddle = new Paddle(GAME_WIDTH,GAME_HEIGHT);
new InputHandler(paddle);
let lastTime = 0;
const ball = new Image();
ball.src = 'assets/ball.png';
function gameLoop(timeStamp){
let dt = timeStamp - lastTime;
lastTime = timeStamp;
ctx.clearRect(0,0,600,600);
paddle.update(dt);
paddle.draw(ctx);
ball.onload = () => {
ctx.drawImage(ball,20,20);
}
window.requestAnimationFrame(gameLoop);
}
gameLoop();
screenshot: no ball
before comment
now I comment out the clearRect():
after comment
hello ball.
There's also a paddle at the bottom of the canvas that doesn't seem to be affected by the clearRect() method. It works just fine. What am I missing here?
It doesn't make much sense to put the image's onload handler inside the game loop. This means the game has to begin running before the image's onload function is set, leading to a pretty confusing situation.
The correct sequence is to set the onload handlers, then the image sources, then await all of the image onloads firing before running the game loop. Setting the main loop to an onload directly is pretty easy when you only have one image, but for a game with multiple assets, this can get awkward quickly.
Here's a minimal example of how you might load many game assets using Promise.all. Very likely, you'll want to unpack the loaded images into more descriptive objects rather than an array, but this is a start.
const canvas = document.createElement("canvas");
document.body.appendChild(canvas);
canvas.width = 400;
canvas.height = 250;
const ctx = canvas.getContext("2d");
const assets = [
"http://placekitten.com/120/100",
"http://placekitten.com/120/120",
"http://placekitten.com/120/140",
];
const assetsLoaded = assets.map(url =>
new Promise(resolve => {
const img = new Image();
img.onerror = e => reject(`${url} failed to load`);
img.onload = e => resolve(img);
img.src = url;
})
);
Promise
.all(assetsLoaded)
.then(images => {
(function gameLoop() {
requestAnimationFrame(gameLoop);
ctx.clearRect(0, 0, canvas.width, canvas.height);
images.forEach((e, i) =>
ctx.drawImage(
e,
i * 120, // x
Math.sin(Date.now() * 0.005) * 20 + 40 // y
)
);
})();
})
.catch(err => console.error(err))
;

jpg loaded with python keras is different from jpg loaded in javascript

I am loading jpg image in python on the server. Then I am loading the same jpg image with javascript on the client. Finally, I am trying to compare it with the python output. But loaded data are different so images do not match. Where do I have a mistake?
Python code
from keras.preprocessing.image import load_img
from keras.preprocessing.image import img_to_array
filename = './rcl.jpg'
original = load_img(filename)
numpy_image = img_to_array(original)
print(numpy_image)
JS code
import * as tf from '#tensorflow/tfjs';
photo() {
var can = document.createElement('canvas');
var ctx = can.getContext("2d");
var img = new Image();
img.onload = function() {
ctx.drawImage(img, 0, 0);
};
img.crossOrigin = "anonymous";
img.src = './rcl.jpg';
var tensor = tf.fromPixels(can).toFloat();
tensor.print()
}
You are drawing the image on a canvas before rendering the canvas as a tensor. Drawing on a canvas can alter the shape of the initial image. For instance, unless specified otherwise - which is the case with your code - the canvas is created with a width of 300 px and a height of 150 px. Therefore the resulting shape of the tensor will be more or less something like the following [150, 300, 3].
1- Using Canvas
Canvas are suited to resize an image as one can draw on the canvas all or part of the initial image. In that case, one needs to resize the canvas.
const canvas = document.create('canvas')
// canvas has initial width: 300px and height: 150px
canvas.width = image.width
canvas.height = image.heigth
// canvas is set to redraw the initial image
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0) // to draw the entire image
One word of caution though: all the above piece should be executed after the image has finished loading using the event handler onload as the following
const im = new Image()
im.crossOrigin = 'anonymous'
im.src = 'url'
// document.body.appendChild(im) (optional if the image should be displayed)
im.onload = () => {
const canvas = document.create('canvas')
canvas.width = image.width
canvas.height = image.heigth
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0)
}
or using async/await
function load(url){
return new Promise((resolve, reject) => {
const im = new Image()
im.crossOrigin = 'anonymous'
im.src = 'url'
im.onload = () => {
resolve(im)
}
})
}
// use the load function inside an async function
(async() => {
const image = await load(url)
const canvas = document.create('canvas')
canvas.width = image.width
canvas.height = image.heigth
const ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0)
})()
2- Using fromPixel on the image directly
If the image is not to be resized, you can directly render the image as a tensor using fromPixel on the image itself

Can't redraw image represented in base64

First part of my application loads picture with file chooser from my file system and draws image to canvas. I take that canvas and convert it to data URL and also save it in my database.
var imgData = canvas.toDataURL("image/jpg");
factory.saveData(imgData); // execute some java code
Here is the problem. I can't redraw that image, my javascript:
var pic = new Image();
pic.onload = function() {
ctx.drawImage(pic, 0, 0); // ctx = canvas.getContext("2d")
};
// This data url is copy/pasted from database for simplicity
pic.src = "data:image/png;base64,iVBOR..........ElFTkSuQmCC";
Onload function is called but I just get transparent image.
Probably has something to do with the fact that getDataFromDatabase() is asynchronous so you're returning a promise object to pic.src instead of the data url. Fix this by using a then call on getDataFromDatabase and using the result on pic.src like this:
var pic = new Image();
var pic.onload = function() {
ctx.drawImage(pic, 0, 0);
};
getDataFromDatabase().then(dataUrl => {
pic.src = dataUrl;
});
Here's a full example of this in action. I draw to a canvas, store the canvas data as a base64 data url into a variable, save it into a mock DB using closures, then reload the data from a simulated asynchronous function call by using a promise.
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Initialize canvas to a random image
const imageUrl = 'https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-logo.svg?v=a010291124bf';
const initialImage = new Image();
initialImage.src = imageUrl;
initialImage.onload = function() {
ctx.drawImage(initialImage, 0, 0);
}
// Convert canvas to base64 data url
const imageData = canvas.toDataURL('image/png');
const getDataFromDatabase = saveData(imageData);
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Redraw the image
const pic = new Image();
getDataFromDatabase().then(data => {
pic.src = data;
});
pic.onload = function() {
ctx.drawImage(pic, 0, 0);
}
function saveData(image) {
return function getDataFromDatabase() {
return new Promise(resolve => {
resolve(image);
});
}
}
<canvas id="myCanvas">
</canvas>
Edit: It shouldn't really matter where you get the image data from. If the data is not malformed, it should draw to the canvas. Here's an example identical to the way you're loading image data (from a hardcoded data url):
const imageData = 'data:image/png;base64,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';
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
const image = new Image();
image.onload = function() {
ctx.drawImage(image, 0, 0);
}
image.src = imageData;
<canvas id="myCanvas">
</canvas>
I suggest you check your image data using a base64 image decoder to see if your image data is malformed or not. If your data url is valid, then something else I can suggest is ensuring your canvas width and height are equal to or greater than the image's width and height or else it'll be hidden (if the image you're supplying has empty space / padding).

GetImageData not returning expected results for comparing 2 images

If I have 2 images that are the same except i change 1 pixel, it says there is 131 different changes in my array. Why is this happening? I'm creating 2 arrays using getcanvasdata and a loop to compare both.
var c = document.getElementById("myCanvas");
var c2 = document.getElementById("myCanvas2");
var ctx = c.getContext("2d");
var ctx2 = c2.getContext("2d");
var img = new Image();
var img2 = new Image();
var diffpixels = [];
var imgData;
var imgData2;
img.src = "avengers2.jpg";
img2.src = "avengers1.jpg";
img.onload = function() {
ctx.drawImage(img, 0, 0);
imgData = ctx.getImageData(0, 0, 1920, 1080).data;
img2.onload = function() {
ctx2.drawImage(img2, 0, 0);
imgData2 = ctx2.getImageData(0, 0, 1920, 1080).data;
console.log(imgData2.length);
for (var i = 0; i < imgData.length; i++) {
if (imgData[i] === imgData2[i]) {} else {
diffpixels.push(i);
}
}
console.log(diffpixels);
}
}
<canvas id="myCanvas" width="1920" height="1080" style="border:1px solid #d3d3d3"></canvas>
<canvas id="myCanvas2" width="1920" height="1080" style="border:1px solid #d3d3d3"></canvas>
I suspect your problem here is actually in the use of .jpgs for your image files, as these contain artifacts from compression. Changing even one pixel and/or recompressing a .jpg image by saving it as a new file frequently causes more than one pixel to change due to the rather lossy compression method. I suggest you try using .png images instead, since they are a (mostly, usually) lossless format. The rest of your method here seems solid.
Alternatively, for the purposes of testing your code to make absolutely sure it works, you could load just one of the images into both canvases, then pick an arbitrary point (for this example, the point (100, 100) in myCanvas2) on one and do a quick one-pixel rectangle of an unexpected colour (say, #FF0080)
ctx2.fillStyle = "#FF0080";
ctx2.fillRect(100, 100, 1, 1);
right before comparing the two canvases. That may not work for the eventual case you are going for, but it should accurately test the function you've written.

Javascript img to base64 don't load

I develop Photoshop extension that sends images to the server.
Edit: Extensions in photoshop build from html file that define the GUI, js file that basically is the same as any js file, but it's can also launch photoshop function and it is execute from photoshop.
I need to send the images from the file system of the user (from C:\path\to\images)
To encode the images I converted them to dataURL (base64).
The problem occurs in the first time that I convert the images to dataURL. But in the second time and so, it manages to convert the images and everything is fine. In the first time the image doesn't loaded.
I have a folder where the images are and I want to upload the pictures from there, I used a loop that runs on photos and set them into <img src=path> to and then it converts them based 64 via <canvas>.
My code:
function convertLayersToBase64(imgHeight, imgWidth){
var img = new Image();
images = [];
for (var i=0; i<=imagesLength; i++){
path = folder + "layer " + i +".png";
img.src = path;
var canvas = document.createElement("canvas");
canvas.height = imgHeight;
canvas.width = imgWidth;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL();
images.push( dataURL );
}
return images;
}
I tried to delay the conversion by delay:
function delay(time) {
var d1 = new Date();
var d2 = new Date();
while (d2.valueOf() < d1.valueOf() + time) {
d2 = new Date();
}
}
JQuery when ready:
$(function(){
images.push(getBase64Image());
});
Img.complete
while(!img.complete)
continue;
(In the last example the code stuck in loop)
To put the function in:
img.onload = function(){
//the function here..
//when I use this method it succeed to convert
//only the last image.
}
Nothing worked..
I tried everything, please tell me what to change and how to fix that.
Edit: It's seem to me that the only way to load an image it's when the code
The function onload is an asynchronous action. You cannot just return images as the last statement within your convertLayersToBase64 function. You should either use promises, or a more simple approach would be to use a callback function.
function convertLayersToBase64(imgHeight, imgWidth, callback){
var images = [];
for (var i = 0; i <= imagesLength; i++) {
var img = new Image();
img.onload = function() {
var canvas = document.createElement("canvas");
canvas.height = imgHeight;
canvas.width = imgWidth;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(this, 0, 0);
var dataURL = canvas.toDataURL();
images.push(dataURL);
if(images.length === imagesLength) {
callback(images);
}
}
path = folder + "layer " + i +".png";
img.src = path;
}
}
You would call this like:
convertLayersToBase64(200, 200, function(images) {
console.log('hii, i got images', images);
});
This is obviously without any form of error check, or even best practice guidelines, but I'll leave it up to you to implement that.

Categories

Resources