I have an image that changes when the user clicks a different thumbnail.
How do I make it reliably get the dimensions of the image that is about to be loaded?
Currently I have:
$('#mainImg').animate({opacity: 0.01}, 500, function() {
$('#mainImg').attr('src', '/cakes/' + file);
setTimeout(function() {
center('#mainImg');
$('#mainImg').animate({opacity: 1.00}, 500);
}, 100)
})
Which is a work around that is a little more reliable than what I previously had:
$('#mainImg').fadeOut('fast', function(){
$('#mainImg').attr('src', '/cakes/'+file);
center('#mainImg');
$('#mainImg').fadeIn('fast');
})
Function center is for now just:
function center(imageElement)
{
var ph = $(imageElement).parent().height();
var pw = $(imageElement).parent().width();
var ih = $(imageElement).height();
var iw = $(imageElement).width();
alert(iw)
}
You should preload image first to have reliable dimensions. Try something like this:
$('#mainImg').animate({opacity: 0.01}, 500, function() {
var $this = $(this),
src = '/cakes/' + file;
preloadImage(src, function() {
$this.attr('src', src);
center($this);
$this.animate({opacity: 1.00}, 500);
});
});
preloadImage(src, callback) {
var img = new Image();
img.onload = callback;
img.src = src;
}
Also modify center function to use already available cached variable:
function center($imageElement) {
var ih = $imageElement[0].height;
var iw = $imageElement[0].width;
alert(iw)
}
I think you'll have to preload that image, something like this:
var image = new Image();
image.src = "https://www.google.nl/images/srpr/logo11w.png";
image.onload = function () {
// Do whatever you want with this.width and this.height overhere.
console.log(this.width);
console.log(this.height);
}
Simular to the already posted answers but with a fallback for the browsers that do not support the load event for images properly:
$('#mainImg').animate({opacity: 0.01}, 500, function() {
// run the load event only once
$('#mainImg').one('load', function() {
// do whatever you want to do if image is loaded...
console.log(this.width);
console.log(this.height);
})
.attr('src', '/cakes/' + file);
.each(function() {
//Cache fix for browsers that don't trigger .load()
if(this.complete) $(this).trigger('load');
});
})
Related
I have the funtction that adds an image in canvas after loading
function handleImage(e) {
var reader = new FileReader();
var _this = this;
var loadImage = _this.parentElement;
var canvas = loadImage.querySelector('canvas');
var ctx = canvas.getContext("2d");
reader.onload = function(event) {
var img = new Image();
img.onload = function() {
let width_pic = 200;
let k = img.width/width_pic;
canvas.height = img.height / k;
ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, width_pic,
canvas.height);
};
img.src = event.target.result
};
reader.readAsDataURL(e.target.files[0]);
}
However I will use this function for virtual element (element that appeared by
$('button').click(function(){
$('#new-pic').html(`<p>virt</p><input class="load_ph_step" type="file"/> <canvas></canvas>`);
});
), for this element the function does not work.
I now in this sutuation in jquery is used $(document).on('click', 'selector', function(e){});, but now I use javascript-function.
https://jsfiddle.net/Nata_Hamster/8v1owgmL/
You can implement in vanilla JS something similar
DOM = {};
DOM.addEventListener = function(onObject, type, subselector, listener, options = {}) {
onObject.addEventListener(type, function(e) {
if (subselector) {
for (let target = e.target; target && target !== this; target = target.parentNode) {
if (target.matches(subselector)) {
listener.call(target, e);
break;
}
}
} else {
listener.call(e.target, e);
}
}, options);
};
DOM.addEventListener(document, 'change', '.load_ph_step', handleImage);
Your code didn't work because you add an event listener to the only
one element which exists at the moment of the running of your JS script.
You need to find the new elements as well whenever you choose an image.
This code places an event listener on the document (you may refine this)
and calls the listener function when the sub-selector matches.
(but it seems you already know this from jQ)
Edit:
Ok, it seems I misunderstand the question?
You probably want to try what #charlietfl commented above:
$(document).on('change', '.load_ph_step', handleImage)
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;
So I have two functions. One to load the image and the other to resize its container element. Naturally the image element needs to be loaded, before any measurements can be taken. It looks something like this:
var imgEl;
loadImage(imgSrc);
// only call the below, once the above has finished loading and appending the image.
resizeModal();
function loadImage(imgSrc) {
var image = new Image();
image.src = imgSrc;
image.onload = function() {
imgEl = $('<img src='+image.src+'/>');
imgEl.prependTo(modal);
}
}
function resizeModal() {
// Do stuff based off the imgEl once it's been loaded and appended
var width = imgEl.width();
modal.width(width)
}
I've tried using $.Deferred, but I'm apparently missing something, as "B" always gets logged before "A":
var imgEl;
loadImage(imgSrc).done( resizeModal() )
function loadImage(imgSrc) {
var def = $.Deferred();
var image = new Image();
image.src = imgSrc;
image.onload = function() {
imgEl = $('<img src='+image.src+'/>');
imgEl.prependTo(modal);
console.log("A");
def.resolve();
}
return def;
}
function resizeModal() {
console.log("B");
// Do stuff based off the imgEl once it's been loaded and appended
var width = imgEl.width();
modal.width(width)
}
That's because you are explicitly calling resizeModal before the promise is resolved:
loadImage(imgSrc).done( resizeModal() )
Just like with foo(bar()), this will call resizeModal and pass its return value to done().
You want to pass the function itself instead:
loadImage(imgSrc).done(resizeModal)
This basically means "call resizeModal once you are done".
var loadImage = function(imgSrc, callback) {
var imgEl;
var image = new Image();
image.src = imgSrc;
image.onload = function() {
imgEl = $('<img src='+image.src+'/>');
imgEl.prependTo(modal);
}
callback(imgEl);
}
var resizeModal = function(imgEl) {
// Do stuff based off the imgEl once it's been loaded and appended
var width = imgEl.width();
modal.width(width)
return width; // or whatever you are trying to get from this.
}
var finalOutput = loadImage(imgSrc, resizeModal);
Have you tried a structure like this?
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.