Search code examples
flashanimationmovieclipcreatejseaseljs

How to instantiate large MovieClip without slowing framerate in EaselJS / CreateJS


A little context: I've created a detailed MovieClip with timeline animation in Flash Pro / Adobe Animate and exported to a CreateJS / EaselJS library. When I instantiate it at runtime it takes longer than a frame or 'tick' to complete this instantiation, and the animation playback waits before advancing to the next frame. This is causing a hiccup in a playing animation. This is a problem that's coming up a lot in my project. How can I overcome this hiccup without simplifying the frame art in the MovieClip?

Code: In case it's not clear, this is what I'm talking about...

var instanceMC = new lib.bigMovieClip_mc(); // <-- LONG DELAY, OVER 1 TICK IN TIME

stage.addChild(instanceMC); // <-- from here on it seems to run smoothly
instanceMC.x = xPosition;
instanceMC.y = yPosition;
stage.update();

My ideas:

  1. If asynchronous instantiation is an option, that would work nicely for my situation, but I couldn't figure out if and how this can be done. I see it can be done for SpriteSheetBuilder, which is a similar but different situation (I can't use SpriteSheetBuilder because I have nested MovieClips that are independently controlled).
  2. I can break up the MovieClip into smaller MovieClips and instantiate them independently, then assemble them together. This is somewhat annoying, but doable. If I did that, I would want to listen for an event for the completion of each instantiation. Does such an event exist? I couldn't find one in the docs.
  3. LoadJS. I'm pretty unfamiliar with LoadJS. I checked it out a bit, and it looks like it's for managing downloading, not other initialization tasks. However, if it could add a series of large instantiations to the load queue (or if there is something similar), and have it not hold up timeline playback during instantiation, that would work well.
  4. Multiple canvases and stages? If I add a second canvas with its own stage, I suppose that each stage would have independent frame tickers and therefore, by instantiating my MovieClip in one and playing my animation in the other, I could decouple the instantiation and playback. In my particular case, each part of the application is pretty independent, so switching canvas mid usage is somewhat doable. I'd rather not deal with juggling an application split in two, but it also seems like a straightforward way to address the issue without digging deep into functionality. Is this even technically possible, or did I make a bad assumption?

HELP: What approach can you suggest (either listed or not) that would solve my issue?


Solution

  • Firstly, I hope that someone adds a better answer than this, and I will happily change the selected answer.

    I've looked into the various options to some degree, and learned that this is actually a very deeply rooted issue. It has to do with the single threaded nature of the browser and that UI updates use the same thread and have to wait their turn. See: here, here, here, and here.

    Designing libraries, like CreateJS, from the ground up to clear the call stack as much as possible and utilize the callback queue can avoid locking the UI during their own operations (like instantiation of a MovieClip). (Effectively this pauses the code of complex operations every frame.) This is the best approach to dealing with this beast. As far as I could find, instantiation of MovieClips has no such support in CreateJS, although building spritesheets in runtime has an 'async' option, and of course, the LoadJS runs asynchronously as well (but doesn't support execution of code as far as I could tell). Presumably, my very large MovieClips would be considered outside the bounds of the creators' intended usage.

    My solution was to preload all movieclips and display a preloader before running my app. This is not an ideal solution, as it has a long wait time, but it works better than the alternatives for my needs (namely less labor). A better approach would probably be to break up my problematic MovieClips into smaller MovieClips that won't cause issues independently, then to instantiate them like so:

    setTimeout( function(){ firstMovieClip = new lib.firstMovieClip(); },0);
    setTimeout( function(){ secondMovieClip = new lib.secondMovieClip(); },0);
     and so on....
    

    This causes them to not enter the stack directly, but to enter the callback queue. Which in turn allows the UI to update in between these smaller instantiations, since the stack clears.

    For what it's worth, there are also options to run additional threads in the browser with web workers. It wouldn't work for my purposes, because objects in these web workers can't be moved to the UI. It would be a good solution for a complex computation with a simple result (like a number).