I have a situation where I need to wait for a img.onload function to resize a picture.
The img.onload function is located within a synchronious function, which can not be async.
I need to wait for the img.onload before I can continue inside the synchronious function.
Does anyone have an idea on how to realize it?
Example:
function test(img) {
const img = new Image();
img.onload = function () {
//Here needs something to happen, set a variable
x = value;
};
//After the img.onload I need to access this here
console.log(x);
}
This is what I have so far.
I know that img.onload will be executed later and console.log(x) is fired before that, but it needs to be afterwards. I can sadly not just move it into img.onload because this function test is used to upload and wont work otherwise.
Appreciating any tips.
EDIT:
Actual code I am using to resize an image before uploading.
Uploading takes place in the test function, after this code:
img.onload = function () {
URL.revokeObjectURL(this.src);
const [newWidth, newHeight] = calculateSize(img, MAX_WIDTH, MAX_HEIGHT);
const canvas = document.createElement("canvas");
canvas.width = newWidth;
canvas.height = newHeight;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, newWidth, newHeight);
canvas.style = "display: none";
canvas.toBlob(
(blob) => {
// Handle the compressed image. es. upload or save in local state
let file_small = new File([blob], name,{type:"image/jpeg", lastModified:new Date().getTime()});
let container = new DataTransfer();
container.items.add(file_small);
ajaxData.set('upload', container.files[0]);
console.log("END of the onload");
},
MIME_TYPE,
QUALITY
);
};
Below is the example, but you can update as you want.
async function test() {
const myImage = new Image(100, 200);
myImage.src = 'https://images.kiwi.com/airlines/128x128/0B.png';
document.body.appendChild(myImage);
await callFunction(myImage);
console.log('called second');
}
function callFunction(img) {
return new Promise((resolve, reject) => {
img.onload = () => {
//here you all code goes
console.log('called first');
resolve(true)
}
})
}
test()
please vote answer as a useful if this is a solution, thank you in advance.
Related
At first I thought it should be as easy as:
const img = document.createElement('img');
img.onload = () => { ... }
img.onerror = () => { ... }
img.src = url;
But then it turned out I need to draw it on a canvas, then toBlob(). And don't forget to add CORS and crossorigin="anonymous" to the img tag. Isn't it a bit too involved? Is there a better way?
To show you the final solution (you need CORS headers):
function getFileFromURL(url) {
return new Promise((resolve, reject) => {
const fileName = url.split('/').pop();
const img = document.createElement('img');
img.setAttribute('crossorigin', 'anonymous');
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
canvas.toBlob(blob => {
resolve(new File([blob], fileName));
});
};
img.onerror = () => {
reject('something went wrong');
};
img.src = url;
})
}
The solution suggested by CBroe is arguably better:
function getFileFromURL(url) {
const fileName = url.split('/').pop();
return fetch(url)
.then(response => {
return new File([response.data], fileName);
});
}
Make sure you add CORS headers to the request(s).
I'm trying to create a JavaScript function that will return a dataURL from a JPEG. This function is intended to be called multiple times in the creation of a pdf document in a Vue.js application.
The following is code I've managed to cobble together from various web sites in seeking code examples for jsPDF use.
async loadImg (url) {
var dataURL = null
var toDataURL = async function (url) {
var img = new Image()
img.onError = function () {
alert('Cannot load image: "' + url + '"')
}
img.onload = async function () {
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d')
canvas.height = this.naturalHeight
canvas.width = this.naturalWidth
context.drawImage(this, 0, 0)
dataURL = canvas.toDataURL('image/jpeg')
console.log('onload ' + dataURL)
}
img.src = url
}
await toDataURL(url)
console.log('end of function ' + dataURL)
return dataURL
}
I've tried using a callback approach, but no matter how what I've done I ultimately end up in the same state the console shows the 'end of function' as a null and then a few milliseconds later the onload remark shows up with a long string, which I assume is the dataURL of the graphic (jpg)
OK I thought async / await construct was the same as using promise.. but just to be on the safe side I rewrote my code using promise
toDataURL (url) {
return new Promise(function (resolve, reject) {
var img = new Image()
img.onError = function () {
reject(Error('Cannot load image: "' + url + '"'))
}
img.onload = async function () {
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d')
canvas.height = this.naturalHeight
canvas.width = this.naturalWidth
context.drawImage(this, 0, 0)
resolve(canvas.toDataURL('image/jpeg'))
}
img.src = url
})
}
// in the function to create the pdf
imageData = toDataURL(url).then(function (response) {
console.log('Success!', response)
}, function (error) {
console.error('Failed!', error)
})
}
There are three jpgs that the main is trying to include in the pdf
In the console I see:
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:277 PromiseĀ {<pending>}
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:284 841.89 595.28
Report.vue?./node_modules/babel-loader/lib!./node_modules/vue-loader/lib??vue-loader-options:272 Success! data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCALaBEcDASIAAhEBAxEB/8QAHgAAAAcBAQEBAAAAAAAAAAAAAAIDBAUGBw ...
There are two additional Success! data:image/ ...
My interpretation is while the results are different in that I get a promise object, which is pending then I get the image data. I'm still no further ahead.
i have this function to create new images to load them in memory
function preloadImgs() {
var imgDomain = "http://imgs.domain/";
if (document.images) {
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = imgDomain + "images/1/cover.jpg";
img2.src = imgDomain + "images/2/cover.jpg";
img3.src = imgDomain + "images/3/cover.jpg";
} }
// initialize the function
preloadImgs()
i need a way to know when all images in preloadImgs() function have been loaded , how can i achive that using the same code that i have provided ?
You can use a promise pattern.
https://developers.google.com/web/fundamentals/getting-started/primers/promises
Although these aren't natively supported everywhere, jQuery (tagged) provides an implementation:
https://api.jquery.com/promise/
Combined with img.onload to resolve the promises you can respond to an array of promises by:
var arrayOfPromises = [];
var $deferred = $.Deferred();
var img = new Image();
img.onload = function() {
// Check your support needs # http://caniuse.com/#search=naturalWidth
if (img.naturalWidth && img.naturalHeight) {
$deferred.resolve()
} else {
$deferred.reject();
}
};
img.onerror = $deferred.reject;
img.src = 'someimage.png';
arrayOfPromises.push($deferred);
$.when.apply($, arrayOfPromises).then(function() {
// your callback, images all loaded
alert('OK')
}, function() {
// Any failed image load occur
alert('FAIL')
});
I have been trying to draw a tile-like map to the canvas, although if i try to draw the same sprite twice, it will only render the final call of drawImage. Here is my code if it helps :
window.onload = function(){
function get(id){
return document.getElementById(id);
}
var canvas = get("canvas");
ctx = canvas.getContext("2d");
canvas.width = 160;
canvas.height = 160;
grass = new Image();
water = new Image();
grass.src = "res/Grass.png";
water.src = "res/Water.png";
path = {
draw: function(image,X,Y){
X = (X*32)-32;
Y = (Y*32)-32;
image.onload = function(){
ctx.drawImage(image,X,Y);
}
},
}
path.draw(water,2,2);
path.draw(grass,1,2);
path.draw(grass,1,1);
path.draw(water,2,1);
}
You're overwriting onload on the image next time you call it.
Think of it this way:
var image = {
onload: null
};
// Wait for the image to "load"
// Remember, onload is asynchronous so it won't be called until the rest of
// your code is done and the image is loaded
setTimeout(function() {
image.onload();
}, 500);
image.onload = function() {
console.log('I will never be called');
};
image.onload = function() {
console.log('I overwrite the previous one');
};
Instead, you might try waiting for your images to load then do the rest of your logic.
var numOfImagesLoading = 0;
var firstImage = new Image();
var secondImage = new Image();
// This is called when an image finishes loading
function onLoad() {
numOfImagesLoading -= 1;
if (numOfImagesLoading === 0) {
doStuff();
}
}
function doStuff() {
// This is where you can use your images
document.body.appendChild(firstImage);
document.body.appendChild(secondImage);
}
firstImage.src = 'http://placehold.it/100x100';
firstImage.onload = onLoad;
numOfImagesLoading += 1;
secondImage.src = 'http://placehold.it/200x200';
secondImage.onload = onLoad;
numOfImagesLoading += 1;
I need to be able to wait to start a function until the height and width of an image are available. When this code calls start(), it still says the height and width are zero:
var img = new Image();
init = function() {
img.onload = this.start();
img.src = "sky.jpg";
}
start = function() {
alert("Start called.\nwidth:"+img.width+"\nheight"+img.height);
}
init();
How can I make it wait until the dimensions are available before calling start()?
var img = new Image();
var start = function() {
alert("Start called.\nwidth:"+img.width+"\nheight"+img.height);
}
var init = function() {
img.onload = start;
img.src = "sky.jpg";
}
init();
Change this.start() to start.
I also scoped your functions.