Skip to main content
New Participant
May 19, 2018
Answered

Accessing movieClip.children array Animate CC Canvas

  • May 19, 2018
  • 3 replies
  • 8545 views

Hello,

I’m trying to make a reusable widget for our design team, and instead of the user having to put this.movieClipInstances in a array inside my logic, I’d like to sort through the children of my container movieClip using Easlejs myContainer.children and storing them dynamically. If I console.log myContainer.children, I get an array with all the children in the movie clip - Great! But if I try to get the length or try to get an index of container.children[0] it returns undefined.  I am so confused as to why this shows up but I’m unable to access it?

On the stage I have a movieClip instance myContainer.

In myContainer I have three movie clip instances test_01, test_02, test_03.

(Inside my container movieClip)

var c = this;

var arrChildren = c.children;

console.log(arrChildren); //returns array

console.log(arrChildren.length); //returns 0

console.log(arrChildren[0]); //returns undefined

If I console.log arrChildren, I get:

Array(3)

0: lib.Symbol1{objects properties}

1: lib.Symbol1{objects properties}

2: lib.Symbol1{objects properties}

length:3

__proto__:Array(0)

Does anyone know why I can’t loop through this array or access even it’s length?

Any help from the community would be awesome. Thank you in advance!

This topic has been closed for replies.
Correct answer ClayUUID

If you want to keep your widget more code-driven, you don't have to add another timeline frame. To wait the duration of one frame using code, do this:

createjs.Ticker.on("tick", myInitFunction, this, true);

function myInitFunction() {

    // your initialization code...

}

However, you don't even really need to wait an entire frame to make things work right. The problem you were experiencing happens because of execution order. CreateJS calls timeline code first, then child initialization code. If you could somehow shuffle your code to execute after all the child initialization, but before the next frame begins, it would work fine. And huzzah, you can!

setTimeout(myInitFunction.bind(this), 0);

function myInitFunction() {

    // your initialization code...

}

Using setTimeout() with a delay of 0 basically yields execution to whatever else the browser has to do (in this case running the rest of the setup code and rendering the current frame), and schedules the function you give it to execute as soon as possible.

javascript - Why is setTimeout(fn, 0) sometimes useful? - Stack Overflow

javascript - What is setTimeout doing when set to 0 milliseconds? - Stack Overflow

If you stick console.log(this.currentFrame); in your init code, you'll see that the first variant executes on frame 1, but the second variant executes while still on frame 0. So it's probably the better option for getting your widget fully operational as quickly as possible.


I'd just like to add some more information to the above post. While the setTimeout approach does work for safely executing initialization code in the same frame, it has the disadvantage that it executes after rendering. So any visual properties you've changed won't show up until the next stage update. However, it turns out the CreateJS API provides an even better approach. You can do this instead:

stage.on("drawstart", myInitFunction, this, true);

Every time the stage is updated, it fires two events-- "drawstart" before it starts drawing, and "drawend" when it's done. Hooking an event listener to "drawstart" allows executing setup code with fully initialized clips, but before anything is actually drawn for the current frame/tick. For example you can do this.my.deeply.nested.clip.stop(); in the event handler and it will work, without having to wait a frame.

The reason to use on instead of addEventListener is because of the extra functionality on provides. The third argument specifies the scope that the event handler executes in (so you don't have to mess with bind), and the fourth argument specifies that the listener only executes once.

3 replies

Known Participant
March 8, 2021

HI, I've read this whole post after 5 hours of works to understand why it doesn't work.

For a game to learn to read, I have a MC with several frames (one per letter).

When i'm on the good frames, i have two children : a background and the letter.

And in this letter, i have many parts (in fact each letter is an animal with mouth, nose ...).

With the method

stage.on("drawstart", this.suite, this, true);

it works for the first level but not for the second :

the code :

class BoutRepOrdre extends createjs.MovieClip {
    
    constructor() {
    super();
    this.alpha = new lib.Alpha();
    this.addChild(this.alpha);
    this.alpha.gotoAndStop("m");
    stage.on("drawstart", this.suite, this, true);
    // delai = setTimeout(this.suite.bind(this), 200);
  }
  suite() {
    console.log("children list :");
    console.log(this.alpha.children);
    console.log("children length :");
    console.log(this.alpha.children.length);
    console.log("first children :");
    console.log(this.alpha.children[0]);
    console.log();
    console.log("children list :");
    console.log(this.alpha.children[0].children);
    console.log("children length :");
    console.log(this.alpha.children[0].children.length);
    console.log("first children :");
    console.log(this.alpha.children[0].children[0]);
  }

 and the result is :

I have to finish my work for last week 🙂 ... if you've an idea.

I've tried with setTimeout and by adding a second frame to each of the 14 parts but no way.

Thanks

Known Participant
March 8, 2021

I answer to myself and for all those who have the same problem :

for each level, use satge.on(drawstart" ....)

class BoutRepOrdre extends createjs.MovieClip {
    
    constructor() {
    super();
    this.alpha = new lib.Alpha();
    this.addChild(this.alpha);
    this.alpha.gotoAndStop("m");
    root.addChild(this);
    stage.on("drawstart", this.suite, this, true);
  }
  suite() {
    this.perso = this.alphaRep.perso; //my alphaRep.children[0]
    stage.on("drawstart", this.suite2, this, true);
  }
  suite2() {
    console.log(this.alpha.perso.children.length);
  }
}​

 

 

i have to put the root.addChild in the first function before the function suite2 otherwise i can't access to this.alpha.perso.children and it will not work!!

 

 

JoãoCésar17023019
Community Expert
June 12, 2018

All of this information is really useful. I've unchecked my answer as the correct and marked Clay's.

And I think it would be really useful if someone from the CreateJS team could stop by once in awhile here in the forums to give us insight about the application of these libraries inside of Animate CC.

I know there is the official documentation but in my experience and seeing the doubts from the users that show up here, I feel that there are many details that could be clarified.

Or the Animate Team could provide a online doc for this integration.

Colin Holgate
Inspiring
June 12, 2018

I found this page:

https://createjs.com/docs/easeljs/classes/Stage.html#event_drawstart

and now realize that for my application, drawend is more useful. drawstart will solve the problem of talking to things and knowing that they are initialize, and what you do will show up even in the first redraw. But I was interested in something equivalent to enterframe.

The good thing about enterframe is that you are doing your time consuming things after the frame is drawn, and you have 1/framerate of a second of time to finish doing those things. exitframe will always extend the duration of the current frame, to be 1/framerate of a second, plus how long it takes for your code to run. Similarly, timerevents could happen at any time, and will very often extend the length of the current frame.

So, if I'm right (and I'll try to test it), using drawend to trigger when your time consuming things will happen could improve performance.

Colin Holgate
Inspiring
June 12, 2018

I suspect using setTimeout with a delay of 0 would do what you want, since that will defer execution of your callback until CreateJS is done and waiting for the next tick.

Hmm... although, scheduling that to execute on every frame could be tricky. Drawend probably would be easier.


I tried it anyway. All sorts of test of the other three ways of working ended up being much the same time to do a lot of complex tasks. But then I changed to looking at how far a complicated animation had advanced in the same time. With tick, drawstart, and drawend, the animation had advanced about 12 frames during the test. With setTimeout it advanced 50 frames.

My conclusion is that behind the scenes CreateJS is doing a lot to keep regular timing, no matter what technique you're doing, and setTimeout is a way to defeat that.

JoãoCésar17023019
Community Expert
May 19, 2018

Hi.

I changed the size of your text because it was too small.

About your question, it seems that in HTML5 (Canvas) documents, Movie Clips on stage are always accessible, you can console.log them, and so on, but their internal contents/timeline no. Not always.

Run a test:

Extend the layer with the content until the 2nd frame and also put the code in the second frame and see if you get the result you're expecting.

Viki-lynnAuthor
New Participant
May 19, 2018

You are awesome, that worked. I've been beating my head on my desk for a day so I thank you for your response 

I changed nothing but extended the script to frame 2 (and added a stop of course) and I am able to access the children array as expected.

Many thanks!

JoãoCésar17023019
Community Expert
May 19, 2018

This is wonderful!

You're welcome!