Search code examples
htmlflashhtml5-canvascreatejs

Access root stage instance when button is clicked


I have an instance of a button called myBtn on stage, and an instance of a square movie clip called myContainer- all of which published from flash.
Within myContainer i have a movieclip instance named mySquare which is animated via classic tween from one point of the stage to another.
When the stage is loaded i use this.myBtn.on("click", startBtnOnClick) to add an event listener to the button, and the function startBtnOnClick(evt) is indeed successfully called.
What i'm trying to achieve is to tell myContainer to start playing when the button is clicked, and to do so i currently need to use:
evt.target.parent.parent.myContainer.gotoAndPlay(0) which works, but doesn't seem to be a best practice here.
What would be a more suitable approach?

The code is generated when i publish from Adobe Flash using HTML5 document type. My addition was the startBtnOnClick() function definition and assigning the event handler to the myBtn instance

Edit:

Two files are created when i publish. In order to play the movie you open the html file.

SquareTrial.html:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>SquareTrial</title>

<script src="http://code.createjs.com/easeljs-0.8.1.min.js"></script>
<script src="http://code.createjs.com/tweenjs-0.6.1.min.js"></script>
<script src="http://code.createjs.com/movieclip-0.8.1.min.js"></script>
<script src="SquareTrial.js"></script>

<script>
var canvas, stage, exportRoot;

function init() {
    canvas = document.getElementById("canvas");
    exportRoot = new lib.SquareTrial();

    stage = new createjs.Stage(canvas);
    stage.addChild(exportRoot);
    stage.update();

    createjs.Ticker.setFPS(lib.properties.fps);
    createjs.Ticker.addEventListener("tick", stage);
}
</script>
</head>

<body onload="init();" style="background-color:#D4D4D4">
    <canvas id="canvas" width="550" height="400" style="background-color:#FFFFFF"></canvas>
</body>
</html>

and SquareTrial.js:

(function (lib, img, cjs, ss) {

var p; // shortcut to reference prototypes

// library properties:
lib.properties = {
    width: 550,
    height: 400,
    fps: 24,
    color: "#FFFFFF",
    manifest: []
};


// functions:

function startBtnOnClick(evt)
{
    console.log("startBtnOnClick called.");

    console.log("stage:");
    console.log(stage);

    console.log("stage.myContainer:");
    console.log(stage.myContainer);

    console.log("this.myContainer:");
    console.log(this.myContainer);
}


// symbols:


(lib.MySquare = function() {
    this.initialize();

    // Layer 1
    this.shape = new cjs.Shape();
    this.shape.graphics.f().s("#000000").ss(1,1,1).p("AoWoWIQtAAIAAQtIwtAAg");
    this.shape.setTransform(53.5,53.5);

    this.shape_1 = new cjs.Shape();
    this.shape_1.graphics.f("#6633FF").s().p("AoWIXIAAwtIQtAAIAAQtg");
    this.shape_1.setTransform(53.5,53.5);

    this.addChild(this.shape_1,this.shape);
}).prototype = p = new cjs.Container();
p.nominalBounds = new cjs.Rectangle(-1,-1,109,109);


(lib.MyBtn = function() {
    this.initialize();

    // Layer 1
    this.shape = new cjs.Shape();
    this.shape.graphics.f().s("#000000").ss(1,1,1).p("AprjWITXAAIAAGtIzXAAg");
    this.shape.setTransform(62,21.5);

    this.shape_1 = new cjs.Shape();
    this.shape_1.graphics.f("#0099FF").s().p("AprDXIAAmtITXAAIAAGtg");
    this.shape_1.setTransform(62,21.5);

    this.addChild(this.shape_1,this.shape);
}).prototype = p = new cjs.Container();
p.nominalBounds = new cjs.Rectangle(-1,-1,126,45);


(lib.MyContainer = function(mode,startPosition,loop) {
    this.initialize(mode,startPosition,loop,{});

    // Layer 1
    this.mySquare = new lib.MySquare();
    this.mySquare.setTransform(53.5,53.5,1,1,0,0,0,53.5,53.5);

    this.timeline.addTween(cjs.Tween.get(this.mySquare).to({x:397.5},47).wait(1));

}).prototype = p = new cjs.MovieClip();
p.nominalBounds = new cjs.Rectangle(-0.5,-0.5,108,108);


// stage content:
(lib.SquareTrial = function() {
    this.initialize();

    // button
    this.myContainer = new lib.MyContainer();
    this.myContainer.setTransform(102.5,99.5,1,1,0,0,0,53.5,53.5);

    this.myBtn = new lib.MyBtn();
    this.myBtn.setTransform(111,337.5,1,1,0,0,0,62,21.5);

    //this.myBtn.on("click", startBtnOnClick); //<< does not work
    this.myBtn.on("click", startBtnOnClick, this);


    this.addChild(this.myBtn,this.myContainer);
}).prototype = p = new cjs.Container();
p.nominalBounds = new cjs.Rectangle(323.5,245.5,125,314);

})(lib = lib||{}, images = images||{}, createjs = createjs||{}, ss = ss||{});
var lib, images, createjs, ss;

Solution

  • If you are just looking for the stage, you can use the stage property (getter) on any display object, which returns the stage the instance lives on. It will return null if it can not reach the stage.

    var stage = evt.target.stage;
    

    Otherwise, you might want to look at your application architecture. For instance, you can bind your event handler to the current scope by passing this as the 3rd parameter of the on() method. This will make your startBtnOnClick method fire in your app scope, rather than making you look up the scope relative to the clicked button.

    this.myBtn.on("click", startBtnOnClick, this);
    

    [Edited based on posted content]

    Your update shows that myContainer is a child of your Flash stage, which is actually exported as the SquareTrial function in your library JS (named after your FLA). This class is instantiated as exportRoot in the HTML.

    The way you define the function inside the library is a little unorthodox. It is basically an anonymous function, but the bigger issue is that if you re-export your library from Flash, it will get overwritten. I recommend either moving it to another script you include, or actually including it in your frame script.

    Anyways, on to the actual function. Since you use the on() method to call it in scope, the this variable will refer to the SquareTrial instance (aka the exportRoot). You should be able to reference this.myContainer in your startBtnOnClick method.

    The stage getter I suggested is not ideal, since the EaselJS stage only contains the exportRoot. It would also need to be accessed as this.stage. The stage reference you use does exist, but it is the global variable created in the index.html.

    So, using the new approach, you should be able to do this:

    // In your frame script:
    this.myBtn.on("click", startBtnOnClick, this); 
    
    // In your function callback
    console.log(this); // Should be the instance, and NOT window.
    this.myContainer.gotoAndPlay(0);
    

    If you decided to stick with an un-scoped, anonymous function, then you can reference your content via the global exportRoot variable:

    exportRoot.myContainer.gotoAndPlay(0);
    

    Hope that clears up any confusion.