Хороший вопрос + 1
Я видел fla на работе, у меня нет дома cs5, но я понимаю, чего вы пытаетесь достичь.
Мои подходы были такими:
- Контуры движения :
Начиная с Flash CS4, вы можете скопировать путь и вставить его в анимацию движения.Это будет работать подобно функции Motion Guide Классического анимации.С этим довольно много проблем:
- Возможно, вам придется вручную вырезать / вставить
- Не все пути могут быть вставлены в движение
- ВыМожно использовать класс AnimationFactory и добавить цель, но проблема в том, что, пока цель анимируется, у вас нет скрипта Actionscript.Вы можете установить таймер на время движения AnimationFactory, но оно становится громоздким.
Очевидно, что это нет-нет.
Использование JSFL для обхода путей внутри IDE :
Я наткнулся на этот очень удобный jsfl скрипт от ericlin , который пересекает все фигуры, выбранные наэтап.Если вы выберете свои пути и запустите скрипт (вы можете просто дважды щелкнуть файл jsfl), вы получите проанализированные координаты.
Я сделал простой тест, используя TweenLite :
import com.greensock.*;
import com.greensock.easing.*;
import com.greensock.plugins.*;
TweenPlugin.activate([BezierPlugin]);
graphics.lineStyle(0.05);
var index:int = 0;
var ball:Sprite = new Sprite();
ball.graphics.beginFill(0x009900,.75);ball.graphics.drawCircle(-2,-2,4);ball.graphics.endFill();
addChild(ball);
drawLines();
function drawLines():void{
var t:Number = .01;
var timeline:TimelineLite = new TimelineLite();
var i:int = index;
for(index; index <= ptArray.length; index += 12){
timeline.append( new TweenLite(ball, t, {x:ptArray[i],y:ptArray[i+1]}) );
timeline.append( new TweenLite(ball, t, {bezier:[{x:ptArray[i+2], y:ptArray[i+3]}, {x:ptArray[i+4], y:ptArray[i+5]}]}) );
this.graphics.moveTo(ptArray[i], ptArray[i+1]);
this.graphics.curveTo(ptArray[i+2], ptArray[i+3], ptArray[i+4], ptArray[i+5]);
i += 6;
timeline.append( new TweenLite(ball, t, {x:ptArray[i],y:ptArray[i+1]}) );
timeline.append( new TweenLite(ball, t, {bezier:[{x:ptArray[i+2], y:ptArray[i+3]}, {x:ptArray[i+4], y:ptArray[i+5]}]}) );
this.graphics.moveTo(ptArray[i], ptArray[i+1]);
this.graphics.curveTo(ptArray[i+2], ptArray[i+3], ptArray[i+4], ptArray[i+5]);
}
}
* Примечание: * ptArray здесь не отображается, поскольку он будет тратить слишком много места. результат не так уж велик.Вы можете взглянуть на fla , чтобы понять, что я имею в виду.Сценарий jsfl мог быть изменен, но я видел, что вы подчеркнули использование actioncript, так что это тоже нет, нет.
Использование AS3SWF для декомпиляции SWF во время выполнения и доступа к фигурам :
Клаус Уолерс разработал удивительную библиотеку as3 под названием as3swf , что позволяет разработчикам flash / flex декомпилировать swfs во время выполнения.Вот потрясающая статья , объясняющая входы и выходы фигур внутри swfs.Уже написано экспортеров .
Я просто продублировал AS3ShapeExporter и изменил команды рисования as3 на код TweenLite.По сути, я заменил moveTo на быструю анимацию движения в положение, lineTo, на обычную анимацию движения, а CurveTo - на более безболезненную анимацию движения.BezierPlugin от Tween Lite, к счастью, использовал квадратичный Безье, точно так же, как это делает CurveTo.
Вот код, который вам нужно будет вставить внутри fla, который содержит формы:
import com.codeazur.as3swf.*;
import com.codeazur.as3swf.tags.*;
import com.codeazur.as3swf.exporters.*;
this.loaderInfo.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(e:Event):void {
var swf:SWF = new SWF(this.loaderInfo.bytes);//new SWF(URLLoader(e.target).data as ByteArray);
var doc:AS3ShapeTweenLiteExporter = new AS3ShapeTweenLiteExporter(swf,"ball",.01);
// Loop over all tags
for (var i:uint = 0; i < swf.tags.length; i++) {
var tag:ITag = swf.tags[i];
// Check if tag is a DefineShape
if (tag is TagDefineShape) {
// Export shape tween
TagDefineShape(tag).export(doc);
}
}
trace(doc.actionScript);
}
В основном я загружаю swfКак только он будет готов, я передаю его байты as3swf и использую AS3ShapeTweenLiteExporter для разбора тегов формы и выкладывания actionScript.3 параметра, которые я передаю конструктору: экземпляр swf, имя цели анимации и время каждой анимации.
Вот как выглядит мой взломанный вместе класс:
package com.codeazur.as3swf.exporters
{
import com.codeazur.as3swf.SWF;
import com.codeazur.utils.StringUtils;
import flash.display.CapsStyle;
import flash.display.InterpolationMethod;
import flash.display.JointStyle;
import flash.display.LineScaleMode;
import flash.display.SpreadMethod;
import flash.geom.Matrix;
import com.codeazur.as3swf.exporters.core.DefaultShapeExporter;
public class AS3ShapeTweenLiteExporter extends DefaultShapeExporter
{
protected var _actionScript:String;
protected var _target:String;
protected var _time:Number;
public function AS3ShapeTweenLiteExporter(swf:SWF,target:String,time:Number) {
super(swf);
_target = target;
_time = time;
}
public function get actionScript():String { return _actionScript; }
override public function beginShape():void {
_actionScript = "import com.greensock.*;\rimport com.greensock.plugins.*;\r\rTweenPlugin.activate([BezierPlugin]);\r\rvar shapeTimeline:TimelineLite = new TimelineLite()\r";
}
override public function beginFills():void {
//_actionScript += "// Fills:\rgraphics.lineStyle();\r";
}
override public function beginLines():void {
//_actionScript += "// Lines:\r";
}
override public function beginFill(color:uint, alpha:Number = 1.0):void {
if (alpha != 1.0) {
_actionScript += StringUtils.printf("graphics.beginFill(0x%06x, %f);\r", color, alpha);
} else {
_actionScript += StringUtils.printf("graphics.beginFill(0x%06x);\r", color);
}
}
override public function beginGradientFill(type:String, colors:Array, alphas:Array, ratios:Array, matrix:Matrix = null, spreadMethod:String = SpreadMethod.PAD, interpolationMethod:String = InterpolationMethod.RGB, focalPointRatio:Number = 0):void {
var asMatrix:String = "null";
if (matrix != null) {
asMatrix = "new Matrix(" +
matrix.a + "," +
matrix.b + "," +
matrix.c + "," +
matrix.d + "," +
matrix.tx + "," +
matrix.ty + ")";
}
var asColors:String = "";
for (var i:uint = 0; i < colors.length; i++) {
asColors += StringUtils.printf("0x%06x", colors[i]);
if (i < colors.length - 1) { asColors += ","; }
}
if (focalPointRatio != 0.0) {
_actionScript += StringUtils.printf("graphics.beginGradientFill('%s', [%s], [%s], [%s], %s, '%s', '%s', %s);\r",
type,
asColors,
alphas.join(","),
ratios.join(","),
asMatrix,
spreadMethod,
interpolationMethod,
focalPointRatio.toString());
} else if (interpolationMethod != InterpolationMethod.RGB) {
_actionScript += StringUtils.printf("graphics.beginGradientFill('%s', [%s], [%s], [%s], %s, '%s', '%s'\r);",
type,
asColors,
alphas.join(","),
ratios.join(","),
asMatrix,
spreadMethod,
interpolationMethod);
} else if (spreadMethod != SpreadMethod.PAD) {
_actionScript += StringUtils.printf("graphics.beginGradientFill('%s', [%s], [%s], [%s], %s, '%s');\r",
type,
asColors,
alphas.join(","),
ratios.join(","),
asMatrix,
spreadMethod);
} else if (matrix != null) {
_actionScript += StringUtils.printf("graphics.beginGradientFill('%s', [%s], [%s], [%s], %s);\r",
type,
asColors,
alphas.join(","),
ratios.join(","),
asMatrix);
} else {
_actionScript += StringUtils.printf("graphics.beginGradientFill('%s', [%s], [%s], [%s]);\r",
type,
asColors,
alphas.join(","),
ratios.join(","));
}
}
override public function beginBitmapFill(bitmapId:uint, matrix:Matrix = null, repeat:Boolean = true, smooth:Boolean = false):void {
var asMatrix:String = "null";
if (matrix != null) {
asMatrix = "new Matrix(" +
matrix.a + "," +
matrix.b + "," +
matrix.c + "," +
matrix.d + "," +
matrix.tx + "," +
matrix.ty + ")";
}
if (smooth) {
_actionScript += StringUtils.printf("// graphics.beginBitmapFill(%d, %s, %s, %s);\r", bitmapId, asMatrix, repeat, smooth);
} else if (!repeat) {
_actionScript += StringUtils.printf("// graphics.beginBitmapFill(%d, %s, %s, %s);\r", bitmapId, asMatrix, repeat);
} else {
_actionScript += StringUtils.printf("// graphics.beginBitmapFill(%d, %s, %s, %s);\r", bitmapId, asMatrix);
}
}
override public function endFill():void {
_actionScript += "graphics.endFill();\r";
}
override public function lineStyle(thickness:Number = NaN, color:uint = 0, alpha:Number = 1.0, pixelHinting:Boolean = false, scaleMode:String = LineScaleMode.NORMAL, startCaps:String = null, endCaps:String = null, joints:String = null, miterLimit:Number = 3):void {
/*
if (miterLimit != 3) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f, %s, %s, %s, %s, %f);\r",
thickness, color, alpha, pixelHinting.toString(),
(scaleMode == null ? "null" : "'" + scaleMode + "'"),
(startCaps == null ? "null" : "'" + startCaps + "'"),
(joints == null ? "null" : "'" + joints + "'"),
miterLimit);
} else if (joints != null && joints != JointStyle.ROUND) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f, %s, %s, %s, %s);\r",
thickness, color, alpha, pixelHinting.toString(),
(scaleMode == null ? "null" : "'" + scaleMode + "'"),
(startCaps == null ? "null" : "'" + startCaps + "'"),
"'" + joints + "'");
} else if(startCaps != null && startCaps != CapsStyle.ROUND) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f, %s, %s, %s);\r",
thickness, color, alpha, pixelHinting.toString(),
(scaleMode == null ? "null" : "'" + scaleMode + "'"),
"'" + startCaps + "'");
} else if(scaleMode != LineScaleMode.NORMAL) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f, %s, %s);\r",
thickness, color, alpha, pixelHinting.toString(),
(scaleMode == null ? "null" : "'" + scaleMode + "'"));
} else if(pixelHinting) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f, %s);\r",
thickness, color, alpha, pixelHinting.toString());
} else if(alpha != 1.0) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x, %f);\r", thickness, color, alpha);
} else if(color != 0) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f, 0x%06x);\r", thickness, color);
} else if(!isNaN(thickness)) {
_actionScript += StringUtils.printf("graphics.lineStyle(%f);\r", thickness);
} else {
_actionScript += "graphics.lineStyle();\r";
}
*/
}
override public function moveTo(x:Number, y:Number):void {
//_actionScript += StringUtils.printf("graphics.moveTo(%f, %f);\r", x, y);
//_actionScript += StringUtils.printf(_target+".x = %f;\r"+_target+".y = %f;\r", x, y);
_actionScript += StringUtils.printf("shapeTimeline.append(new TweenLite("+_target+",0.001,{x:%f,y: %f}));\r", x, y);
}
override public function lineTo(x:Number, y:Number):void {
//_actionScript += StringUtils.printf("graphics.lineTo(%f, %f);\r", x, y);
_actionScript += StringUtils.printf("shapeTimeline.append(new TweenLite("+_target+","+_time+",{x:%f,y: %f}));\r", x, y);
}
override public function curveTo(controlX:Number, controlY:Number, anchorX:Number, anchorY:Number):void {
//_actionScript += StringUtils.printf("graphics.curveTo(%f, %f, %f, %f);\r", controlX, controlY, anchorX, anchorY);
_actionScript += StringUtils.printf("shapeTimeline.append(new TweenLite("+_target+","+_time+",{bezier:[{x:%f,y: %f},{x:%f,y: %f}]}));\r", controlX, controlY, anchorX, anchorY);
}
}
}
После загрузки as3swf вам нужно сохранить этот класс в пакете экспортера.Вот результат .Вы можете скачать его fla , а также fla для этого сгенерированного кода.
Это чистая версия actioncript и имеет достойный результат.
Анимация выглядит прерывисто, потому что она использует одинаковое количество времени для анимации для каждого из отрезков линии.некоторые короче, а другие длиннее.Вы можете сохранить предыдущую позицию и использовать ее с текущей позицией для вычисления расстояния, и на основе этого генерировать приличную для каждого экземпляра TweenLite.Также не стесняйтесь изменять этот класс любым удобным вам способом (скажем, вы хотите использовать вместо него Таймер и т. Д.)
Обновление
У меня было время повозиться с этимеще немного.Я немного изменил экспортер, теперь он также ожидает максимальное расстояние для перемещения целевого объекта.Это будет ширина или высота выделения (все строки), в зависимости от того, какой из них больше (ширина против высоты).Предыдущие значения x и y сохраняютсяи используется для расчета расстояния, то это расстояние делится на максимальное расстояние для перемещения. Это, в свою очередь, используется для масштабирования времени каждой анимации. Кроме того, я установил замедление на Linear, потому что по умолчанию (Quad.easeOut) добавлено к дрожанию. Время не очень точное, но выглядит немного лучше . Обновленные флаги здесь и здесь
Обновленный код временной шкалы:
import com.codeazur.as3swf.*;
import com.codeazur.as3swf.tags.*;
import com.codeazur.as3swf.exporters.*;
this.loaderInfo.addEventListener(Event.COMPLETE, completeHandler);
function completeHandler(e:Event):void {
var swf:SWF = new SWF(this.loaderInfo.bytes);//new SWF(URLLoader(e.target).data as ByteArray);
var doc:AS3ShapeTweenLiteExporter = new AS3ShapeTweenLiteExporter(swf,"ball",1,300);
// Loop over all tags
for (var i:uint = 0; i < swf.tags.length; i++) {
var tag:ITag = swf.tags[i];
// Check if tag is a DefineShape
if (tag is TagDefineShape) {
// Export shape tween
TagDefineShape(tag).export(doc);
}
}
trace(doc.actionScript);
System.setClipboard(doc.actionScript);
}
обновленный экспортер :
Опять не стесняйтесь возиться.
ОБНОВЛЕНИЕ 2:
Хорошо, вот еще один способ ...
- Использование AS3 для анализа FXG, экспортированного прямо из Illustrator :
Начиная с Illustrator CS4, вы можете сохранять графику как FXG через Файл> Сохранить копию> выберите тип файла FXG
Вот файл fxg , который я использовал.
Япония сделала это снова :)
Удивительная Lib Spark содержит FXG Parser . Существует также SVGParser, но сейчас я играл только с fxg.
Итак, первый шаг - скачать библиотеку:
svn export http://www.libspark.org/svn/as3/FxgParser
Вы можете использовать этот пример, поскольку вы используете Flash CS5. Парсер использует TLF для текста. Я не стал загружать весь Flex4 SDK, чтобы получить SWC и настройки. Я только что закомментировал анализатор текста, так как нас интересуют пути. Класс с комментариями находится внизу.
Библиотека содержит анализатор Path , который клонирован и изменен для получения некоторого анимационного кода: PathTween.as
Вы можете узнать некоторые переменные из классов as3swf.
Вот некоторые объяснения некоторых переменных, которые я добавил:
- ID - статический и является счетчиком количества проанализированных путей, это означает, что мы можем анимировать каждый путь отдельно
- CODE - статический, содержит код импорта + облегченный код временной шкалы для каждого экземпляра пути, это THE CODE:)
- MAX_DISTANCE - аналогично подходу as3swf, он используется для изменения времени каждой анимации в зависимости от пройденного расстояния
- ВРЕМЯ - общее время для каждой подростки, которое удобно устанавливать вне класса
- TARGET - имя, которое будет увеличиваться для каждого пути и используется в качестве цели анимации.
- _code, _distance, _x, _y, _timeScale - так же, как в подходе as3swf
- _timeRel - относительное время каждого времени (например, после того, как оно было отрегулировано)
Кроме того, я сделал быстрое исправление, добавил обмотку по умолчанию, поскольку иногда в файле .fxg может отсутствовать атрибут winding , и это нарушает работу парсера.
Для использования вам необходимо внести незначительные изменения в FxgFactory.as , чтобы он использовал анализатор PathTween вместо класса Path по умолчанию.
private static const PARSERS:Array = [ Graphic , Group , Library,
Path , Ellipse, Rect, Line,
BitmapGraphic, BitmapImage,
TextGraphic, RichText ];
становится:
private static const PARSERS:Array = [ Graphic , Group , Library,
PathTweenTracer , Ellipse, Rect, Line,
BitmapGraphic, BitmapImage,
TextGraphic, RichText ];
Наконец, некоторый базовый код временной шкалы, который использует все это:
import fxgparser.*
import fxgparser.parser.*;
var fxgurl:String = "fingerprint.fxg";
var fxgSprite:FxgDisplay;
var loader:URLLoader = new URLLoader( new URLRequest( fxgurl ) );
loader.addEventListener( Event.COMPLETE , displayData );
//some setup
PathTween.MAX_DISTANCE = 360;//change this to fit your shape's largest dimension(width || height)
PathTween.TIME = 2;//change this to your needs
PathTween.TARGET = "ball";//a name of a target clip that will be incremented for each move,line,curve
function displayData( e:Event ):void {
var fxgxml:XML = XML( e.currentTarget.data );
fxgSprite = new FxgDisplay( fxgxml ); //parse SVG
System.setClipboard(PathTween.CODE);
//make some clips for the tester
trace(getClips());
addChild( fxgSprite );
}
function getClips():String {
var result:String = 'this.filters = [new GlowFilter(0x00ff99)]\r';
var clipsNum:int = PathTween.ID;
var target:String = PathTween.TARGET;
for(var i:int = 0 ; i < clipsNum ; i++)
result += 'var '+(target+i)+':Sprite = new Sprite();\r'+(target+i)+'.graphics.beginFill(0x00ff00);\r'+(target+i)+'.graphics.drawCircle(-2,-2,4);\r'+(target+i)+'.graphics.endFill();\raddChild('+(target+i)+');\r';
return result;
}
Это довольно просто:
- установить константы в PathTween
- загрузить FXG
- как только он будет загружен, нарисуйте его. Во время рисования код генерируется в фоновом режиме
- как только он будет нарисован, поместите код в буфер обмена. Для моего пути у меня было около 11K сгенерированных строк, поэтому трассировка здесь не очень хорошая идея
- также оставьте только код анимации в PathTween, я генерирую некоторый код ( getClips () ) для создания целевых мувиклипов здесь. Не стесняйтесь добавлять такую функциональность в PathTween по мере необходимости.
Затем я открыл новый файл fla и:
- вставил клип, который уже был в буфере обмена (несколько тысяч строк кода)
- скопировал код с панели вывода и вставил его после "TweenPlugin.activate ([BezierPlugin]);"
Вы можете увидеть результат и получить fla .
Пока что as3swf - это круто, когда у вас есть готовые флаги с вставленными путями иллюстратора,
может быть быстрее, так как as3swf работает с байтами.
Что мне нравится в подходе FXG:
- вы пропустите шаг, где вы вставляете
графика в FLA, вы просто сохраняете
скопировать как FXG. Вы можете использовать один флаг для
сгенерируйте весь код, который вам нужен, просто
измените путь к файлу fxg
хочу оживить.
- каждый путь анализируется индивидуально, так что это немного более гибко.
- хотя он генерирует больше кода, чем версия as3swf, кубические Безье, дуги
и другие кривые разбиты на команды lineTo, которые немного выравнивают анимацию.
Это на самом деле стало весело, с отдельными временными рамками, поэтому я сделал еще одну копию, которая рисует некоторые
сырные следы в растровые данные.
Здесь - класс PathTweenTracer, как и предыдущий, поместите его в пакет анализатора.
Опять же, константа PARSERS должна быть обновлена внутри FxgFactory:
private static const PARSERS:Array = [ Graphic , Group , Library,
PathTweenTracer , Ellipse, Rect, Line,
BitmapGraphic, BitmapImage,
TextGraphic, RichText ];
Код временной шкалы почти такой же.
Результат выглядит красиво ( source )
Вот несколько скриншотов сгенерированной анимации:
Закомментировано TextGraphic.as
Подход FXG лучше подошел бы к этому вопросу («Используя чистый ActionScript, как я могу переместить символ, следующий за каждой из строк, без использования предварительно определенных направляющих движения?»)
Что касается вложенного вопроса («Доступны ли эти графические объекты через AS3, или я должен преобразовать их в символы / любой необходимый формат?»):
Как уже упоминалось @Casey, вы не можете получить доступ к графике, когда она установлена. Используя обновленный Graphics API, вы можете копировать графику из одного экземпляра Graphics в другой, но это не раскрывает команды. Я помню Тинк имел что-то намного раньше, чем Flash Player 10, но я не знаю, как продвигается в этом направлении.
НТН