用TweenLite制作窗口拖动特效(1)

本教程讲解窗口(图片)拖动特效的制作.
主要是利用drawTriangles绘制变形位图,用TweenLite实现移动过程的缓动.

先看下最终的效果(鼠标点击拖动面板):
[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://www.itamt.com/wp-content/uploads/2011/10/Step4.swf" width="550" height="400" targetclass="flashmovie"]
Get Adobe Flash player
[/kml_flashembed]

大体的思路是:把图片矩形分段,把矩形的运动分解为各个顶点的运动,结合各个顶点利用drawTriangles绘制变形位图,最后用TweenLite加入缓动效果.
null
接下来逐步讲解:

1.图片矩形的分段

如果图片位置是pos,要分的段数是segments,取得分段后的顶点,我们通过buildSegments方法来实现.具体如下:

		/**
		 * 对面板分段, 返回顶点(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方法,取得分段后的顶点,并以二维数组([列,行])的形式保存下来.

2.drawTriangles绘制图片

drawTriangles是FP 10后Graphics增加的方法.可用来绘制三角形,并进行贴图.它提供三个参数

  • vertices(Vector.<Point>),三角形的顶点,顶点的数据形式不是Point而是x,y的数值(所以长度必定是6的倍数).
  • indices(Vector.<int>),三角形的边,根据顶点在数组中的索引,两个索引表示一条边.
  • uvData(Vector.<Number>),三角形顶点对应的贴图比例.

把面板分段后,循环绘制出每一个网格矩形.每一个矩形有四个顶点(tl,tr,br,bl),切分成两个三角形来绘制:
null
绘制(render)方法具体如下:

		/**
		 * 绘制面板
		 */
		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中.

3.实现鼠标拖动

鼠标拖动的实现主要是侦听处理鼠标的三个事件:MOUSE_DOWN,MOUSE_UP,MOUSE_MOVE

		/**
		 * 侦听鼠标交互
		 */
		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方法传递目标位置,移动面板.看下一步.

4.移动面板,实现缓动

面板移动时,我们先把各个顶点的目标位置通过buildSegments计算出来,然后遍历对每个顶点添加缓动.每个顶点的移动时间需要不一样(如果一样的话,最终效果还是整个面板在平移),这里顶点的移动时间根据它与鼠标的距离来计算得出.

		/**
		 * 实现移动动过程缓动
		 */
		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

FlashFirebug 3.0已经推出!
新版本重写显示对象树面板,整合了FlashInspector,也修复了大量的bug。
除此之外,FlashFirebug 3.0增加了专业版,专业版多出的功能是控制台(Console),通过控制台你可以运行AS3代码。我想对绝大多数用户来说非专业版的功能已经够用了。
更多信息请访问:http://www.o-minds.com/products/flashfirebug

HtmlText及打字效果

HtmlText目的是方便对TextField.htmlText字符串进行分割,如下图:

可以直接利用TextField.text来的字符索引来定位分割,但返回的字符串仍保留html的所有标签。使用方法如下:

var html:HtmlText = new HtmlText('Hello &lt;b&gt;world&lt;/b&gt;!');
//输出&lt;b&gt;world&lt;/b&gt;,而不是&lt;b&gt;wo
trace(html.slice(5, 10));

一个简单的应用:HtmlText实现支持html的打字效果:

[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://www.itamt.com/wp-content/uploads/2011/06/Demo.swf" width="590" height="242" targetclass="flashmovie"]Get Adobe Flash player

[/kml_flashembed]
源码下载:
HtmlText.zip

计算网格中直线经过的格子

关于在位图上像绘制直线的算法,可以参见:http://free.pages.at/easyfilter/bresenham.html。但是不同于在位图上绘制直线,需要的是:一条直线经过哪些格子。

  1. 假设有p0,p1两个点,位置如下图:
  2. 我们很容易得到连线的方程。首先我们按照在x上取整递增,很容易计算出x=1, 2, 3, 4…时y的值是多少。
  3. 然后对y值取整,所得到(x1,y1),(x2,y2),(x3,y3)…肯定是连线过的格子。
  4. 但是我们遗漏了这种情况,左上格也是连线经过的格子,但由于我们只在x轴上进行取整递增运算,没有被考虑在内。
  5. 在(2)时已经可以知道A点坐标,我们可以斜率来判断连线是否经过上面一格。如果“蓝色线的斜率小于p0-p1的斜率”,说明连线经过了上面一格。
  6. 利用这种方法,可以轻松计算出连线经过的所有格子。

AS3实现的demo:
[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://www.itamt.com/wp-content/uploads/2011/04/TestLineCrossPoints.swf" width="550" height="400" targetclass="flashmovie"]Get Adobe Flash player

[/kml_flashembed]

源码:

/**
* 返回网格中两个点,连线经过的格子。
* @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) &gt; Math.abs(x1 - x0);
if (steep)
{
x0=p0.y;
y0=p0.x;
x1=p1.y;
y1=p1.x;
}

if (x0 &gt; 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 &gt; y0 ? 1 : -1;

for (var col:int= Math.floor(x0); col &lt; 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 &gt; 0 ? (Math.ceil(currY) - currY) : (currY - Math.floor(currY))) &lt; ratio)
{
var crossY:int = Math.floor(currY) + mirror;

//判断是否超出范围
if(crossY&gt;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与Flash Builder的概要分析工具冲突

FlashInspector与FlashFirebug(2.0)和Flash Builder的概要分析工具(Profiler)都会设置mm.cfg的PreloadSWF参数, 所以会产生冲突, 导致Flash Builer的Profiler失效. 如果遇到这种情况, 可以先禁用FlashInspector和FlashFirebug(2.0), 或者为Firefox建个新的Profile.

FlashInspector 0.2.3更新

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 0.2.2,更新主要包括:

  • 整合Transform3DTool,这样Flash Inspector就支持对显示对象进行3D变形(transform)了。不过遗憾的是Transform3DToolFlash Player 10下存在严重Bug,在Flash Player 10.1下没问题。这个bug还没着手解决,尚不知道具体原因。总之,如果你不想把FirefoxFlash Player升级到10.1,也就没有必要更新FlashInspector 0.2.2了。
  • 设置Firefoxdom.ipc.plugins.enabled.npswf32.dllfalse,这个属性默认是为true,设置成false是为了防止Flash Player报错时,Firefox整个假死。详见
  • 去除as3swf的使用。as3swf真的很给力,但它体积宠大,而且Flash Inspector用到as3swf的只有SwfInfoView这个插件,解析swf的尺寸、背景色等信息,杀鸡焉用牛刀?为此Flash Inspector编写自身的swf解析类。

最后,希望FlashInspector对你有用。

Transform3DTool 1.0

[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://www.itamt.com/wp-content/uploads/2010/12/Tfm3DToolDemo.swf" width="600" height="450" targetclass="flashmovie"]
Get Adobe Flash player
[/kml_flashembed]

Transform3DTool 1.0完成,目标:对3D显示对象(fp 10)进行变形操作。有四个Tool组成:RotationToolScaleToolTranslationToolGlobalTranslationTool,各个Tool都可以分开,单独在代码中使用。源码:Transform3DTool

	//初始化
	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;

翻页时钟组件(SenseFlipClock)

[kml_flashembed publishmethod="static" fversion="10.0.0" movie="http://www.itamt.com/wp-content/uploads/2010/12/preview.swf" width="590" height="300" targetclass="flashmovie"]
Get Adobe Flash player
[/kml_flashembed]

下载SenseFlipClock

提供的是MXP的组件安装文件,下载:SenseFlipClock(mxp)

自定义时钟皮肤

  1. 新建一个MovieClip,然后先画一个矩形,这会作为时钟的背景;然后在背景矩形上面放一个文本框(动态文本),用来显示时间文本。
  2. MovieClip设置为链接导出,并取一个类名,比如:CustomClockSkin
  3. 设置SenseFlipClockskin参数为CustomClockSkin
  4. 测试影片,现在你应该看到翻页时钟使用的是自定义皮肤。

代码使用

除了组件参数面板中的那些参数,SenseFlipClock还提供以下属性、方法:

  • SenseFlipClock.value: 用于设置值
  • SenseFlipClock.flip(): 翻页,一般是和SenseFlipClock.value配合使用。
  • SenseFlipClock.update(): 立即更新渲染,不建议使用。

注意,如果你想手动调用flip来翻页,必须先把flipOn设置为custom。

最后

希望SenseFlipClock对您有用
: )