I'm trying to get my head around why this doesn't work and wondered if someone could help me out. Basically I need multiple instances of this object with different images and I need each object to store the image height/width for its associated image for further operations, however the onload event never fires?
Sorry guys to be clear heres the complete code, if you see the TestTheVar function imgW is never set to anything.
<script type="text/javascript">
(function() {
var myTest = new TestObj();
mytest.TestTheVar();
})();
function TestObj() {
this.img = new Image();
this.img.onload = function(){
this.imgW = this.img.width;
this.imgH = this.img.height;
};
this.img.src = "reel_normal.PNG";
this.TestTheVar = function() {
alert(this.imgW);
}
}
</script>
You have two problems here
1) scope
2) timing
Scope, as mentioned in other answers refers to the fact that this inside the onload function is the Image object and not your TestObj so you would need to do the following:
<script type="text/javascript">
(function() {
var myTest = new TestObj();
mytest.TestTheVar();
})();
function TestObj() {
var self = this;
this.img = new Image();
this.img.onload = function(){
self.imgW = this.width;
self.imgH = this.height;
};
this.img.src = "reel_normal.PNG";
this.TestTheVar = function() {
alert(this.imgW);
}
}
</script>
Timing refers to the fact that you cannot assume the image has finished loading by the time you are trying to access height and width. This is what callbacks are good for:
<script type="text/javascript">
(function() {
var myTest = new TestObj(function() {
myTest.TestTheVar();
});
})();
function TestObj(cb) {
cb = cb || function() {};
var self = this;
this.img = new Image();
this.img.onload = function(){
self.imgW = this.width;
self.imgH = this.height;
cb();
};
this.img.src = "reel_normal.PNG";
this.TestTheVar = function() {
alert(this.imgW);
}
}
</script>
this is a keyword that belongs to each function.
In the load event listener it will be the image, not your TestObj instance.
Therefore, you can
Use this.img.imgW to get it:
function TestObj() {
var that = this;
this.img = new Image();
this.img.onload = function(){
this.imgW = this.width;
this.imgH = this.height;
that.testTheVar();
};
this.img.src = "reel_normal.PNG";
this.testTheVar = function() {
alert(this.img.imgW);
};
}
Store it in your TestObj instance:
function TestObj() {
var that = this;
this.img = new Image();
this.img.onload = function(){
that.imgW = this.width;
that.imgH = this.height;
that.testTheVar();
};
this.img.src = "reel_normal.PNG";
this.testTheVar = function() {
alert(this.imgW);
};
}
Customize this inside the event handler to be your TestObj instance:
function TestObj() {
this.img = new Image();
this.img.onload = (function(){
this.imgW = this.img.width;
this.imgH = this.img.height;
this.testTheVar();
}).bind(this);
this.img.src = "reel_normal.PNG";
this.testTheVar = function() {
alert(this.imgW);
};
}
Related
I have a vue app. I use this.data to access it's data variables and methods.
In a certain method I have to use img.onload to get the width of an image. But there are 2 "this" now and vue js methods and variables are not working now. I need an alternative solution so both will work.
vueMethod(url) {
var img = new Image();
img.onload = function() {
this.size = {this.width,this.height} //size is a vue variable
}
img.src = url;
}
You could assign this to a variable called vm before calling img.onload function as follows
vueMethod(url) {
var img = new Image();
let vm = this;
img.onload = function() {
vm.size = { this.width, this.height } //size is a vue variable
}
img.src = url;
}
You can use an arrow function (documentation) to keep the englobing scope:
vueMethod(url) {
var img = new Image();
img.onload = () => {
this.size = {img.width, img.height}
}
img.src = url;
}
In this example, this will have the same reference as if you used it next to var img
You should save Vue instance into a variable outside the img.onload scope
try with:
vueMethod(url) {
var img = new Image();
var vm = this;
img.onload = function() {
vm.size = {this.width,this.height} //size is a vue variable
}
img.src = url;
}
Put var that = this; inside your img funciton.
That will give you scope in your function, or bind your function when you call it e.g
vueMethod("url").bind(this)
So when you use a function with the keyword function it creates a new scope for this to refer to consider the below example
const obj = {
size: 10,
testFunc() {
console.log(this, 'now obj')
const func = function () {
// this now refers to function its currently in
}
const otherFunc = () => console.log(this, 'refers to obj still')
},
testFunc2: () => {
console.log(this, 'now window')
}
}
any questions let me know and i'll be happy to help
const obj = {
size: 10,
vueMethod(url) {
console.log(this.size, 'this now refers to obj')
const img = new Image();
const self = this;
img.onload = function() {
console.log(this, 'this now refers to img')
console.log(self.size, 'but you can reassign this to a var outside the scope');
self.size = {
width: this.width,
height: this.height
}
};
// mocking the onload dont include this
img.onload();
img.src = url;
}
}
obj.vueMethod()
Following is a simple class definition. this.bounds is undefined in the console so i am wondering how can i access and alter bounds inside a callback. The goal is after the image is loaded, the bounds will be set to bounds.width = image.width etc. Thanks!
function Car() {
this.image = new Image();
this.bounds = new Rectangle(0, 0, 0, 0);
this.setImage = function(ii){
this.image.src = ii;
this.image.onload = function () {
console.log("image was loaded " + this.bounds);
}
};
}
Can you try doing :
this.image.onload = function () { console.log("image was loaded " + this.bounds); }.bind(this)
"bind" is used to bind the current scope of this to any function.
Whenever you call the function in future, the bound reference of this will be used.
Please refer this link : what is bind in javascript
Inside onload callback this no longer points to Car instance but to image because it's called by image object when image is loaded. To access Car methods inside this callback you can:
use bind method which returns new function inside which this is set the to value passed as argument to bind
this.image.onload = function () {
console.log("image was loaded " + this.bounds);
}.bind(this)
You can read more about bind here
in ES2015 you can use arrow function which also preserves this value:
function Car() {
this.image = new Image();
this.bounds = new Rectangle(0, 0, 0, 0);
this.setImage = function(ii){
this.image.src = ii;
this.image.onload = () => {
console.log("image was loaded " + this.bounds);
}
};
}
You can read more about arrow functions here
you can also define a variable that will hold a value of this
function Car() {
this.image = new Image();
this.bounds = new Rectangle(0, 0, 0, 0);
var self = this;
this.setImage = function(ii){
this.image.src = ii;
this.image.onload = function () {
console.log("image was loaded " + self.bounds);
}
};
}
"this" inside function image.onload() is actually scoped under function setImage(), which definitely don't have this.bounds defined, hence you got it undefined.
One solution could be to assign this to any variable just before the line: this.image.onload = function () {
so it will change to:
var that = this;
this.image.onload = function () {
console.log("image was loaded " + that.bounds);
}
I think you should see this:
var doc, bod, htm, Img, C, E; // for reuse on other loads
addEventListener('load', function(){
doc = document; bod = doc.body; htm = doc.documentElement;
C = function(tag){
return doc.createElement(tag);
}
E = function(id){
return doc.getElementById(id);
}
Img = function(src, complete, context){
this.image = C('img');
var img = this.image;
var cx = context || this;
img.addEventListener('load', function(eventObj){
complete.call(cx, eventObj);
});
img.src = src;
this.imgMethod = fuction(){
this.newProp = 'this in here refers to instance of Img';
}
}
Car = function(){
this.setImg = function(src, complete){
return new Img(src, complete);
}
}
var what = new Car; // no args it's okay to not use () with new
var someImg = what.setImg('yourImgSrc.png', function(){
this.imgMethod(); console.log(this.newProp);
});
someImg.image.addEventListener('click', function(ev){
var bc = this.getBoundingClientRect();
var xy = {x:ev.clientX-bc.left, y:ev.clientY-bc.top};
console.log(xy);
});
});
Note: this is only an example of this binding using call
I'm having a problem while loading pictures in my javascript. Seems that the .onload function is never called and so the picture is not marked as loaded. Can you help?
Here is the code:
//image loader
var img = function (source){
this.loaded = false;
this.image = new Image();
this.image.onload = function () {
this.loaded = true;
};
this.image.src = source;
}
//default component definition
var component = function (x, y){
this.x = x;
this.y = y;
this.draw = function (offsetX, offsetY, img){
if(img.loaded){
ctx.drawImage(img.image,
this.x + offsetX,
this.y + offsetY,
img.image.width, img.image.height);
}
}
}
Thanks
Context #1
this.image.onload = function () {
this.loaded = true;
};
this referes to [object HTMLImageElement], not to the img instance.
Context #2
Now, changing the code to:
this.image.onload = function () {
this.loaded = true;
}.bind(this);
When you do new img("http://serban.ghita.org/imgs/serban.jpg");, this will refer to your [object Object].
Context #3
Now, If you do img("http://serban.ghita.org/imgs/serban.jpg");, without new, this will refer to [object Window].
Solution
It depends on the context of this.
You change it with bind
Or by declaring and external var _this = this; and then use _this.loaded = true; inside onload.
Code: https://jsbin.com/pasumab/edit?js,console
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;
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)