I don't need an image inside the DOM, but need to get it's dimensions.
Trying to create a temporary img element this way:
let src = 'images/01.jpg';
let img = document.createElement('img');
img.src = src;
let w = img.naturalWidth;
let h = img.naturalHeight;
console.log(w); // 0
console.log(h); // 0
$(img).remove();
Result is 0 and 0. The real dimensions are 1349 and 250.
How can I get them, without disturbing existing page layout?
You have to wait for the image to be loaded:
let src = 'http://www.fillmurray.com/g/200/300';
let img = document.createElement('img');
img.onload = () => {
console.log(img.naturalWidth);
console.log(img.naturalHeight);
}
img.src = src;
Until loaded, there's no way for the browser to know the dimensions of your image. If you need the dimensions in another function, you either have to use a callback parameter or return a Promise:
let src = 'http://www.fillmurray.com/g/200/300';
// with a callback
const loadImgCallback = (url, callbackFn) => {
let img = document.createElement('img');
img.onload = () => { callbackFn(img); }
img.src = src;
}
// with Promise
const loadImgPromise = url => new Promise((ok, fail) => {
let img = document.createElement('img');
img.onload = () => { ok(img); }
img.onerror = fail;
img.src = url;
});
/* === USAGE EXAMPLES === */
loadImgCallback(src, img => {
console.log(`callback, naturalWidth: ${img.naturalWidth}`);
console.log(`callback, naturalHeight: ${img.naturalHeight}`)
});
loadImgPromise(src).then(img => {
console.log(`Promise, naturalWidth: ${img.naturalWidth}`);
console.log(`Promise, naturalHeight: ${img.naturalHeight}`)
});
It's up to you to decide which solution you prefer.
Just assign it to the image object and for image to load :)
let src = 'https://images.pexels.com/photos/428338/pexels-photo-428338.jpeg';
var img = new Image();
//img = document.createElement('img');
img.src = src;
img.onload = function() {
let w = img.naturalWidth;
let h = img.naturalHeight;
console.log(w); // 0
console.log(h); // 0
}
//$(img).remove();
Related
I have two functions,
One function converts images from whatever format to canvas, then to WEBP image format.
The other function receives the image and console.log the image.
The problem is that when I log the result of the image in the saveToBackend function, I get a result of undefined, but when I console.log the converted image in the convertImage function, I get the image. Please how can i solve the issue?
Below are the functions in my NUXT app
convertImage(file) {
// convert image
let src = URL.createObjectURL(file)
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d')
let userImage = new Image();
userImage.src = src
userImage.onload = function() {
canvas.width = userImage.width;
canvas.height = userImage.height;
ctx.drawImage(userImage, 0, 0);
let webpImage = canvas.toDataURL("image/webp");
console.log(webpImage); // with this i get the image in the console
return webpImage
}
},
async saveToBackend(file, result) {
// convert images to webp
let webpFile = await this.convertImage(file)
console.log(webpFile); // with this i get undefined in the console
}
You can't return webpImage from inside the onload callback. It simply will not wait for onload to execute. You can instead create a Promise that resolves the value of webpImage when onload completes:
convertImage(file) {
return new Promise((resolve) => {
// convert image
let src = URL.createObjectURL(file);
let canvas = document.createElement("canvas");
let ctx = canvas.getContext("2d");
let userImage = new Image();
userImage.src = src;
userImage.onload = function() {
canvas.width = userImage.width;
canvas.height = userImage.height;
ctx.drawImage(userImage, 0, 0);
let webpImage = canvas.toDataURL("image/webp");
console.log(webpImage); // with this i get the image in the console
// resolve promise so that saveToBackend can await it
return resolve(webpImage);
};
});
}
Hopefully that helps!
Ho do I use getBoundingClientRect() to determine the width and height of image
function popUp() {
var img = document.createElement('img');
img.id = "TT";
img.src = "https://picsum.photos/200/300";
document.body.appendChild(img);
var img = document.getElementById("TT");
console.log(img)
var dims = img.getBoundingClientRect();
console.log(dims)
}
popUp();
This is because the image is not loaded at the time of log. You need to console.log inside onload function
function popUp() {
var img = document.createElement('img');
img.id="TT";
img.src="https://picsum.photos/200/300";
document.body.appendChild(img);
img.onload = function(){
var img = document.getElementById("TT");
var dims = img.getBoundingClientRect();
console.log(dims)
}
}
popUp();
If you want the function to await for further execution of the function until the image is loaded you can create a promise and then await for it. But this will make the structure of the code asynchronous
async function popUp() {
var img = document.createElement('img');
img.id="TT";
img.src="https://picsum.photos/200/300";
document.body.appendChild(img);
const imageLoaded = new Promise((res, rej) => {
img.onload = function(){
var img = document.getElementById("TT");
var dims = img.getBoundingClientRect();
res(dims)
}
});
const data = await imageLoaded;
console.log(data)
}
popUp();
I am attempting to read images from a list of paths (10 items long) returned via an ajax function, resize each one and then display one after the other on the page. However, the below code only displays the first image (resized) and none of the others. I am fairly sure the resizing works as the printed sizes look correct. Below is my code in JavaScript:
// Helper function
function scaleSize(maxW, maxH, currW, currH){
var ratio = currH / currW;
if(currW >= maxW && ratio <= 1) {
currW = maxW;
currH = currW * ratio;
} else if(currH >= maxH) {
currH = maxH;
currW = currH / ratio;
}
return [currW, currH];
}
function get_similar_images(image_name) {
console.log(image_name)
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "/get_similar",
dataType: "json",
async: true,
data: JSON.stringify({name: image_name}),
success: function(data) {
console.log(data);
image_list = data['images']
category = data['category']
for (var i=0; i<image_list.length; i++) {
var img = document.createElement("img");
img.src = "static/products/"+category+"/"+image_list[i];
var actualH;
var actualW;
var newH;
var newW;
img.onload = function(){
actualW = this.width;
actualH = this.height;
console.log(actualW, actualH)
var newSize = scaleSize(300, 300, actualW, actualH);
console.log(newSize)
img.width = newSize[0];
img.height = newSize[1];
document.getElementById('imageDiv').appendChild(img)
};
}
},
error: function(xhr, status, error) {
// console.log(xhr.responseText);
}
})
}
If we simplify your for loop like that :
var img = document.createElement("img");
img.src = image_list[0];
img.onload = function() {
console.log(img);
};
var img = document.createElement("img");
img.src = image_list[1];
img.onload = function() {
console.log(img);
};
You will understand where your mistake comes from. onload is an event, which means it's running asynchronously from your your code. You're updating img value with a new image element (and a new href attribute) before the first load event is triggered. Thus the onload event is actually called after the last update of the img variable, which actually happen when your reach the last loop of your for.
Why ?
Because a for loop doesn't create a new scope for the variable img, so each time you touch the img var, even if you're putting var in front, the original img is updated.
You should then use a function, because a function actually create a new scope for variables that are declared (var myVar) inside it :
function injectAndResize(imageUrl) {
var img = document.createElement("img");
img.src = imageUrl;
var actualH;
var actualW;
var newH;
var newW;
img.onload = function() {
actualW = this.width;
actualH = this.height;
var newSize = scaleSize(300, 300, actualW, actualH);
this.width = newSize[0];
this.height = newSize[1];
document.getElementById('imageDiv').appendChild(img);
};
}
for (var i = 0; i < image_list.length; i++) {
injectAndResize(image_list[i]);
}
There are a few issues in your code.
1) Image width and height are CSS properties and may be undefined in your img.onload handler. Use this.naturalWidth instead.
2) img.src = 'url'; triggers image load. Place it after img.onload.
success: function(data) {
console.log(data);
image_list = data.images;
category = data.category;
for (var i=0; i<image_list.length; i++) {
var img = document.createElement("img");
img.onload = function(){
var actualW = this.naturalWidth;
var actualH = this.naturalHeight;
console.log(actualW, actualH)
var newSize = scaleSize(300, 300, actualW, actualH);
console.log(newSize)
this.width = newSize[0];
this.height = newSize[1];
//also think what to do with images which are already there
//probably remove
document.getElementById('imageDiv').appendChild(this)
};//img.onload
img.src = "static/products/"+category+"/"+image_list[i];
}//for
},//success
I know we can use this code to enable CORS on a single image
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var img = new Image();
img.crossOrigin = '';
img.src = 'http://crossdomain.com/image.jpg';
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.width, img.height);
Is there any way to do it for multiple image URLs at once?
URL array
To load several images enabling CORS request, you can use an array which is practical for this purpose.
One thing to be aware of is that requesting CORS can be denied by server. The browser may fail loading the image in those cases so you will need to know in advance if CORS need to be requested or not.
Example loader
var urls = [url1, url2, url3, ...]; // etc. replace with actual URLs
var images = []; // store the loaded images
var i = 0, len = urls.length;
var count = len; // for load and error handlers
for(; i < len; i++) {
var img = new Image();
img.onload = loadHandler;
img.onerror = img.onabort = errorHandler;
img.crossOrigin = ""; // enable CORS request
img.src = urls[i]; // set src last
images.push(img); // store in array
}
function loadHandler() {
if (!--count) callback(); // loading done
}
function errorHandler() {
// handle errors here
loadHandler(); // make sure to update counter/callback
}
function callback() {
// ... ready, continue from here
}
Demo
var urls = [
"http://i.imgur.com/0LINzxs.jpg", // random urls from imgur..
"http://i.imgur.com/6ksiMgS.jpg",
"http://i.imgur.com/aGQSLi9.jpg"
];
var images = []; // store the loaded images
var i = 0, len = urls.length;
var count = len; // for load and error handlers
for(; i < len; i++) {
var img = new Image();
img.onload = loadHandler;
img.onerror = img.onabort = errorHandler;
img.crossOrigin = ""; // enable CORS request
img.src = urls[i]; // set src last
images.push(img); // store in array
}
function loadHandler() {
if (!--count) callback(); // loading done
}
function errorHandler() {
// handle errors here
loadHandler(); // make sure to update
}
function callback() {
// ... ready, continue from here
console.log(images);
var ctx = document.querySelector("canvas").getContext("2d");
ctx.drawImage(images[0], 0, 0);
ctx.drawImage(images[1], 0, 0);
ctx.drawImage(images[2], 0, 0);
console.log(ctx.canvas.toDataURL()); // OK if CORS is OK!
}
<canvas></canvas>
Put the srcs in an array and iterate over them
working fiddle
https://jsfiddle.net/ps50po4z/
This allows you to use multiple images from different sources and display them.
use this function as a template to iterate through all the cors src images
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
}
</style>
</head>
<body>
<canvas id="myCanvas" width="578" height="200"></canvas>
<script>
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var sources = {
darthVader: 'http://www.html5canvastutorials.com/demos/assets/darth-vader.jpg',
yoda: 'http://www.html5canvastutorials.com/demos/assets/yoda.jpg'
};
loadImages(sources, function(images) {
context.drawImage(images.darthVader, 100, 30, 200, 137);
context.drawImage(images.yoda, 350, 55, 93, 104);
});
</script>
</body>
</html>
I'm trying to get the width of an image after a change in the height and I only have the URL (the image is not in a tag in the HTML) so I tried this code:
var img = document.createElement("img");
img.src = "./images/niceImages.jpg";
img.style.height = "200px";
console.log(img.clientWidth);
But the property "clientWidth" returned 0. I know that maybe with JQuery it is a lot easier however, I want do it with pure Javascript.
Does anyone know if this can be done? Or at least how to get the aspect ratio or width and height of the image URL?
If you append the image you create to the DOM then you should find that the width is then calculated and is then available as normal
var img = document.createElement("img");
img.src = "http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png";
img.onload = function(){
img.style.height = "200px";
img.style.visibility = 'hidden';
document.body.appendChild(img);
console.log(img.clientWidth);
}
I figured out some better solutions -
Javascript
function loadImage() {
const imgSrc = 'https://cdn.pixabay.com/photo/2015/03/26/09/47/sky-690293_1280.jpg'
const img = new Image();
img.src = imgSrc;
img.onload = function() {
document.body.appendChild(img); // You can get the image ratio without inserting the image in body
alert('Image ratio' + img.width/img.height);
}
}
<button onClick="loadImage()">Load Image</button>
Angular (Typescript) - ( Snippet will not run because of angular unavailability )
public getImageRatio(url: string): Promise <any> {
return Promise((resolve, reject) => {
const img = new Image();
img.src = url;
img.onload = function() {
resolve(img.width / img.height);
};
img.onerror = function() {
reject('Image failed to load');
};
});
}
async loadImage(url) {
const imageRatio = await getImageRatio('image-url-here');
alert(imageRatio);
}
loadImage(url);