一篇清楚阐述 JAvaScript 传递数据 到 Flash 的文章
JavaScript in love with Flash -- Control swf from JavaScript
l Control swf from JavaScript
JavaScript controls a Flash movie via Flash Methods. A Flash Methods is a function exposed by the Flash plug-in that can be called from a hosting scripting environment. For instance, GetVariable, SetVariable and GotoFrame are all Flash Methods. Personally, I consider this nomenclature tedious and confusing. The Flash plug-in has no difference with other types of plug-ins really. Therefore a dedicated name here can only introduce troubles. There are many available Flash Methods at hand covering almost every aspect. But I strongly suggest that you use these methods mainly to send messages to Flash movies other than control over them directly. Because a Flash movie is powerful enough to manage itself but Flash Methods are relatively messy and error prone, it's a wise choice to avoid the additional complexity.
Getting a reference
First things first, we get a reference to the object representing the Flash movie (later referred to as Flash Object) via document.getElementById. Suppose we have the value "mosquito" for the id and name parameters.
We write in JavaScript:
var flashObj = document.getElementById("mosquito"); // id passed as a string
Sometimes you see people using another equivalent way:
var flashObj = document.mosquito; // an object path expression approach
You can use either one as you wish. However, I prefer the first approach for its clarity and ease. Don't forget to always check if the flashObject is invalid.
if(flashObj == null) {
//error handling code
}
An example:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>mosquito</title>
</head>
<body>
<!--url's used in the movie-->
<!--text used in the movie-->
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="550" height="400" id="mosquito" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="mosquito.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="mosquito.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="mosquito" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
<script language="javascript">
var flashObj = document.getElementById("mosquito");
if(flashObj == null) {
alert("Fail to get a reference!");
}
</script>
</body>
</html>
Getting and Setting variables/properties
Now that a reference to the Flash Object is acquired (flashObj), anything is possible. Let's move on!
To get or set a variable or property inside a Flash movie, we need the help of GetVariable and SetVariable functions.
To get a variable/property from the Flash movie:
flashObj.GetVariable("varName");
The return value is always a string.
It is supposed that GetVariable returns null when the specified variable does not exist. But my test ended in an "unspecified error" with execution termination.
To set a variable/property of the Flash movie:
flashObj.SetVariable("varName", "newValue");
This method has no return value.
The first string parameter "varName" is the variable name that you define in the root time line or any valid target path expression. For example you can write
var x = flashObj.GetVariable("_root.mosquito_mc._x");
to get the _x property of a MovieClip named mosquito_mc.
Type hacking!!!
Careful readers may already have noticed that there is a problem related to type conversion between JavaScript and ActionScript. Since all parameters of the GetVariable and SetVariable are string and the return type of GetVariable is also string, getting and setting variables in this way guarantees no type safety. In fact no type information can be gained from or specified for the two methods.
What does this mean? Suppose that we have the following ActionScript in the first frame of the root timeline.
var age:Number = 3; // typeof(age) == "number"
Then in JavaScript we write
flashObj.SetVariable("age", "5");
This will actually set age to "5" and change its type to string silently and possibly cause runtime failure in the affected swf. Caution! Savvy hackers may use this trick to break into your code!!!
However, I found the Flash plug-in somewhat excessively clever with the type.
flashObj.SetVariable("_root.mosquito_mc._x", "5"); // no problem at all
This will change the position of mosquito_mc without any error! Maybe an internal conversion was done by the plug-in, but unfortunately we have no control over it!
We know that a MovieClip has a frequently used method named addListener. Now let's hack it!
flashObj.SetVariable("_root.addListener", "you fail! Ha-ha…");
Inside the swf, the oringal function addListener will become a string "you fail! haha…" and any proceeding call to it will definitely FAIL!
Seems that the Flash plug-in is not excessively clever enough!
What happens if we pass a non-string value as the second argument to SetVariable?
Suppose again we have the ActionScript
var age:Number=3;
//in the first frame of the root timeline.
flashObj.SetVariable("age", 5);
//This will still change age to a string of "5".
flashObj.SetVariable("age", null);
//This will cause a type mismatch in JavaScript.
flashObj.SetVariable("age", undefined);
//This will change age to an empty string, weird indeed!
flashObj.SetVariable("age", {a:1, b:2});
//This will change age to a string of value [object Object].
flashObj.SetVariable("age", somethingUndefined);
//Where somethingUndefined is an undefined identifier in the JavaScript.
//This will cause an error in JavaScript saying that an undefined identifier is used.
flashObj.SetVariable("someClassInstace.prop", value);
//Where someClassInstace is an instance of a Class and prop is a getter/setter property
//of someClassInstace.
//This will cause an error in JavaScript saying that an undefined identifier is used.
Whatever you specify for the second parameter, it will be firstly converted to a string and then passed to the core of the SetVariable. Make sure that you are passing a string otherwise the conversion may cause unwanted effects.
It is a common case that you want to get or set a numeric variable/property. I have not yet found any elegant way. Nevertheless you can watch the variable in ActionScript and explicitly convert it to Number in time. Or in another way, you can defined a getter/setter property in your MovieClip thus your own setter function will be called when SetVariable triggers, giving a chance for any necessary type conversion. This is a tricky way indeed! :-) I'll demonstrate how to do this later in the section.
Again I strongly suggest that you use these methods mainly to send messages to Flash movies other than control over them directly.
Control the play back
If your swf is mainly a sequence of frames without complex programmatic logic, it is a good idea to control the playback from JavaScript. The following Flash Methods are intended for playback control.
flashObj.Play(); // start playing the _root timeline
flashObj.StopPlay(); // stop playing the _root timeline
flashObj.Rewind(); // Rewind the _root timeline to the first frame
flashObj.IsPlaying(); // returns true if the movie is playing otherwise false
flashObj.TGotoFrame("target", frameNumber); // moves play head of the MovieClip specified
//by "target" to the frame specified by frameNumber.
flashObj.TGotoLabel("target", "label");// moves play head of the MovieClip specified
//by "target" to the frame specified by "label", "label" is the frame label you set in the Flash
//authoring environment
flashObj.GotoFrame(frameNumber); // set the play head to the frame specified by //frameNumber.
Notice that the frameNumber starts at 0, unlike in Flash where the frame number begins at 1. Before calling this method, you should ensure that the target frame has already been loaded.
if(flashObj.PercentLoaded() == 100) {
flashObj.GotoFrame(frameNumber); // frameNumber is assigned somewhere else
}
Calling a function defined in the Flash movie
There is no direct way to call a method defined in Flash from JavaScript because JavaScript and ActionScript functions and types are not compatible. But there are various ways to trigger code execution in a Flash movie from JavaScript.
You can place your ActionScript code in a particular frame (latterly referred to as Frame Function). Then in JavaScript:
flashObj.GotoFrame(frameNumber);
flashObj.TGotoFrame("target", frameNumber);
flashObj.TGotoLabel("target", "label");
These functions control Flash movie playback. Jumping to the specified frame will of course trigger the code execution there.
The following two functions execute ActionScript code at specified frame without moving the play head.
flashObj.TCallFrame("target", frameNumber);
flashObj.TCallLabel("target", "label");
Frame Functions are convenient for sequential movies. However, because each Frame Function must occupy one frame. The frames holding Frame Functions become messy and unmanageable as the application complexity grow.
ActionScript 2.0 has greatly extended the capabilities of Flash as an application platform. The fact is that sequential frames are not as useful as before in an application. Instead, OOP and design patterns become more and more active in the development of large and rich internet applications.
Here I recommend a reasonably better approach using getter/setter properties. Getter/setter properties appear similar to other properties except that any access to them are delegated by user defined functions. More, if a SetVariable is applied to a getter/setter property, the corresponding setter function will be called.
An example, suppose we have a Flash movie and we want to get or set the age property of it.
In ActionScript we defined age as a getter/setter property, shown as below.
//mosquito.fla first frame of root timeline
import mx.utils.Delegate;
import mx.controls.Alert;
var name:String = "Timmy";
var _age:Number = 3; //hold the actual value of property age
//getter method for property age
function getAge():Number {
return _age;
}
//setter method for property age
function setAge(val:Number) {
var old:Number = this._age;
_age = Number(val); //type conversion if necessary
/* ... */
Alert.show("age has been changed!\nold value: " + old + "\nnew value: " + _age, "setter method triggered");
}
this.addProperty("age", getAge, setAge); //register the age property
In our HTML document:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>mosquito</title>
</head>
<body>
<!--url's used in the movie-->
<!--text used in the movie-->
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0" width="550" height="400" id="mosquito" align="middle">
<param name="allowScriptAccess" value="sameDomain" />
<param name="movie" value="mosquito.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#ffffff" />
<embed src="mosquito.swf" quality="high" bgcolor="#ffffff" width="550" height="400" name="mosquito" align="middle" allowScriptAccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" />
</object>
<script language="javascript">
var mySwf = document.getElementById("mosquito");
mySwf.SetVariable("age", "5");
}
</script>
</body>
</html>
In this way the age property will be properly set and an Alert will be displayed in the Flash movie showing the old and new value of the getter/setter property age. In this demonstration, we popup an Alert box in Flash. You can replace it with anything you need. In SetVariable("varName", "newValue"), "newValue" is a single string. What if we want to pass multiple parameters? We can put the parameters into a comma separated list and pass the list as a string. For example, SetVariable("varName", "param1, param2, param3"). Then we parse the list and extract what we need in the setter method for the varName property.
This approach is somewhat like an event dispatching process which is generally considered suitable for large scale applications.
Other Flash Methods
Many more Flash Methods are available, see
http://www.macromedia.com/support/flash/publishexport/scriptingwithflash/scriptingwithflash_03.html
for more information.