Copy link to clipboard
Copied
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!
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 even
...Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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!
Copy link to clipboard
Copied
This is wonderful!
You're welcome!
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
That's useful to know, thanks. Do you know where a tick listener falls in time? Like, does a tick listener function extend the current frame duration, in the way that exitframe would do in AS3 or Lingo? Even if tick is handled before draw starts, is there any drawback (pardon the pun) to using the drawstart approach instead of a tick listener?
By the way, one worry is that drawstart would happen when everything is ready to draw. If you then change the state of something, will the new state be drawn, or the state before the change?
I'll be trying it out anyway!
Copy link to clipboard
Copied
I just wanted to complement you for mentioning Lingo . And that this is one of the best flash posts in years. I am coming back to the IDE after years of frustration with React and everything else post 2010. This is gold.
Copy link to clipboard
Copied
Before I was well known for working in Flash Pro, I was famous for working in Director.. Quite a few Director engineers went on to work on Flash Pro, which is why, for example, Flash 8 gained some of the imaging code that was already in Director. Another thing that was added to ActionScript was exitframe.
For a long time Director didn't have enterframe, and so it did suffer from the problem of complex code causing the frame to last longer than it should. I guess exitframe is useful for jumping to another part of the timeline, knowing that the current frame has been shown for long enough. But for getting lengthy code to run, enterfame is good in both tools, because it runs while the current frame is being displayed.
Copy link to clipboard
Copied
Forget most of what I just said. It looks like the drawstart approach is a one time thing, not every frame. Maybe it's also when you change frames? But staying on one frame and using drawstart instead of tick, stops anything you were animating with code.
Copy link to clipboard
Copied
No, drawstart executes once per tick, regardless of whether the playhead is advancing. This will rapidly fill up the console:
this.stop();
stage.addEventListener("drawstart", function(){console.log("hello")});
Regarding listenening for a tick event, as I noted in my older post, that delays code execution until the next frame (or frame render).
Copy link to clipboard
Copied
Could I be using the 'this' or 'true' wrongly? Here you can see my normal line and the replacement line, and with the normal way I can flick some movieclips and over time they come to a halt. With drawstart they stop moving as soon as I release the mouse button:
//stage.on("drawstart", inertia, this, true); | |
createjs.Ticker.addEventListener("tick", inertia); |
Copy link to clipboard
Copied
Remember, passing true as the fourth argument says to only execute the listener once. That makes it useful for initialization code.
The distinction between listening for tick vs drawstart appears to be... subtle. If you stick this in the first frame of a multi-frame timeline:
createjs.Ticker.on("tick", function(){console.log("Tick:"+this.currentFrame)}, this);
stage.on("drawstart", function(){console.log("Drawstart:"+this.currentFrame)}, this);
You get:
Drawstart:0
Drawstart:1
Tick:1
Drawstart:2
Tick:2
Drawstart:3
Tick:3
etc...
So drawstart appears to be guaranteed to fire within the same frame the listener is added.
Copy link to clipboard
Copied
Adding some new info to this old thread. I recently found it necessary to confirm the exact order of event execution in Canvas documents, and this is what I found.
Animate HTML5 Canvas Event Order
(CreateJS 2015 and 1.0.0)
That last item refers to setTimeout code scheduled with a delay of zero, as described elsewhere in this thread.
Copy link to clipboard
Copied
Interesting post !
Colin, Clay, Joao, have you looked deeply into the animate JS final output file?
From a glance, the load order/queuing of code within the final animate may be influencing this ?
The lib is processes from the get go, so you can at any time call anything from the library directly.
Then, the rest of the content, as animate uses a timeline approach, is loaded as separate functions as dictated by your placement in the timeline on your stage.
Adding to that, if you have 1 frame with code, and a movie clip, it looks like the timeline functions are processed first, then the stage items. Layer order with regards to the code seems to be ignored, its always loaded first, even if the code layer is at the bottom.
Which falls in line with the recommend" placing code on the second frame, not the first" to ensure all the visual elements have been loaded that many people here have said ?
But, not sure if this is the case, haven't had time to look deeply into it.
Regards,
Copy link to clipboard
Copied
My usual approach is to have the symbols I want to talk to be on a given frame, and then have the code that talks to them be on the next frame. Then I'm sure they are around. Clay's drawstart idea is a away to do that on the same frame.
My other way of working is to put the code needed to set up a symbol in the first frame of that symbol. This is sometimes the only realistic way to do some things. For example, I have animations where the animated character needs to be customized on the fly, based on selections the user had made previously. Like, they had chosen a certain head type, different shirts, hat, eyewear, and so on. In each of those I might have something like (this would be the hat symbol):
this.gotoAndStop(window.hatnumber);
Previously window.hatnumber has been set to be the variation of the hat that the user wanted. In the hat symbol would be as many frames as there are hats, and when the user chose a hat it would have been (for JavaScript), 0-n (n being one less than the number of hats).
The good thing about this approach is both that the timeline of the symbol does exist at the time that I this.gotoAndStop(window.hatnumber);, but more importantly it doesn't matter how deep in the hierarchy the symbol is. If I did the same thing from the main timeline I might have something like:
this.character1.head.hat.gotoAndStop(this.hatnumber);
That's fine if the character is at the main timeline, but what if I put it into an animated scene, that is inside another holder symbol. The depth to the hat could get to be:
this.scene.room1.character1.head.hat.gotoAndStop(this.hatnumber);
It's not worth the hassle!
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
https://forums.adobe.com/people/Colin+Holgate wrote
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.
If I'm understanding the experiment you're describing correctly, this result makes perfect sense. Tick, drawstart, and drawend events will never fire any faster than the CreateJS frame rate. setTimeout with an interval of 0, on the other hand, will execute its callback as frequently as possible, continuously pushing itself onto the browser event queue. Probably not a great idea for a continuous animation, since this could easily blow up CPU utilization and yield an uneven frame rate.
Copy link to clipboard
Copied
Hate to be that guy that revives a year-old thread, but I found it extremely helpful.
Special thanks to everyone that chimed in.
I've been really excited about teaching beginners JavaScript using Animate CC but every time I try to get into it i run into these "hacks"
I just can't fathom how in 2019, we can't write code on the first frame that simply does
my_mc.x = 300; //works great.
my_mc.gotoAndStop(2); // fails
but now I have to write all this:
stage.on("drawstart", myInit, this, true);
function myInit() {
this.my_mc.gotoAndStop(2);
this.my_mc.x = 300
}
Perhaps I'm misunderstanding something, but is there any reason why Frame 1 code on the main timeline just wouldn't naturally wait for the first "drawstart" to fire by default? Shouldn't this be handled behind the scene when we export?
does that seem like a reasonable request?
I get warned 10,000 times when I export that EaselJS frame numbers start at 0... but nothing about something as simple as telling a movieclip to go to another frame isn't allowed. lol
Copy link to clipboard
Copied
Hey folks, good news. I was just using Animate 20.0.1 and you no longer need to wait a frame or do ANY tricks to access children of a movie clip on frame 1. One of my biggest problems with Animate / HTML5 is now gone!
let letters = this.words.children;
console.log(letters); // IT WORKS!
gsap.timeline({repeat:9})
.from(letters, {y:100, alpha:0, stagger:0.1, scaleX:0.2, ease:"back"})
.to(letters, {y:-50, alpha:0, duration:0.25, stagger:-0.05}, ">0.5");
Anyone know is this something the createJS folks fixed or the Animate team?
Didn't see any mention of it in the release notes.
Either way, I'm so happy!
Copy link to clipboard
Copied
That should be "var", not "let". let isn't implemented correctly in IE11, so should just be avoided altogether.
Copy link to clipboard
Copied
Great thread, I have had good luck with drawstart, tick, & added (based on your classes link), with changing my dynamic button labels onLoad.
I still cannot get my buttons (movieClips) to simply gotoAndStop on "rollover" or "mouseover" using either frame numbers or label...any help?
this.btn01.addEventListener("rollover", fl_MouseOverHandler.bind(this));
function fl_MouseOverHandler(e) {
//simply plays timeline and loops back to frame 1, even tho 'this.stop()'
e.currentTarget.gotoAndPlay("over");
//e.currentTarget.gotoAndStop("over"); //does nothing
//e.currentTarget.gotoAndStop(0); //does nothing
//e.currentTarget.gotoAndStop(1); //does nothing
}
Each buttonMC has unque label created from MC symbol and listeners appear to be applied correctly. Each fire alerts and individual labels applied....ANY IDEAS?