Javascript <-> Flash мост

Эта статья описывает, как вызывать из Javascript методы Flash и наоборот.

Используя эти способы, javascript может получить доступ к буферу обмена ОС, хранимым объектам SharedObject, управлять flash-интерфейсами и многое другое.

Связь JS с объектом Flash

Какой бы способ коммуникации мы не выбрали, для начала JS должен уметь находить объект Flash на странице.

Для того, чтобы все работало кроссбраузерно, Flash-ролик нужно вставить с использованием обоих тегов: object и embed, например так:

<object 
    id="BridgeMovie" width="400" height="200"        
    classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"   
 codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab">
    <param name="movie" value="bridge.swf" />
    <param name="allowScriptAccess" value="sameDomain" />
    <embed
        src="bridge.swf" 
        name="BridgeMovie" align="middle"
        play="true" loop="false" quality="high" allowScriptAccess="sameDomain"
        width="400" height="200" scale="exactfit" 
        type="application/x-shockwave-flash"
        pluginspage="http://www.macromedia.com/go/getflashplayer">
    </embed>
</object>

В этом примере существенны детали:

  1. Имя ролика BridgeMovie дублируется как object.id и embed.name.
  2. Путь к ролику bridge.swf дублируется в object/embed
  3. Параметр allowScriptAccess="sameDomain"
  4. Дублируются размеры ролика

Теперь объект ролика можно получить из window["BridgeMovie"] для IE или document["BridgeMovie"] в остальных браузерах:

function getMovie() {
    var M$ =  navigator.appName.indexOf("Microsoft")!=-1
    return (M$ ? window : document)["BridgeMovie"]
}

Далее мы разберем средства для коммуникации с этим роликом.

Flash вызывает Javascript

getURL

Самый древний, знакомый большинству флешеров способ - это вызов javascript-функции посредством getURL с протоколом javascript:

getURL('javascript:функция("параметры");');

fsCommand

Вызов осуществляется так:

fsCommand("функция", "параметры")

Чтобы принять этот вызов, в Javascript должна быть специальная обвязка.

Редактор Macromedia Flash может генерировать ее автоматически при публикации ролика.

Для этого нужно в Publish Settings:

  1. Во вкладке Formats включить публикацию html-файла
  2. Во вкладке HTML выбрать Template: Flash with FSCommand

Генерируемый шаблон javascript-обвязки состоит из двух частей: функция обработки и дополнительного кода для IE.

Функция обработки fsCommand

Название функции имеет вид <ИмяРолика>_DoFSCommand. В нашем случае это BridgeMovie_DoFSCommand

Первый аргумент - имя вызываемой функции, второй - строка аргументов.
Простейший вариант выглядит так:

function BridgeMovie_DoFSCommand(command, args) {
    // вызвать функцию command с аргументом args
    window[command].call(null, args)
}

Вызов, например, функции show из Flash приведет к цепочке вызовов:

  1. (Flash) fsCommand("show","something")
  2. (JS) BridgeMovie_DoFSCommand("show","something")
  3. (JS) show("something")

Дополнительная обвязка для IE/Win

Этот код нужен только для IE под Windows, в дополнение к BridgeMovie_DoFSCommand, так как в этом браузере fsCommand вызывает не Javascript, а VBScript.

if (navigator.appName && navigator.appName.indexOf("Microsoft") != -1 
&& navigator.userAgent.indexOf("Windows") != -1) {
        document.write('<script language=\"VBScript\"\>\n');
        document.write('On Error Resume Next\n');
        document.write('Sub BridgeMovie_FSCommand(ByVal command, ByVal args)\n');
        document.write('        Call BridgeMovie_DoFSCommand(command, args)\n');
        document.write('End Sub\n');
        document.write('</script\>\n');
}

Здесь VBScript просто передает вызов Javascript'у.

Итак, плюсы и минусы метода fscommand:

  • Flash 5 и выше
  • Не работает под Mac OS
  • Не возвращает результат
  • Нужен дополнительный JS-код

ExternalInterface

Этот способ работает, начиная с Flash 8. В отличие от всех предыдущих способов, он не только вызывает javascript, но и передает обратно возвращаемое значение, по возможности сохраняя его тип.

import flash.external.ExternalInterface;
var result = ExternalInterface.call("func", "param1", "param2", ...);

К сожалению, начиная с Flash 8, где он впервые появился, в ExternalInterface нашли большое количество разнообразных багов. Глюки сериализации, самопроизвольное изменение передаваемых данных и т.п.

Эти ошибки поправлены в новейших версиях Flash Player, но многие продолжают использовать более старые редакции Flash 8,9.

Актуальную информацию об ошибках можно получить из google, набрав "ExternalInterface bug".

  • Простота
  • Возвращает результат
  • Разнообразные баги, в отличие от предыдущих способов

Передача данных из Javascript во Flash

SetVariable

Самый простой способ - установка переменной ролику:

getMovie().SetVariable("message","data")

Обратите внимание - именно SetVariable, не setVariable. Регистр здесь важен.

Чтобы Flash получил значение - можно проверять сообщения, например, на каждом кадре. Это около 12 раз в секунду.

Следующий код срабатывает на каждом кадре и ждет появления значения переменной message.

var message = null
_root.onEnterFrame = function() {
	if (message!==null)	{
		_root.txtRecieve.text = message // получили сообщение
		message = null
	}
}
  • Простота и надежность
  • Кросс-браузерность, Flash 5+
  • Дорога в одну сторону, не больше одного сообщения за кадр.

ExternalInterface

Применив ExternalInterface из Flash8+, можно объявить флеш-метод, который будет обрабатывать вызовы из javascript.

Формат вызова:

ExternalInterface.addCallback(функция JS, объект Flash, функция Flash);.

Например, следующий код устанавливает глобальную функцию recieveFromJS как обработчик JS-вызова sendFromJS.

import flash.external.ExternalInterface;
...

ExternalInterface.addCallback("sendFromJS", null, recieveFromJS);

function recieveFromJS(msg) {
	_root.txtRecieve.text = msg;
}

В JS достаточно сделать простой вызов:

getMovie().sendFromJS(value);
  • Можно тут же получить значение из Flash
  • Множество багов в ExternalInterface.

Другие способы

Можно также использовать LocalConnection, как это сделано во Flash-Javascript Integration Kit.

Пример. Исходники

Этот пример пересылает текст из верхнего JS-поля направо во Flash, из нижнего Flash-поля - налево в JS.

Вводите любой, текст и жмите кнопку для пересылки нужным методом.

Послать из JS во Flash:
->
<-

Код на ActionScript

// Flash Storage example

import flash.external.ExternalInterface;

ExternalInterface.addCallback("sendFromJS", null, recieveFromJS);

function recieveFromJS(msg) {
	_root.txtRecieve.text = msg;
}


_root.button.onRelease = function() {
	fscommand("recieveFromFlash", _root.txtSend.text);
}

_root.button2.onRelease = function() {
	ExternalInterface.call("recieveFromFlash", _root.txtSend.text);
}

_root.button3.onRelease = function() {
	var txt = _root.txtSend.text.split('"').join('\\"')
	getURL('javascript:recieveFromFlash("'+txt+'");');
}

var message = null

_root.onEnterFrame = function() {
	if (message!==null)	{
		_root.txtRecieve.text = message
		message = null
	}
}

Скачать .fla - исходник флешки.

Код на JavaScript

function recieveFromFlash(Txt) {
        document.getElementById('txtRecieve').value = Txt;
}

function getMovie() {
        var M$ =  navigator.appName.indexOf("Microsoft")!=-1
        return (M$ ? window : document)["BridgeMovie"]
}


function sendSetVariable() {
        var value = document.getElementById('txtSend').value

        var movie = getMovie()
        movie.SetVariable("message", value)
}


function sendExternalInterface() {
        var value = document.getElementById('txtSend').value

        var movie = getMovie()

        movie.sendFromJS(value);
}

/* movie name_DoFSCommand */
function BridgeMovie_DoFSCommand(command, args) {
        window[command].call(null, args)
}


// Hook for Internet Explorer.
if (navigator.appName && navigator.appName.indexOf("Microsoft") != -1 && 
navigator.userAgent.indexOf("Windows") != -1) {
        document.write('<script language=\"VBScript\"\>\n');
        document.write('On Error Resume Next\n');
        document.write('Sub BridgeMovie_FSCommand(ByVal command, ByVal args)\n');
        document.write('        Call BridgeMovie_DoFSCommand(command, args)\n');
        document.write('End Sub\n');
        document.write('</script\>\n');
}

Примеры на отдельных страничках

По просьбам читателей - примеры и исходники вынесены на отдельную страницу.

Успешной интеграции!