Мanipulating virtual elements javascript - javascript

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)

Related

JQuery - Calling function after image load

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?

draw image from canvas constructor prototype

I'm trying to preload a set of images and draw first one from the list on to multiple canvases using canvas constructor.
It seems I can't even start loading images using prototype function. Console log says this.setImages is not a function. I'm just starting with js therefore it might be a very simple mistake.
I left some comments in the code i hope it will make it a bit clear.
There're other ways to achieve what I'm asking but in my case i have to use constructor function.
var sources = [
'http://i.telegraph.co.uk/multimedia/archive/02830/cat_2830677b.jpg',
'http://r.ddmcdn.com/w_622/u_0/gif/05-kitten-cuteness-1.jpg',
'http://simplycatbreeds.org/images/Kitten.jpg'];
var images = [];
var canvas1 = new canvas('canvas1');
var canvas2 = new canvas('canvas2');
// load images using callback function
setImages(sources, function(images){
window.alert("Done loading... Start!");
this.draw();
});
// canvases constructor
function canvas(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = this.canvas.getContext('2d');
this.canvas.style.width = '100%';
this.canvas.style.height = '100%';
this.canvasId = canvasId;
this.setImages();
};
// preloading images
canvas.prototype.setImages = function(sources, callback){
var cnt = 0;
function imagesLoaded(){
cnt++;
if (cnt >= sources.length){
// passing the objet containing images to load as parameteres
callback(images);
}
}
for (var i=0; i<sources.length; i++) {
images[i] = new Image();
images[i].src = sources[i];
images[i].onload = function(){
imagesLoaded();
}
}
};
canvas.prototype.draw = function() {
// some code to manipulate image to be added here...
this.context.drawImage(images[1], 0, 0);
};
here is FIDDLE example
There are some things I have fixed.
First, I have moved the code that creates the two objects to the end of the JS. The reason is that the prototype functions must be loaded before you try to access them. I recommend using a event listener such as window.onload to prevent the code from starting before everything is loaded.
Second, I changed the setImage so it is called on the canvas objects.
var sources = [...];
var images = [];
// load images using callback function
// canvases constructor
function canvas(canvasId) {
this.canvas = document.getElementById(canvasId);
this.context = this.canvas.getContext('2d');
this.canvas.style.width = '100%';
this.canvas.style.height = '100%';
this.canvasId = canvasId;
//I removed this since it does not have the right params
//this.setImages();
};
// preloading images
canvas.prototype.setImages = function(sources, callback){
var cnt = 0;
var obj = this;
function imagesLoaded(){
cnt++;
if (cnt >= sources.length){
// passing the objet containing images to load as parameteres
callback(images, obj);
}
}
for (var i=0; i<sources.length; i++) {
images[i] = new Image();
images[i].src = sources[i];
images[i].onload = function(){
imagesLoaded();
}
}
};
canvas.prototype.draw = function() {
// some code to manipulate image to be added here...
this.context.drawImage(images[1], 0, 0);
};
var canvas1 = new canvas('canvas1');
canvas1.setImages(sources, function(images){
window.alert("Done loading... Start!");
this.draw();
});
var canvas2 = new canvas('canvas2');
canvas2.setImages(sources, function(images, obj){
window.alert("Done loading... Start!");
obj.draw();
});
Demo
Try moving setImages function inside canvas function like this:
function canvas(canvasId)
{
this.setImages = function(...){ };
...
this.setImages();
}
In line 13 you should call setImages on a canvas object: canvas1.setImages();

Fire event when img src changed

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');
});
})

image.onload in function - why doesn't it work?

I'm new using JS. I want to keep all of the information about an object in one place.
So I've made this function:
function Obiekt(img) {
this.img = img;
this.ready = false;
this.image = new Image();
this.image.src = this.img;
this.image.onload = function() {
ready = true;
};
this.x = 0;
this.y = 0;
this.rotation = 0;
}
Then I've made my object, named hero.
var hero = new Obiekt("images/hero.png");
Problem is, hero.ready is always false and I can't draw this.
if (hero.ready) {
ctx.drawImage(hero.image, 0, 0);
}
Can you help me?
Two problems :
ready can't be found
If your image is cached (depending on the browser), the order you used can't work as the image isn't loaded after you set the callback.
Here's a solution :
var _this = this;
this.image.onload = function() {
_this.ready = true;
};
this.image.src = this.img;
(and I'd add that the naming isn't very good : I'd prefer imgsrc over img)

Delay function until an asset has loaded?

I am playing with the canvas, and it seems to be working great in FF6, but in Chrome 13, the sprite that I am drawing does not appear reliably. I have done some research and found that the problem stems from the function firing before the asset has loaded completely.
Fiddle here:
http://jsfiddle.net/LqHY9/
Relevant Javascript:
function sprite(ipath, sh, sw, ih, iw){
/* BASIC INFO FOR SPRITE */
this.frameWidth = sw;
this.frameHeight= sh;
frame_rows = ih/sh;
frame_columns = iw/sw;
num_frames = frame_columns*frame_rows ;
this.frame = new Array();
frameNumber = 0;
for(row = 0; row<frame_rows; row++){
for(i=0;i<frame_columns;i++){
this.frame[frameNumber] = {};
this.frame[frameNumber].offsetX = this.frameWidth*i;
this.frame[frameNumber].offsetY = this.frameHeight*row;
frameNumber++
}
}
this.sheight = sh;
this.swidth = sw;
this.raw = new Image();
this.raw.src = ipath;
}
animation=new sprite("http://www.melonjs.org/tutorial/tutorial_final/data/sprite/gripe_run_right.png",64,64,64,512);
context.drawImage(animation.raw, animation.frame[0].offsetX, animation.frame[0].offsetY, animation.frameWidth, animation.frameHeight, 0, 0, animation.frameWidth,animation.frameHeight)
(Don't worry, my context variable is defined, I just cut that bit out, you can see the whole thing in the JSFiddle.)
The Image object has an onload event which you should hook into.
Assuming you have more than one image, you could implement a sort of a "loader". This would basically just take an array of image URLs, load each of them, and listen to their onload events. Once each image has loaded, it would in turn call some other function, which would signal that every resource has finished loading.
the Image() object has an onload(and onerror) event. If you need to execute something after it loads you can attach a function.
e.g.
var img = new Image();
img.onload = function() {
//do something
};
Just make sure you attach the onload before setting the src.
You need to use the onload handler for the image. You must set the handler before you set the .src for the object because in some browsers, the load event may fire immediately upon setting .src if the image is in the browser cache. Here's a piece of pseudo code:
var img = new Image();
img.onload = function () {
// image is now loaded and ready for handling
// you can safely start your sprite animation
}
img.src = "xxx";
You can see related sample code from another answer I wrote here: jQuery: How to check when all images in an array are loaded?.
function Sprite(urls, speed, box)
{
var that = this, running = false, interval = 0, loaded = false;
that.urls = urls;
that.speed = speed;
that.images = new Array();
that.box = box || { x: 0.0, y: 0.0, w: 64, h: 64 };
for(var i = 0; i < that.urls.length; ++i)
{
that.images[i] = new Image();
that.images[i].src = that.urls[i];
that.images[i].id = i;
var len = that.urls.length;
that.images[i].onload = function(){ if(parseInt(this.id) === len) { loaded = true; } };
}
that.current = 0;
var Draw = function(ctx)
{
if(loaded)
{
var curr = that.images[that.current];
ctx.drawImage(curr, 0.0, 0.0, curr.width, curr.height, that.box.x, that.box.y, that.box.w, that.box.h);
}
};
that.Run = function(ctx)
{
if(!running)
{
running = true;
interval = setInterval(function(){
Draw(ctx);
if(that.current < that.urls.length)
{
that.current++;
}
else
{
that.current = 0;
}
}, that.speed);
}
};
that.Clear = function()
{
if(running)
{
running = false;
clearInterval(interval);
}
};
}
// Exemple
var test = new Sprite(["image1.png", "image2.png", "image3.png"], 250);
test.Run(myContext);

Categories