So I have loaded in an image and can access the properties within this function locally such as ball.height and width, but I need to use these properties in other functions to do calculations with. How would I access these properties since they are only locally defined currently. I can't set it as global (or at least don't know how) since in order to get the image in the first place I have to load it in with the function.
function drawBall() {
var ball = new Image();
$(ball).on('load', function() {
ctx.drawImage(ball, xBall, yBall);
console.log(ball.height);
});
ball.src = 'ball.png';
}
function ballHit() {
// For example calculate if ball.height = canvas.height
}
Since the image load is a asynchronous task, you can use a callbackļ¼
function drawBall(callback) {
var ball = new Image();
$(ball).on('load', function() {
ctx.drawImage(ball, xBall, yBall);
console.log(ball.height);
callback(ball.height,ball.width);
});
ball.src = 'ball.png';
}
drawBall(ballHit);
Create a global variable globalBall and once the image is loaded you can assign the image from the function to the global object.
var globalBall = new Image();
function drawBall() {
var ball = new Image();
$(ball).on('load', function() {
ctx.drawImage(ball, xBall, yBall);
console.log(ball.height);
});
ball.src = 'ball.png';
globalBall = ball;
}
function ballHit() {
if(globalball.height == canvas.height)
{
// Do your stuff
}
}
Related
I have two canvas the first one is working good, but when I initialize the second one the paper.Tool does not work properly, sometimes the event onMouseMove works others not.
var dataLoad;
var mypapers = []
$(document).ready(function () {
dataLoad = new DataLoad();
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
mypapers[0].setup(document.getElementById('dataCanvas'));
dataLoad.Init();
});
// "returnedData" THIS ARRAY COMES FROM AN AJAX CALL
DataLoad.prototype = {
Init: function () {
var self = this;
var paperData = new
DataReader(document.getElementById('dataCanvas'));
paperData.Init(returnedData[i],mypapers[0]);
paperData.Draw(true);
self.datas.push(paperData);
}
});
Till here everything is good the first canvas is populated with the graphics I setted.
DataReader.prototype = {
Init: function (data,mypaper) {
var self = this;
paper = mypaper;
self.paper = paper;
self.toolPan = new self.paper.Tool()
self.toolPan.activate();
self.toolPan.onMouseDrag = function (event) {
var delta = event.downPoint.subtract(event.point)
self.paper.view.scrollBy(delta)
};
self.toolPan.onMouseMove = function (event) {
self.OnMouseMove(event);
};
self.toolPan.onMouseUp = function (event) {
// AFTER MAKE A SELECTION OF ITEMS IN THE CANVAS CALLING THE SECOND CANVAS
var core = self.HighlightElementsInBox();
self.dc = new DefineComponent();
self.dc.Init(core);
$('#DCCanvas').modal('toggle'); // THE CANVAS IS INSIDE THIS BOOTSTRAP MODAL
}
}
});
/* this initialize the second canvas that basically creates another instance of the same prototype i use to manipulate paperjs in the first canvas */
DefineComponent.prototype = {
Init: function (dataCore) {
var self = this;
mypapers[1].setup(document.getElementById('DCCanvas')); // setting second canvas
var paperDataDC = new DataReader(document.getElementById('DCCanvas'));
paperDataDC.Init(dataCore,mypapers[1]);
paperDataDC.Draw(true);
self.datas.push(paperDatasDC);
}
});
In this second canvas all is drawn correctly, but the events onmousedrag and onmousemove does not works properly the first one move the canvas in another position where the mouse is not and mousemove works only in some places of the canvas not in all.
As you are creating two different paperScopes you will need to toggle between them when working with one or the other.
You are saving both paperScopes inside "mypapers" array
mypapers[0] = new paper.PaperScope();
mypapers[1] = new paper.PaperScope();
So to use any of those you should do
mypapers[0].activate();
// or
mypapers[1].activate();
Check out this (min setup) example of what I mean above.
Also, follow stefan's suggestion if you want more help on this since it's hard for people to try to help without a minimal working example
I'm writing a small javascript class that I'm trying to use to create an image via canvas
I keep running into an issue where when I run the update function to redraw the canvas, some properties are undefined when they should be an instance of an image
To better explain what I mean, this code is what I have:
this.setHabboLeft = function(src)
{
var callback = this.update;
this.habboLeft = getImage(src, callback);
}
var getImage = function(src, callback)
{
var img = new Image;
if(typeof callback == 'function')
{
img.onload = callback;
}
img.src = src;
return img;
}
this.update = function()
{
console.log('updating...', this.background, this.habboLeft);
...
}
I am using setHabboLeft that sets an image from an external URL, then once that is loaded, it will run this.update()
I think the problem is with the onload callback in getImage. The console.log should return the image object but tells me it's undefined
EDIT for #gus27
Note, the image is on a different domain
I call the functions like:
// set background of lovelock
lovelockCanvas.setBackground($(self).data('picture'));
// create logged in user habbo
var habboUrl = habboCreator.generateUrl({
habbo_username: '{{ Auth::user()->habbo_username }}'
});
lovelockCanvas.setHabboLeft(habboUrl);
EDIT 2
Function being called
http://pastebin.com/ZRut9gyD
full js class
http://pastebin.com/WjRUkj4X
FIXED:
Instead of my update using this to draw everything, I created a new function and passed this as a parameter
See pastebin: http://pastebin.com/BTNigqLS
The problem is in the update function which is called by the img. When update is called by the img (by it's onload callback) the this variable in the update function refers to the img.
The documentation for this states:
Inside a function, the value of this depends on how the function is
called.
You can try something like that:
this.setHabboLeft = function(src)
{
var callback = this.update;
this.habboLeft = getImage(src, callback, this);
}
var getImage = function(src, callback, obj)
{
var img = new Image;
if(typeof callback == 'function')
{
img.onload = function(){
callback(obj);
};
}
img.src = src;
return img;
}
this.update = function(obj)
{
console.log(this); // this is here the img, not the lovelockCanvas
console.log('updating...', obj.background, obj.habboLeft);
}
Function.bind(objRef)
All functions are a type of object called Function MDN Function object. They come with some methods and properties, one of which is called bind.
Bind creates a new copy of the function binding its keyword (this) to the function.
Thus to solve the problem of an event overwriting the functions binding you simple do the binding yourself to ensure it is not overwritten by the native event handler.
this.setHabboLeft = function(src){
// create a new function bound to this
this.habboLeft = getImage(src, this.update.bind(this));
}
var getImage = function(src, callback){
var img = new Image;
img.onload = typeof callback == 'function' ? callback : undefined;
img.src = src;
return img;
}
this.update = function() {
console.log('updating...', this.background, this.habboLeft);
...
}
Additionally you don't have or add more intermediate steps to keep hold of the event object.
this.setHabboLeft = function(src){
// create a new update function bound to this
// only reference the image if it loads
getImage(src, this.update.bind(this));
}
var getImage = function(src, callback){
var img = new Image;
img.onload = typeof callback == 'function' ? callback : undefined;
img.src = src;
}
this.update = function(event){
// image loaded so can be referenced
this.habboLeft = event.target; // the image object
}
I was wondering if it was possible to get the width and height of an image without putting an image into page, i.e without creating an image tag that displays it.
trying to make a sprite class using this method.
function Sprite(src,frames) {
// Sprite class
this.img = new Image();
this.img.src = src;
this.frames = frames;
this.cframe = 1;
this.setDim = function() {
this.fullWidth = this.img.width;
this.fullHeight = this.img.height;
}
this.img.onload = this.setDim();
console.log(this.fullWidth);
return this;
}
however this.fullWidth returns undefined
and the below that ignores the onload returns 0
function Sprite(src,frames) {
// Sprite class
this.img = new Image();
this.img.src = src;
this.frames = frames;
this.cframe = 1;
this.fullWidth = this.img.width;
this.fullHeight;
this.setDim = function() {
this.fullWidth = this.img.naturalWidth;
this.fullHeight = this.img.height;
console.log(this.fullWidth)
}
console.log(this.fullWidth)
//this.img.onload = this.setDim();
return this;
}
I don't really want to use Jquery for this.
I have also tried this.img.natrualWidth (as you can see in the example above)
it also returns 0
Any advice would be great,
Thanks
Updated this to match #vihan1086 answer
function Sprite(src,frames) {
// Sprite class
this.img = new Image();
this.img.src = src;
this.frames = frames;
this.cframe = 1;
var self = this;
self.loaded = function () {};
this.setDim = function() {
self.fullWidth = this.width;
self.fullHeight = this.height;
self.frameWidth = this.width / self.frames;
self.frameHeight = this.height;
self.loaded.apply(self, []);
}
this.loaded = function() {
return this;
}
this.img.onload = this.setDim;
}
then use
sprite = new Sprite(sprite,5);
sprite.loaded = function() {
console.log(sprite.fullWidth);
}
Problem
var img = new Image();
img.src = '/my/src/to/file';
//this refers to the current function at this point
img.onload = function () {
//this is 'img' at this point not the function
}
Solution
this is not in scope so you would add:
var self = this;//Self and this are referring to the same thing, the function
img.onload = function () {
//this refers to image but self still refers to the function's this
self.width = this.width;
self.height = this.height;
}
console.log(this.width);//logs width
console.log(this.height);//logs height
This leaves async problems which can be solved using two methods
A
this.img.onload = this.setDim; //Noticed the dropped ()
B
self.loaded = function () {};
this.setDim = function () {
//...
self.loaded.apply(self, []);
}
then
var sprite = new Sprite(...);
sprite.loaded = function () {
console.log(this.fullHeight);
}
Explanation
img.onload() changes the scope of the code, resulting in your this referring to img. Now the weird part. We created a variable self which refers to this, this allows us to refer to this in a different scope by using self.
Other
img.onload is "async" which means it won't follow along with the rest of the code. This means the console.log() has run, but the img.onload hasn't. Be careful when working when this type of code (I wrote a few solutions in the update). You should wait until img.onload is finished before checking the values. I've worked on something like this before and I'll see if I can find what I did to address all these issues; if I can, I'll update this answer.
UPDATE: I wouldn't run the setDim function at first and let the user run it setDim() -> setDim. If you wish to load the dimensions at first, put a load function to your Sprite() which is run when the dimensions are retrieved.
In javascript the statements are executed asynchronously. To know more about this read this excellent article Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
In your case as #Juhana mentioned passing reference should get the issue resolved
In one of my projects, I successfully used the following code to render a png image in an html5 canvas using JavaScript.
var sizeIcon = new Image();
sizeIcon.draw = function() {
ctx.drawImage(sizeIcon, tooltip.x + 10, tooltip.y + 10);
};
sizeIcon.onload = sizeIcon.draw;
sizeIcon.src = "./images/icons/ruler.png";
tooltip.icons.push(sizeIcon);
Since I have multiple icons to load, I implemented the following function:
var imageLoader = function(x, y, src) {
var image = new Image();
image.draw = function() {
ctx.drawImage(image, x, y);
};
image.onload = image.draw;
image.src = src;
tooltip.icons.push(image);
};
imageLoader(tooltip.x + 10, tooltip.y + 10, "./images/icons/ruler.png");
With tooltip.icons being a globally accessible array.
This function does nothing (and does not produce any error nor warnings in the console). I also tried filling the tooltip.icons array directly using something like tooltip.icons[n] = new Image(); without success (where n = tooltip.icons.length). There is probably a part of the JavaScript scope that I don't understand.
You are basically risking invalidating your image object (as in not available) when you get to the callback handler as image loading is asynchronous and the function will (most likely) exit before the onload is called.
Try to do a little switch around such as this:
var imageLoader = function(x, y, src) {
var image = new Image();
function draw() {
// use *this* here instead of image
ctx.drawImage(this, x, y)
};
image.onload = draw;
image.src = src;
tooltip.icons.push(image);
};
Instead of the small hack here you could store the coordinates (and urls) in an array and iterate through that.
I'm making a simple engine for isometric games to use in the future.
The thing is i'm having trouble drawing on the canvas. Take for instance this code:
function pre_load() {
var imgs = ['1', '1000','1010','1011','1012','1013'];
var preload_image_object = new Image();
$.each(imgs,function(i,c){
preload_image_object.src='img/sprites/'+c.logo+'.png';
})
}
function GameWorld() {
//[...]
this.render = function() {
this.draw(1000, this.center);
this.draw(1013, this.center);
this.draw(1010, this.center);
}
this.draw = function(id, pixel_position) {
draw_position_x = pixel_position[0] - this.tile_center[0];
draw_position_y = pixel_position[1] - this.tile_center[1];
inst = this;
var img = new Image();
img.onload = function () {
inst.ctx.drawImage(img, draw_position_x, draw_position_y);
console.log('Drawn: ' + id);
}
img.src = "img/sprites/"+id+".png";
}
}
One might expect that things will be drawn in the order, id 1000, then id 1013, then id 1010 since the images are already loaded(even if not, currently i'm working on a local server).
But if I refresh the page several times, I'll get different results:
Case 1
Case 2
Case 3
(Any other permutation may happen)
The only solution I can think about is making the function "hang" until the onload event has been called and only then proceed to the next .draw() call. Would this be the right approach? How would I go about doing it? Is there a better solution?
Thanks!
Your intuition is correct...you need an image loader.
There are plenty of image loader patterns out there--here's one:
When all your images are fully loaded, the imgs[] array below has your sprites in proper order.
var imgURLs = ['1', '1000','1010','1011','1012','1013'];
var imgs=[];
var imgCount=0;
function pre_load(){
for(var i=0;i<imgURLs.length;i++){
var img=new Image();
imgs.push(img);
img.onload=function(){
if(++imgCount>=imgs.length){
// imgs[] now contains all your images in imgURLs[] order
render();
}
}
img.src= "img/sprites/"+imgURLs[i]+".png";
}
}