Jump to content

Detect PIXI images loaded in browser


NavCore
 Share

Recommended Posts

Hello,

I'm working on PixiJS application where I dressing up my character. So, different PNG images are loaded to Pixi stage.

There is a button to finish dressing character and it saves current canvas to gallery using .toDataURL() method.

The problem is that during image changing the user can click finish button to save canvas to gallery, but the image is not already loaded in browser, so it result in missing image in canvas data.

The example of adding hair image to stage:

function dressUpCharacter(callback) {

  hair_texture = PIXI.Texture.fromImage(hair_img);
  hair = new PIXI.Sprite(hair_texture);
  stage.addChild(hair);
  
  if (callback && typeof(callback) === "function") {
    callback(); // callback -> unlock finish button
  }

}

As You can see, I already made callback to unlock finish button when my dressing function finishes.

The thing is that javascript code completes successfully and adds canvas image to stage, but the image needs few seconds/miliseconds (depending on internet speed) to load in browser.

During that time if user clicks finish button it can result with incomplete canvas.

Is there any way I can detect canvas images are fully loaded?

I saw that PIXI has loader with loader.once('complete', callback) method, but I don't know how to use it.

Many thanks in advanced!

Josip

Link to comment
Share on other sites

4 minutes ago, NavCore said:

loader.once('complete', callback)

Yep I think this is the best way to do it, if possible load all your images up front (maybe a sprite atlas/spritesheet would help) so that you know your game is ready to go and that saving will always succeed. This way the user doesn't need to wait for images either when they're playing.

If you have to stay with loading as required then you'd have to use a callback on the load function of the image (or the PIXI loader) and disable the button until your new assets have been delivered to the browser.

Try checking out the PIXI examples (or even digging through the code, its well structured, and there are good API docs too).

Link to comment
Share on other sites

Thanks for the answer.

I would try anything that will solve my problem.

So, with loader:

hair_texture = PIXI.Texture.fromImage(hair_img);
hair = new PIXI.Sprite(hair_texture);
loader.add('hair',hair_texture);
loader.once('complete',unlockFinishButton);
loader.load();
stage.addChild(hair);
function unlockFinishButton() {
    characterDressed = true; // flag to unlock finish button
}

This code from above isn't working, I know.
Do I need to put 'adding child to stage' in my loader.once callback?

Can You help me reorganize this code or write a new one using callback on the load funcion !?

Thanks.

Link to comment
Share on other sites

Yeah, you're close but you made an easy mistake (you'll only make it once, the solution will stick with you from now on! hopefully!) of calling an asynchronous function and then trying to do stuff before it has completed, try rearranging into two distinct phases, load stuff, then do stuff:

var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb})
document.body.appendChild(app.view)

PIXI.loader
  .add('hair', 'assets/hair_img.png')
  .load(onAssetsLoaded)

function onAssetsLoaded () {
  var tex = PIXI.loader.resources['hair'].texture
  var spr = new PIXI.Sprite(tex)
  app.stage.addChild(spr)
}

We only create the texture and sprite once we know the image has loaded, then we add the sprite to the stage and we should be good to go.

Link to comment
Share on other sites

On 2/4/2017 at 4:00 PM, mattstyles said:

Yeah, you're close but you made an easy mistake (you'll only make it once, the solution will stick with you from now on! hopefully!) of calling an asynchronous function and then trying to do stuff before it has completed, try rearranging into two distinct phases, load stuff, then do stuff:


var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb})
document.body.appendChild(app.view)

PIXI.loader
  .add('hair', 'assets/hair_img.png')
  .load(onAssetsLoaded)

function onAssetsLoaded () {
  var tex = PIXI.loader.resources['hair'].texture
  var spr = new PIXI.Sprite(tex)
  app.stage.addChild(spr)
}

We only create the texture and sprite once we know the image has loaded, then we add the sprite to the stage and we should be good to go.

Great!

I'll try with loading my sprites inside callback, by looping all added loader resources and adding them to stage.

Thanks for the tips ;)

Link to comment
Share on other sites

On 2/4/2017 at 4:00 PM, mattstyles said:

Yeah, you're close but you made an easy mistake (you'll only make it once, the solution will stick with you from now on! hopefully!) of calling an asynchronous function and then trying to do stuff before it has completed, try rearranging into two distinct phases, load stuff, then do stuff:


var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb})
document.body.appendChild(app.view)

PIXI.loader
  .add('hair', 'assets/hair_img.png')
  .load(onAssetsLoaded)

function onAssetsLoaded () {
  var tex = PIXI.loader.resources['hair'].texture
  var spr = new PIXI.Sprite(tex)
  app.stage.addChild(spr)
}

We only create the texture and sprite once we know the image has loaded, then we add the sprite to the stage and we should be good to go.

I done just like You wrote, but my texture is not appearing.

In my callback onAssetsLoaded() I can log my resource successfully using:

console.log(PIXI.loader.resources["hair"]);

But if I log .texture:

console.log(PIXI.loader.resources["hair"].texture);

I got "undefined".

Also, using that texture to create sprite and adding it to stage results in missing character hair on stage and no console error found.

Thanks in advanced!

Josip

Link to comment
Share on other sites

8 minutes ago, NavCore said:

I done just like You wrote, but my texture is not appearing.

In my callback onAssetsLoaded() I can log my resource successfully using:


console.log(PIXI.loader.resources["hair"]);

But if I log .texture:


console.log(PIXI.loader.resources["hair"].texture);

I got "undefined".

Also, using that texture to create sprite and adding it to stage results in missing character hair on stage and no console error found.

Thanks in advanced!

Josip

This one works:

var hair_texture = PIXI.Texture.fromImage(PIXI.loader.resources["hair"].url);

What to do now? To use this method or?

Thanks.

 

Link to comment
Share on other sites

Which version of Pixi are you using? Copying the following (same as above, just replaced the .png path) into the pixi examples displays the sprite.

var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb})
document.body.appendChild(app.view)

PIXI.loader
  .add('hair', 'required/assets/basics/bunny.png')
  .load(onAssetsLoaded)

function onAssetsLoaded () {
  var tex = PIXI.loader.resources['hair'].texture
  var spr = new PIXI.Sprite(tex)
  app.stage.addChild(spr)
}

 

Link to comment
Share on other sites

5 hours ago, mattstyles said:

Which version of Pixi are you using? Copying the following (same as above, just replaced the .png path) into the pixi examples displays the sprite.


var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb})
document.body.appendChild(app.view)

PIXI.loader
  .add('hair', 'required/assets/basics/bunny.png')
  .load(onAssetsLoaded)

function onAssetsLoaded () {
  var tex = PIXI.loader.resources['hair'].texture
  var spr = new PIXI.Sprite(tex)
  app.stage.addChild(spr)
}

 

I'm using newest version v4.3.5.

Here's my code:

var loader = new PIXI.loaders.Loader();
loader.reset();

loader.add("pants", "path_to_image/image.png");

loader.once('complete', changeLookOnComplete);
loader.load();

function changeLookOnComplete() {

	var pants_texture = loader.resources["pants"].texture; // NOT WORKING
	pants = new PIXI.Sprite(pants_texture);
	stage.addChild(pants);
	
}

The result is that sprite is not visible on the stage and there are no console errors.

Here is an examle of code that works for me by creating texture from image:

var loader = new PIXI.loaders.Loader();
loader.reset();

loader.add("pants", "path_to_image/image.png");

loader.once('complete', changeLookOnComplete);
loader.load();

function changeLookOnComplete() {

	var pants_texture = PIXI.Texture.fromImage(loader.resources["pants"].url); // WORKING
	pants = new PIXI.Sprite(pants_texture);
	stage.addChild(pants);
	
}

What am I doing wrong so that it won't assign texture directly from resource?

Thanks in advanced!

Link to comment
Share on other sites

19 hours ago, bubamara said:

there has been change in resource loader recently, which isn't using event emitter anymore, but signals

you need to change your code a little

 


loader.onComplete.once(changeLookOnComplete);

 

One more question regarding this onComplete callback from above..

How to forward a parameter to "changeLookOnComplete" callback?

This one doesn't work:

loader.onComplete.once(changeLookOnComplete(param));

Says: First arg must be a Function.

And previous PIXI v3 callback with params worked:

PIXI.loader.once('complete', changeLookOnComplete(param));
Link to comment
Share on other sites

I think Phaser has a more idiomatic way of doing this but a generic solution is to wrap the call to the function you want (this is usually called a thunk, although they have a better application which I'll explain later) e.g.

var PARAM = 'something'
loader.onComplete.once(() => changeLookOnComplete(PARAM))

A more verbose way to show this, if you're not comfortable with thunks, would be:

var PARAM = 'something'
var onCompleteCallback = function () {
  changeLookOnComplete(PARAM)
}

loader.onComplete.once(onCompleteCallback)

It should be a little clearer to see what is happening, the 'onCompleteCallback' function gets invoked by the loader onComplete hook, which in turn calls the `changeLookOnComplete` function with whatever arguments you want.

I'm very surprised that worked in PIXI v3, the reason is that when you 'add' the complete handler to the event emitter you are actually invoking the function and sending whatever it returns as the handler (which, in my onCompleteCallback function is nothing, which would probably result in an error, unless Pixi is checking for it). This mechanism can be used to your advantage to properly use a thunk to pass data through to other functions i.e.

var onCompleteCallback = function (param) {
  return function () {
    changeLookOnComplete(param)
  }
}

loader.onComplete.once(onCompleteCallback('something'))

This is functionally equivalent to the code snippets above but has one key distinction, the parameter you pass in becomes local to the function rather than relying on state from elsewhere. This can be a very powerful mechanism and can help avoid a whole slew of bugs. It's also what allows JS to use partial application (see currying, or even schoenfinkelization).

Lambdas/fat arrow functions make this terser and more readable:

var onCompleteCallback = param => () => changeLookOnComplete(param)

loader.onComplete.once(onCompleteCallback('something'))

You could inline the function but the JS engine will likely do this for you anyway and long lines aren't readable, you could split it of course, but, extracting the thunk means it could be generalised and used in other places. Typically the returned function would also contain parameters, which in this case would come from the event emitter (Phaser calls these signals now I believe).

I'd say this is a pretty advanced concept in most languages, certainly in JS, but its a very handy functional construct that can make your code tidier, more self-contained and more reusable.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...