I want to preload my images but don't necessarily want to append them to the DOM. If I preload an image will it effectively be precached?
My understanding is that caching works by reference to the src of an image. From playing around it seems as if preloading in order to precache does indeed work. However, I'm not sure; this may be unreliable or have some negative effect on performance.
I'm using the image constructor to preload.
const img = new Image();
img.src = imageArray[i].url;
Yes, it does indeed work.
function preloadImage(url){
const img = new Image();
img.src = imageArray[i].url;
}
However, there is a couple of approach. For example, delaying preloading until after the page loads:
function preloader() {
if (document.images) {
var img1 = new Image();
var img2 = new Image();
var img3 = new Image();
img1.src = "http://url/image-001.jpg";
img2.src = "http://url/image-002.jpg";
img3.src = "http://url/image-003.jpg";
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {
oldonload();
}
func();
}
}
}
addLoadEvent(preloader);
Related
I have this piece of javascript that is taking several images and waiting for them all to load to then performs some logic on them. This is a stripped out piece of code that is still not doing what is expected:
$(window).on('load', function() {
var art = new Image();
var top_image = new Image();
var bottom_image = new Image();
var left_image = new Image();
var right_image = new Image();
art.onload = function() {
console.log("1");
top_image.onload = function() {
console.log("2");
bottom_image.onload = function() {
console.log("3");
right_image.onload = function() {
console.log("4");
left_image.onload = function() {
console.log("5");
}
}
}
}
}
art.src = 'images/prototype.jpg';
top_image.src = 'images/2_4_top_center.png';
bottom_image.src = 'images/2_4_bottom_center.png';
right_image.src = 'images/2_4_middle_right.png';
left_image.src = 'images/2_4_middle_left.png';
});
The problem is that when I hard reload the browser a seemingly random number logs appear in the console. It mostly only logs "1" but sometimes gets all the way to logging "5". I'm not sure what's happening here and why it only behaves as expected sometimes.
You just append an onclick handler to top_image when art has already loaded, therefore if top_image loads before art, the second handler would not be triggered. To solve this, attach handlers to all images before you set their source, and use promises to handle them:
const onload = img => new Promise(res => img.onload = res);
var art = new Image();
var top_image = new Image();
var bottom_image = new Image();
var left_image = new Image();
var right_image = new Image();
Promise.all([art, top_image, bottom_image, left_image, right_image].map(onload))
.then(() => {
// All loaded !!
});
// Add sources
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);
I have an array of enemies sent from the server and I am recreating them because they were serialized. After, I'm trying to get them to render on the canvas, but that isn't working for some reason.
for (var i = 0; i < enemies.length; i++) { // recreate each enemy and render it
var image = new Image();
var currentFish = new Fish();
for (var key in enemies[i]) { // copying properties to object that has the necessary methods
if (enemies[i].hasOwnProperty(key)) {
currentFish[key] = enemies[i][key];
}
}
image.src = currentFish.icon;
image.onload = function () {
ctx.drawImage(image, currentFish.position.x, currentFish.position.y);
};
ctx.fillText('Drone', 250, 200);
}
I think the issue is that image.onload is not called until after or in between frames so it isn't seen. I'm not sure how to work around this.
Edit:
I forgot to mention that I'm using requestAnimationFrame to handle rendering the canvas, so I don't know when the frame is going to be rendered.
The problem is that your outside for loop is overwriting the image variable with each loop (before the image can be loaded and drawn).
Try this alternative image loader which preloads all images into imgs[] and then calls start():
// image loader
var imageURLs = []; // put the paths to your images here
var imagesOK = 0;
var imgs = [];
imageURLs.push("");
loadAllImages(start);
function loadAllImages(callback) {
for (var i = 0; i < imageURLs.length; i++) {
var img = new Image();
imgs.push(img);
img.onload = function() {
imagesOK++;
if (imagesOK >= imageURLs.length) {
callback();
}
};
img.onerror = function() {alert("image load failed");}
img.crossOrigin = "anonymous";
img.src = imageURLs[i];
}
}
function start() {
// imgs[] holds fully loaded images
// imgs[] is in the same order as imageURLs[]
}
I have an image uploader in my drawing application that I've written in Javascript. I want to allow the user to place multiple of the same image on the canvas. However, when I try to upload an image that's already on the canvas, nothing happens and a breakpoint in my event handler for the uploader never gets hit. What's going on and how can I fix it? Thanks!
Here's the code for my image handler:
function handleImage(e) {
var reader = new FileReader();
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
img.className = 'drag';
img.style.left = 0;
img.style.top = 0;
context.drawImage(img, parseInt(img.style.left, 10) , parseInt(img.style.top, 10));
images.push(img);
}
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
};
I do tend to agree with Rene Pot to use the same image again (duplicate button), but you still can't prevent the user from inserting/loading the same image again. I've encountered the problem a while ago and used this bit of code to check if the image is already cached (if cached, there is no load, hence the onload won't fire either).
var img = new Image();
img.src = event.target.result;
var insertImage = function() {
img.className = 'drag';
img.style.left = 0;
img.style.top = 0;
context.drawImage(img, parseInt(img.style.left, 10) , parseInt(img.style.top, 10));
images.push(img);
}
if(img.complete){
img.onload = insertImage;
} else {
insertImage();
}
Hope that helps.
I'm using html5 to create drag and drop image upload functionality. This works great for me in firefox but in chrome the image onload event only fires the first time. If I drag multiple images in only the first works and if I drag a second in it fails. I believe the problem is with the image onload.
here is the way my code works I have removed the irrelevant sections:
var img = document.createElement("img");
var reader = new FileReader();
var canvas = document.createElement("canvas");
var canvasData;
var ctx = canvas.getContext("2d");
var myFiles;
var i = 0;
reader.onload = (function (aImg)
{
return function (e)
{
aImg.src = e.target.result;
};
})(img);
img.onload = function (){
//resizes image
//draws it to the canvas
//posts to server
i++;
if(i < myFiles.length){
processNext(i);
}
}
function processNext(filei) {
var file = myFiles[filei];
img.file = file;
reader.readAsDataURL(file);
}
i = 0;
myFiles = files;
processNext(0);
Does anyone know why this works in firefox but not chrome?
Explanation from chromium tracker:
This is not a bug. WebKit is just more strict. You must instantiate a new Image() object before the replacement, like this:
var photo = document.getElementById('image_id');
var img = new Image();
img.addEventListener('load', myFunction, false);
img.src = 'http://newimgsource.jpg';
photo.src = img.src;
source: http://code.google.com/p/chromium/issues/detail?id=7731#c12
This is strange, none of the above worked for me. I was defining the image variable as local and change it to global and it started working. Does this make sense? Can somebody explain it?
This didnt worked for me:
function loadImage() {
var ImageToLoad = new Image();
ImageToLoad.onload = function() {
console.log("finish loading");
};
ImageToLoad.src = "myimage.png";
}
This did work:
var ImageToLoad = new Image();
function loadImage() {
ImageToLoad.onload = function() {
console.log("finish loading");
};
ImageToLoad.src = "myimage.png";
}