项目地址:as3timeline
本意是想做个表现录像回放的时间轴.API按照Flash IDE的JSFL来设计的.所以如果大胆幻想一下,也许可以做出个Flash的Flash动画编辑工具.
本教程讲解窗口(图片)拖动特效的制作.
主要是利用drawTriangles绘制变形位图,用TweenLite实现移动过程的缓动.
先看下最终的效果(鼠标点击拖动面板):
大体的思路是:把图片矩形分段,把矩形的运动分解为各个顶点的运动,结合各个顶点利用drawTriangles绘制变形位图,最后用TweenLite加入缓动效果.
接下来逐步讲解:
如果图片位置是pos,要分的段数是segments,取得分段后的顶点,我们通过buildSegments方法来实现.具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/** * 对面板分段, 返回顶点(Point)的二维数组 */ private function buildSegments(pos : Point) : Array { var pts : Array = new Array(); // 分段后平均每段的宽/高 var unitW : Number = this.panel.width / segments; var unitH : Number = this.panel.height / segments; // 遍历, 把分段后的顶点存放在pts数组(二维)中. for (var col : int = 0; col < segments + 1; col++) { pts[col] = new Array(); for (var row : int = 0; row < segments + 1; row++) { pts[col][row] = new Point(pos.x + col * unitW, pos.y + row * unitH); } } return pts; } |
通过buildSegments方法,取得分段后的顶点,并以二维数组([列,行])的形式保存下来.
drawTriangles是FP 10后Graphics增加的方法.可用来绘制三角形,并进行贴图.它提供三个参数
把面板分段后,循环绘制出每一个网格矩形.每一个矩形有四个顶点(tl,tr,br,bl),切分成两个三角形来绘制:
绘制(render)方法具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
/** * 绘制面板 */ public function render() : void { var g : Graphics = this.graphics; g.clear(); var tl : Point, tr : Point, bl : Point, br : Point; // 使用drawTriangles绘制分段矩形 var vertices : Vector. = new Vector.(); var indices : Vector. = new Vector.(); var indiceIndex : int; var uvData : Vector. = new Vector.(); var seg : Number = segments; for (var col : int = 0; col < segments; col++) { for (var row : int = 0; row < segments; row++) { // 单个矩形的四个顶点 tl = pts[col][row]; tr = pts[col + 1][row]; br = pts[col + 1][row + 1]; bl = pts[col][row + 1]; vertices.push(tl.x, tl.y, tr.x, tr.y, br.x, br.y, bl.x, bl.y); var nextU : Number = (col != segments - 1) ? (col + 1) / seg : (1 + 1 / this.panel.width); var nextV : Number = (row != segments - 1) ? (row + 1) / seg : (1 + 1 / this.panel.height); uvData.push(col / seg, row / seg, nextU, row / seg, nextU, nextV, col / seg, nextV); indices.push(indiceIndex, indiceIndex + 1, indiceIndex + 3, indiceIndex + 1, indiceIndex + 2, indiceIndex + 3); indiceIndex += 4; } } g.beginBitmapFill(new IMAGE().bitmapData); g.drawTriangles(vertices, indices, uvData); g.endFill(); } |
这里有个问题,我在实际书写drawTriangles的uvData并测试最终效果时,发现右边缘与下边缘都是少一个像素的.所以在遍历到边缘(col == segments – 1 || row == segments – 1)时, 把这一个像素转成百分比(1/panel.width, 1/panel.height)加到uv中.
鼠标拖动的实现主要是侦听处理鼠标的三个事件:MOUSE_DOWN,MOUSE_UP,MOUSE_MOVE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
/** * 侦听鼠标交互 */ private function buildInteractor() : void { this.stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseEventHandler); this.stage.addEventListener(MouseEvent.MOUSE_UP, mouseEventHandler); } /** * 处理鼠标事件,实现拖动 */ private function mouseEventHandler(event : MouseEvent) : void { switch(event.type) { case MouseEvent.MOUSE_DOWN: this.mouseOffset = new Point(this.mouseX - this.posPt.x, this.mouseY - this.posPt.y); // 开始拖动 this.stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseEventHandler); break; case MouseEvent.MOUSE_UP: // 结束拖动 this.stage.removeEventListener(MouseEvent.MOUSE_MOVE, mouseEventHandler); break; case MouseEvent.MOUSE_MOVE: this.move(this.mouseX - this.mouseOffset.x, this.mouseY - this.mouseOffset.y); break; default: } } |
在鼠标移动时,调用move方法传递目标位置,移动面板.看下一步.
面板移动时,我们先把各个顶点的目标位置通过buildSegments计算出来,然后遍历对每个顶点添加缓动.每个顶点的移动时间需要不一样(如果一样的话,最终效果还是整个面板在平移),这里顶点的移动时间根据它与鼠标的距离来计算得出.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
/** * 实现移动动过程缓动 */ private function move(tx : Number, ty : Number) : void { this.endPt = new Point(tx, ty); // 计算各个分段顶点的目标位置 var endPts : Array = this.buildSegments(this.endPt); // 创建缓动引擎 this.engine = new TimelineLite({onUpdate:this.render, onComplete:this.onMoveComplete}); // 遍历, 把分段后的顶点存放在pts数组(二维)中. for (var col : int = 0; col < segments + 1; col++) { for (var row : int = 0; row < segments + 1; row++) { var end : Point = endPts[col][row]; var time : Number = Math.sqrt((end.x - mouseX) * (end.x - mouseX) + (end.y - mouseY) * (end.y - mouseY)) / 400; this.engine.insert(new TweenLite(this.pts[col][row], time, {x:end.x, y:end.y})); } } // 播放缓动 this.engine.play(); } |
注意代码在TimelineLite.onUpdate时执行render重新绘制面板.
源码打包,包里不含有TweenLite库.
好了,初步完成,还有很多优化的空间,下回继续.
FlashFirebug 3.0已经推出!
新版本重写显示对象树面板,整合了FlashInspector,也修复了大量的bug。
除此之外,FlashFirebug 3.0增加了专业版,专业版多出的功能是控制台(Console),通过控制台你可以运行AS3代码。我想对绝大多数用户来说非专业版的功能已经够用了。
更多信息请访问:http://www.o-minds.com/products/flashfirebug
HtmlText目的是方便对TextField.htmlText字符串进行分割,如下图:
可以直接利用TextField.text来的字符索引来定位分割,但返回的字符串仍保留html的所有标签。使用方法如下:
var html:HtmlText = new HtmlText('Hello <b>world</b>!');
//输出<b>world</b>,而不是<b>wo
trace(html.slice(5, 10));
一个简单的应用:HtmlText实现支持html的打字效果:
关于在位图上像绘制直线的算法,可以参见:http://free.pages.at/easyfilter/bresenham.html。但是不同于在位图上绘制直线,需要的是:一条直线经过哪些格子。
源码:
/**
* 返回网格中两个点,连线经过的格子。
* @see http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
*/
public static function determineTouchedTiles(p0:Point, p1:Point):Vector.
{
var touched:Vector.=new Vector.();
var x0:Number=p0.x;
var y0:Number=p0.y;
var x1:Number=p1.x;
var y1:Number=p1.y;
var steep:Boolean=Math.abs(y1 - y0) > Math.abs(x1 - x0);
if (steep)
{
x0=p0.y;
y0=p0.x;
x1=p1.y;
y1=p1.x;
}
if (x0 > x1)
{
var x0_old:Number=x0;
var y0_old:Number=y0;
x0=x1;
x1=x0_old;
y0=y1;
y1=y0_old;
}
var ratio:Number=Math.abs((y1 - y0) / (x1 - x0));
var mirror:int=y1 > y0 ? 1 : -1;
for (var col:int= Math.floor(x0); col < Math.ceil(x1); col++)
{
var currY:Number=y0 + mirror * ratio * (col - x0);
//第一格不进行延边计算
var skip:Boolean = false;
if(col == Math.floor(x0)){
skip = (int(currY) != int(y0));
}
if(!skip){
if (!steep)
{
touched.push(new Point(col, Math.floor(currY)));
}
else
{
touched.push(new Point(Math.floor(currY), col));
}
}
//根据斜率计算是否有跨格。
if ((mirror > 0 ? (Math.ceil(currY) - currY) : (currY - Math.floor(currY))) < ratio)
{
var crossY:int = Math.floor(currY) + mirror;
//判断是否超出范围
if(crossY>Math.max(int(y0), int(y1)) || crossY
//跨线格子
if (!steep)
{
touched.push(new Point(col, crossY));
}
else
{
touched.push(new Point(crossY, col));
}
}
}
return touched;
}
FlashInspector与FlashFirebug(2.0)和Flash Builder的概要分析工具(Profiler)都会设置mm.cfg的PreloadSWF参数, 所以会产生冲突, 导致Flash Builer的Profiler失效. 如果遇到这种情况, 可以先禁用FlashInspector和FlashFirebug(2.0), 或者为Firefox建个新的Profile.
Flash Inspector更新至0.2.3. 加入了我最期待的一个功能:与FlashFirebug整合. FlashFirebug是对Flash开发很有用的一个调试工具. 但是使用它必须在swf里面先导入FlashFirebug的一些类. 通过Flash Inspector就不必这么麻烦了.
Flash Inspector采用了插件机制, 对FlashFirebug的支持, 其实就是编写一个插件负责与FlashFirebug交互. 要使用这个插件需要Firefox安装:Firebug(对于使用Firefox的前端开发者这应该是必备了吧?), FlashFirebug, Flash Inspector.
安装之后, 在状态栏上右击Flash Inspector的图标, 勾选”FlashFirebug”. 刷新一下页面.
不过FlashFirebug本身是针对自主项目的, 要求swf必须是”allowScriptAccess”的. 所以网页的swf不满足这个条件就没办法使用Flash Inspector的这个功能. Flash Inspector会尝试10次连接Flash Firebug, 如果连接失败, 会在左上角的工具栏中显示一个tip.
这次更新也加了对Firefox 4的支持.
FlashInspector 0.2.2,更新主要包括:
最后,希望FlashInspector对你有用。
Transform3DTool 1.0完成,目标:对3D显示对象(fp 10)进行变形操作。有四个Tool组成:RotationTool、ScaleTool、TranslationTool、 GlobalTranslationTool,各个Tool都可以分开,单独在代码中使用。源码:Transform3DTool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//初始化 var tool3d:Transform3DTool = new Transform3DTool(); addChild(tool3d); //设置各个tool的鼠标样式 tool3d.rotationTool.xCursor = new XControlCursor(); tool3d.rotationTool.yCursor = new YControlCursor(); tool3d.rotationTool.zCursor = new ZControlCursor(); tool3d.rotationTool.regCursor = new RegistrationControlCursor(); tool3d.rotationTool.pCursor = new PControlCursor(); tool3d.translationTool.xCursor = new XControlCursor(); tool3d.translationTool.yCursor = new YControlCursor(); tool3d.translationTool.zCursor = new ZControlCursor(); tool3d.translationTool.regCursor = new RegistrationControlCursor(); tool3d.scaleTool.regCursor = new RegistrationControlCursor(); tool3d.globalTranslationTool.cursor = new GlobalTranslationCursor(); //选择使用"scale"和"global translation"两个tool tool3d.selectTool("scale"); tool3d.selectTool(TransformToolMode.GLOBAL_TRANSLATION); //设置目标. tool3d.target = test.mc1.mc2; |
提供的是MXP的组件安装文件,下载:SenseFlipClock(mxp)
除了组件参数面板中的那些参数,SenseFlipClock还提供以下属性、方法:
注意,如果你想手动调用flip来翻页,必须先把flipOn设置为custom。
希望SenseFlipClock对您有用
: )