6 Replies Latest reply on Apr 30, 2014 1:42 PM by sinious

    How do I place javascript code after layer initialization

    drkstr_1 Level 4

      Hello all,

       

      I am giving the HTML5 Canvas thing a try in Flash CC, but all instance references to items on the stage are coming up undefined when I try to reference them in JavaScript code sitting on the layers panel.

       

      When I open the generated output, I notice that all of my code is placed before the layers are even initialized. I tried placing the code layers at the bottom, but this had no affect on the output.  Is there an easy way to have code in the layers panel run after initialization, or do I just need to add an extra frame to all of my projects so I can get the code in the correct place?

       

       

      Thank you for your time!

        • 1. Re: How do I place javascript code after layer initialization
          drkstr_1 Level 4

          It turns out it was a scoping issue rather than issue with the execution order. Any code on the stage gets wrapped in a function when exported, so you need to capture a local reference to "this", then reference the stage through that instance.

           

          EG.

           

          var self = this;
          this.frame_0 = function()
          {
               self.someInstance.doStuff();
          }
          

           

           

          I would still very much appreciate suggestions if anyone has a solution that does not require a manual edit every time the file is published.

           

           

          Cheers and thank you!

          • 2. Re: How do I place javascript code after layer initialization
            sinious Most Valuable Participant

            A prototype Function inherits its current context. You're creating self = this right above the function. You should be able to access it without that, e.g.

             

            this.someInstance.doStuff();

             

            For example, say you make a new symbol in the library that is an animation of a ball. You animate the ball in a motion tween and you add a new layer inside adding a frame script to the ball:

             

            this.stopAnimating = function() {

                 this.gotoAndStop(0); // remember frames are 0-based, not 1

            }

            This adds a function to the ball clip that can be invoked.

             

            Now you add a linkage ID of 'Ball' to the symbol and go in your main timeline and add that ball clip to the stage:

             

            var b = new lib.Ball();

            b.x = b.y = 100;

            this.addChild(b);

             

            // set timeout to run that stopAnimating function

            setTimeout(function(){ b.stopAnimating(); }, 3000);

             

            Because I created the anonymous function in the scope of the frame script it will have access to 'b'. If I didn't do that and passed setTimeout b.stopAnimating instead, I'd be running from global scope (window) and it would fail, because in that scope 'b' does not exist:

             

            setTimeout(b.stopAnimating, 3000); // wrong, b doesn't exist in global

             

            So yes, you're catching on that there's a lot of scope to understand when using JavaScript (right back to the ActionScript 2.0 days.. "What is my context?"...)

            1 person found this helpful
            • 3. Re: How do I place javascript code after layer initialization
              drkstr_1 Level 4

              Much appreciated for the guidance! I have to admit, my JS is a bit rusty.

               

               

               

              There are actually two-levels of anonymous functions, but I'm not sure if that makes any difference.

               

              Here is the full code minus the uninteresting bits.

               

              (function(lib, img, cjs)
              {
              
                  //init stuff snipped...
              
                  // stage content:
                  (lib.FrameStackJS = function(mode, startPosition, loop)
                  {
                      if (loop == null)
                      {
                          loop = false;
                      }
                      this.initialize(mode, startPosition, loop, {});
              
                      var self = this; // <--- I added this by hand
                     
                      // timeline functions:
                      this.frame_0 = function()
                      {
                         //code sitting in actions layer of the stage...
                          console.log("this.someInstance = "+this.someInstance);
                          console.log("self.someInstance = "+self.someInstance);
                      }
              
              
                  }).prototype = p = new cjs.MovieClip();
              
                  //rest of the stuff snipped...
              
              })(lib = lib || {}, images = images || {}, createjs = createjs || {});
              var lib, images, createjs;
              
              

               

               

              this.someInstance turns up undefined, where self.someInstance contains a reference to the MC sitting on the stage.

               

               

              This process will be done on hundreds of fla's, most of which are using 'this' to reference the stage in one way or another (yes yes, I told them not to do that).

               

              Any suggestions for a quick and easy work around that won't involve rewriting all our legacy cruft with FP techniques?

              • 4. Re: How do I place javascript code after layer initialization
                sinious Most Valuable Participant

                That's pretty strange. It works for me. I just draw a circle, named my FLA "MyBall" (you'll see this referenced) and named my ball library item with a linkage of Ball. I put it on stage and gave it the instance name 'myBall'. I duplicated it and changed the instance name to 'secondBall'. I actually nested them both and made a Classic Tween to make it easy to see I'm accessing them just fine. They just animate left and right on the screen over 60 frames.

                 

                Relevant JS:

                 

                // stage content:

                (lib.MyBall = function(mode,startPosition,loop) {

                          this.initialize(mode,startPosition,loop,{});

                 

                          // timeline functions:

                          this.frame_0 = function() {

                                    console.log(this.myBall);

                                    console.log(this.secondBall);

                 

                                    this.myBall.newFunction = function() {

                                              console.log(this);

                                              this.gotoAndStop(0);

                                    }

                 

                                    this.myBall.newFunction();

                  }

                 

                          // actions tween:

                          this.timeline.addTween(cjs.Tween.get(this).call(this.frame_0).wait(1));

                 

                          // ball

                          this.secondBall = new lib.Ball_1();

                          this.secondBall.setTransform(172.5,301.5,1,1,0,0,0,50.5,50.5);

                 

                          this.myBall = new lib.Ball_1();

                          this.myBall.setTransform(172.5,178.5,1,1,0,0,0,50.5,50.5);

                 

                          this.timeline.addTween(cjs.Tween.get({}).to({state:[{t:this.myBall},{t:this.secondBa ll}]}).wait(1));

                 

                }).prototype = p = new cjs.MovieClip();

                 

                You can see my logs do just the same as yours except I return a valid object all 3 times. I also added in a function literal directly on myBall to use 'this' to show you it properly scoped to myBall. When I run the function, the ball stopped animating.

                 

                Here's Chrome's developer showing the console log:

                 

                ball.png

                Each reference is perfect. If you just console.log(this) you'll see you're in your own FLA. In a single timeline FLA, inside stage.children[0] basically. I can control them right from there from the names I made.

                 

                e.g. to stop secondBall, in developer console:

                 

                stage.children[0].secondBall.stop(); // the ball stops animating

                 

                When you console.log(this), do you see: lib.FLADocName ?

                1 person found this helpful
                • 5. Re: How do I place javascript code after layer initialization
                  drkstr_1 Level 4

                  Hmm, that is odd. Many thanks for a working example!

                   

                  I will get back to you once I can reduce the problem enough to find the disconnect.

                   

                  Cheers!

                  • 6. Re: How do I place javascript code after layer initialization
                    sinious Most Valuable Participant

                    You're welcome. JavaScript, encapsulation and scope, always a bear .

                     

                    If you have any live examples to peek at (just throw together demos that replicate your issue) it'd be easier to diagnose if you shared that.

                     

                    Ultimately just get in the JavaScript console and start lurking around the objects you know to exist (window.createjs, window.lib, etc). You'll find it all nested in there.