Get image width and height. src is proxy - javascript

I have a Vue component, in it I have an img, I need to get that image dimensions, preferably before showing the image (to fit in the container either by width or height).
this.img = new Image();
this.img.src = this.payload.src;
this.img.onload = () => {
let width = this.img.naturalWidth;
let height = this.img.naturalHeight;
}
That code might not work, the image src can return 401 (not sure yet), we use proxy and get that file from a storage bucket on the server. like /api/getStorageResource?blob=
What can I do?
Having the link, can I somehow fetch image through axios and set it as an element, instead of <img src=".." />
As an option I see that I can probably have the element as it is now <img src="/api/getStorageResource?blob=" /> and getElementById it and get the width and height... Not sure what my options here.

You can use a combination of async/await and the fetch api inside a try/catch block to get the image URL from your server and then you can proceed with creating the <img> element, assign the retrieved image URL to the img element src and finally set the width and height of the container equal to the img element's width and height.
In the following example Code Snippet, I have added a button which will add the image to the container on click so you can see how the container has the retrieved image dimensions even before the image is rendered on the DOM:
const imgDiv = document.querySelector('#imgDiv');
const btn = document.querySelector('#imgBtn');
//The fetchImg function will fetch the image URL from the server and log error to console if file not found
const fetchImg = async () => {
try {
// replace the following example url with "this.payload.src"
const imgURL = await fetch('https://picsum.photos/id/237/200/200');
return imgURL;
} catch (err) {
// do something here if image not found
console.log('Image not found!');
}
}
fetchImg().then((res) => {
// create a new image element with the URL as the src
const img = new Image();
img.src = res.url; // change ".url" according to how the data you get from your server is structured
// assign the retrieved width and height of img to container
img.addEventListener('load', () => {
imgDiv.style.width = img.naturalWidth + 'px';
imgDiv.style.height = img.naturalHeight + 'px';
});
btn.addEventListener('click', () => imgDiv.appendChild(img));
});
html, body {margin: 0;padding: 10px;width: 100%; height: 100%;text-align: center;}
#imgDiv {background-color: #222;margin: 0 auto;}
<button id="imgBtn">Add Image</button>
<div id="imgDiv"></div>
JSFiddle with above code: https://jsfiddle.net/AndrewL64/2hu3yxtq/104/

Related

html2canvas cuts off div overflow

I am trying to download the entire contents of a div including the overflow using html2canvas, but the image is always cropped.
Here is a fiddle link with example code:
https://jsfiddle.net/50x2gfne/1/
function grab() {
const captureElement = document.querySelector('#capture')
html2canvas(captureElement)
.then(canvas => {
canvas.style.display = 'none'
document.body.appendChild(canvas)
return canvas
})
.then(canvas => {
const image = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream')
const a = document.createElement('a')
a.setAttribute('download', 'my-image.png')
a.setAttribute('href', image)
a.click()
canvas.remove()
})
capture.style.oveflow = 'scroll'
}
I have tried changing the width of the html and body to a wider size, setting the viewport wider, and changing the width of the canvas function.
I also tried the method in the following link for handling horizontal overflow with no luck:
https://blog.logrocket.com/export-react-components-as-images-html2canvas/

Can't retrieve the correct image size using javascript

Ok, first time posting a question so I hope I do everything by the book.
I wanted to use javascript to retrieve and display the width/height of an image that the user picks using an Input button. But it's a hot day here and I can feel my brain just going round in circles.
Using this script I get it to display the image and the width (or so I thought). I try to select an image which is 180px wide but my script returns the number 16. Other images either returns 16 or other arbitrary numbers.
EDIT: Different browsers return different numbers.
What am I displaying and what am I missing to get it right?
This is my HTML:
<head>
<title>Image size calculator</title>
<!-- Normalize.css -->
<link rel="stylesheet" type="text/css" href="normalize.css">
<!-- Custom Styles -->
<link rel="stylesheet" type="text/css" href="style.css">
<!--[if IE]>
<script
src="https://github.com/aFarkas/html5shiv/blob/master/src/html5shiv.js">
</script>
<![endif]-->
</head>
<body>
<h1>Vilken storlek har min bild?</h1>
<p><em>Supported formats are: BMP/CUR/GIF/ICO/JPEG/PNG/PSD/TIFF/WebP/SVG/DDS</em></p><br><br>
<form>
<input type="file" onchange="readURL(this);">
<br><br>
<img id="imageDisplay" src="#" />
<label id="imageSize">Bilden är: </label>
</form>
<!-- How to load jQuery.
1. Load the CDN version (internet)
2. If fail, load the installed (local) -->
<script
src="http://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="js/jquery-3.3.1.js"><\/script>');</script>
<script src="js/script.js"></script>
</body>
And this is the code in my script.js file:
function readURL(input) {
var reader = new FileReader();
reader.onload = function (e) {
$('#imageDisplay')
.attr('src', e.target.result)
.width(this.width)
.height(this.height);
};
reader.readAsDataURL(input.files[0]);
var imgWidth = $("#imageDisplay").width();
$("#imageSize").append(imgWidth);
}
EDIT 2: Thanks for all the solutions. I tried out Scott's (which made me smack my forehead and say 'duh' when I read it). It seems I was on the right track as my suspicion was that the image wasn't fully loaded. I just couldn't get the code for checking that right. Sometimes you need a little push in the right direction. :)
You are attempting to get the size immediately after setting the src and it hasn't had enough time for the image to finish loading by then. You need to set up a load event handler for the image in addition to the one for the reader.
var imgWidth = null;
var imgHeight = null;
// The image needs a load callback that won't fire until the image
// is finished downloading.
$('#imageDisplay').on("load", function(){
// Now, it's safe to get the width and height >
imgWidth = this.width;
imgHeight = this.height;
$("#imageSize").append(imgWidth);
});
function readURL(input) {
var reader = new FileReader();
reader.onload = function (e) {
$('#imageDisplay').attr('src', e.target.result)
};
reader.readAsDataURL(input.files[0]);
}
You should use HTMLImageElement.naturalWidth and HTMLImageElement.naturalHeight instead of just image.width and image.height.
While the former returns the original/intrinsic width or height of the image, the latter will return the dimensions of the image as displayed. That means that if you are resizing it with CSS, you will get the values based on that.
Also, the image itself also has an image.onload event you should listen to in order to access its properties once it has finished loading. Otherwise, you might access them before and get incorrect values.
Anyway, image.naturalWith and image.naturalHeight are available before the onload event is triggered, so you might try to get them instead, but in order to have a reliable solution, you need to implement polling using WindowOrWorkerGlobalScope.setInterval.
You can check this out in this example:
const input = document.getElementById('input');
const button = document.getElementById('button');
const image = document.getElementById('image');
const naturalSize = document.getElementById('naturalSize');
const displaySize = document.getElementById('displaySize');
let intervalID = null;
function updateSizeLabels() {
naturalSize.innerText = `ORIGINAL SIZE: ${ image.naturalWidth } × ${ image.naturalHeight }`;
displaySize.innerText = `DISPLAY SIZE: ${ image.width } × ${ image.height }`;
};
function changeImage(src) {
console.clear();
// Reset src so that you don't get the previus dimensions
// if you load more than one image:
image.src = '';
image.src = src;
// Solution with polling:
// We try to get the dimensions just after setting src:
const alreadyAvailable = pollNaturalDimensions();
// Polling only needed if the dimensions are not there yet:
if (!alreadyAvailable) {
// Just in case we already have a setInterval running:
clearInterval(intervalID);
// Every 10ms, we check again if naturalWidth is there:
intervalID = setInterval(pollNaturalDimensions, 10);
}
}
function pollNaturalDimensions() {
if (image.naturalWidth) {
console.log('Dimensions already available.');
// Stop polling:
clearInterval(intervalID);
// You can display the dimensions already:
updateSizeLabels();
return true;
}
}
// Solution with onload:
// This will update the size labels everytime an image is loaded:
image.onload = () => {
console.log('Image loaded.');
updateSizeLabels();
};
input.onchange = () => {
const reader = new FileReader();
reader.onload = (e) => changeImage(e.target.result);
reader.readAsDataURL(input.files[0]);
}
button.onclick = (e) => {
e.preventDefault();
changeImage(image.src === 'http://www.traversecityfilmfest.org/wp-content/uploads/2017/07/Baby-driver-gif.gif' ? 'https://thumbs.gfycat.com/BleakTenderBarb-small.gif' : 'http://www.traversecityfilmfest.org/wp-content/uploads/2017/07/Baby-driver-gif.gif');
};
body {
padding: 0 0 45px;
}
body,
input,
button {
font-family: monospace;
}
.as-console-wrapper {
max-height: 45px !important;
}
#image {
max-width: 200px;
max-height: 200px;
margin: 16px 0 0;
}
<h1>Select an image to see its dimensions:</h1>
<form>
<input id="input" type="file">
<button id="button">Load Remote Image</button>
</form>
<img id="image" />
<p id="naturalSize"></p>
<p id="displaySize"></p>
Note, that when working with local images, the image.onload solution might return the dimensions before the polling one. When testing it on my Mac, they seem to be head-to-head. However, if you try loading remote images, especially big ones and/or on slow networks, the polling solution would make more sense.
I would suggest restructuring your code like so, so that your image loads before you set the width:
function readURL(input) {
var reader = new FileReader();
var imgWidth;
reader.onload = function (e) {
$('#imageDisplay')
.attr('src', e.target.result)
.width(this.width)
.height(this.height);
imgWidth = $("#imageDisplay").width();
};
reader.readAsDataURL(input.files[0]);
$("#imageSize").append(imgWidth);
}
A simple approach would be
var img = new Image();
img.onload = function(){console.log(img.width,img.height)}
img.src = 'url'
As correctly pointed out by LGSon, you have to wait for onLoad.
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
var image = new Image();
image.src = e.target.result;
image.onload = function () {
var height = this.height;
var width = this.width;
}
}
clientWidth and clientHeight are DOM properties that show the current in-browser size of the inner dimensions of a DOM element, excluding margin and border. So this will get the actual dimensions of the visible image.
var img = document.getElementById( 'imageDisplay' );
var width = img.clientWidth;
var height = img.clientHeight;
If you are requesting image sizes you have to wait until they load or you will only get zeroes:
Example:
var img = new Image();
img.onload = function() { console.log(this.width + ' x ' + this.height ) }
img.src = 'http://www.google.com/intl/en_ALL/images/logo.gif';

Render background-image to canvas

I have a DIV container with an image set as background-image (specified by url). Is there any way how to redraw this background-image into canvas (without re-contacting or re-downloading from server -- as the content image is once-to-show)?
Yes. You can get the URL of the background image, request the image file as Blob, create an <img> element, set src to Blob URL of response, at load event of created img element call .drawImage() with img as first parameter.
The background image could be cached, depending on browser settings. If the image is not cached at browser, you can request image first, then set both css background-image and <canvas> using single request.
You can use fetch() or XMLHttpResponse() to request background-image url(), FileReader(), FileReader.prototype.readAsDataURL() to set src of <img> to .result of FileReader at load event.
You can also use URL.createObjectURL() to create a Blob URL from response Blob. URL.revokeObjectURL() to revoke the Blob URL.
Check Network tab at DevTools or Developer Tools, the image should be retrieved
(from cache)
#bg {
width:50px;
height:50px;
background-image:url(http://placehold.it/50x50);
}
<div id="bg"></div>
<br>
<canvas id="canvas" width="50px" height="50px"></canvas>
<script>
window.onload = function() {
var bg = document.getElementById("bg");
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image;
img.width = canvas.width;
img.height = canvas.height;
img.onload = (e) => ctx.drawImage(e.target,0,0);
var background = getComputedStyle(bg).getPropertyValue("background-image");
var src = background.replace(/^url|["()]/g, "");
var reader = new FileReader;
reader.onload = (e) => img.src = e.target.result;
fetch(src)
.then(response => response.blob())
.then(blob => {
reader.readAsDataURL(blob);
// const url = URL.createObjectURL(blob); img.src = url;
})
}
</script>

Using css-background image with img element

Is it possible to load image data stored in img element into a css background-image property?
For example, assume that we have downloaded image data into 'img' element
var img = Image();
img.src = '/foo/bar'
img.onload = ....
Then, I'd like to load that image to the css background-image property
.something {
background-image: img
}
Is this possible? Mixing using image Element and css background-image property so that CSS can use image data in img element as a background-image
Edit: This first answer was only ever meant to address the original question asked around working with an image element. Scroll down for a better alternative to fetching image data.
If you are trying to safely capture the raw data to use at a later point, you can draw the image onto a canvas element in order to generate a base-64 encoded data-URL. Though this solution will be subject to same-origin restrictions.
see: MDN: Allowing cross-origin use of images and canvas
const getImageData = imageElement => {
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.width = imageElement.width
canvas.height = imageElement.height
ctx.drawImage(imageElement, 0, 0)
return canvas.toDataURL()
}
const img = new Image
img.addEventListener('load', () =>
// assign to some CSS rule
console.log(getImageData(img))
)
img.src = '/foo/bar'
Reading between the lines however your comment, "wouldn't that make the browser download the image twice?" sounds like a misunderstanding - browsers already cache resources and you can reuse asset URLs in any context in your page (i.e. HTML / CSS / JS) and unless explicitly circumvented, rely on them only being downloaded once.
Alternatively, it would be cleaner to load the image as a Blob.
Note: I'm using a CORS proxy here purely to facilitate a runnable example. You probably wouldn't want to pass your own assets through an arbitrary third-party in a production environment.
see: MDN: Cross-Origin Resource Sharing
const getImage = async url => {
const proxy = 'https://cors-anywhere.herokuapp.com/'
const response = await fetch(`${proxy}${url}`)
const blob = await response.blob()
return URL.createObjectURL(blob)
}
const imageUrl =
'https://cdn.sstatic.net/Sites/stackoverflow/' +
'company/img/logos/so/so-logo.png?v=9c558ec15d8a'
const example = document.querySelector('.example')
getImage(imageUrl).then(objectUrl =>
example.style.backgroundImage = `url(${objectUrl})`
)
.example {
min-height: 140px;
background-size: contain;
background-repeat: no-repeat;
}
<div class="example"></div>
You can do this with JQuery
var img = new Image();
img.src = 'http://placehold.it/350x150';
$('div').css('background-image', 'url('+img.src+')');
div {
height: 150px;
width: 300px;
background-size: cover;
background-position: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
Or pure Javascript
var img = new Image();
img.src = 'http://placehold.it/350x150';
document.getElementById('element').style.backgroundImage = "url("+img.src+")";
div {
height: 150px;
width: 300px;
background-size: cover;
background-position: center;
}
<div id="element"></div>

How to get new image size after changing src in IE

I have an image element with a spinner gif. I later dynamically change the src of that image with jQuery, and I want to get the actual width and height of the new image. Here is my code:
function loadImage() {
$('#uploaded-image').load(function() {
var img = document.getElementById('uploaded-image');
// These statements return the correct values in FF and Chrome but not IE
imgWidth = img.clientWidth;
imgHeight = img.clientHeight;
});
$('#uploaded-image').removeAttr('width').removeAttr('height').attr('src', imgUrl);
}
This works perfectly in Chrome and Firefox, but IE always returns the size of the original spinner gif instead of the new image.
How can I get the width and height of the new image in IE?
Notes:
I've tried the jQuery .width() and .height() methods in addition to the pure Javascript approach, with the same results.
The .load event is being fired as expected in all browsers.
Use offsetHeight and offsetWidth in IE.
Check this out in your IE: http://jsbin.com/abopih/5
var imgUrl = "http://www.google.com/intl/en_com/images/srpr/logo3w.png";
var img = document.getElementById('uploaded-image');
$('#uploaded-image').click(function() {
imgWidth = img.clientWidth;
imgHeight = img.clientHeight;
$('#uploaded-image').removeAttr('width').removeAttr('height').attr('src', imgUrl);
console.info('clientWidth: ', img.clientWidth);
console.info('clientHeight: ', img.clientHeight);
console.info('offsetWidth: ', img.offsetWidth);
console.info('offsetWidth: ', img.offsetWidth);
});
You could create a hidden div and place the image in there in order to get the size. Just make sure the hidden div isn't done with display:none, because then it will have no dimensions. Use visibility:hidden, grab the dimensions, and then remove it.
Make sure you remove css height and width as well:
$('#uploaded-image').css({ height: "auto", width: "auto" });
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="file" id="file" />
<script type="text/javascript" charset="utf-8">
$("#file").change(function(e) {
var file, img;
file = document.getElementById("file");
if (file!=null) {
img = new Image();
img.src = file.value;
img.onload = function() {
alert(img.width + " " + img.height);
};
img.onerror = function() {
alert( "not a valid file: " + file.type);
};
}
});
</script>
It seems to be a cache problem.
Add this to your image source url:
imgUrl += new Date().getTime();

Categories

Resources