为了保障原创作者在本站发表文章的利益, 并维护本站原创的精神, 特声明: RIAShanghai对有以下任何情况之一的文章将不通知作者并直接进行快意删除:
- 非原创, 或者原创但一文多发;
- 各种形式的广告与吹擂;
- 不符合本站文章格式.
欢迎各位读者监督. 谢谢合作. 另: 作为Adobe正式的UG, 我们将把Adobe不定期分发的软件,书籍及各种纪念品赠送给发文活跃的作者, 共同进步.
前阵子看到WordPress出了个Flash的Tag Cloud的插件。感觉挺独具匠心的,3D圆球。 ![]()
我的“山寨”心,继上次模仿Buzzword Context Menu后,又再次蠢蠢欲动。
这次的模仿,涉及到3D算法。我的数学丢下好久不用。
所以作者投机取巧了,在网上参考了ahab's math tutorial,一个3D数学教程(作者还在进一步的学习中)。
并且利用了反编译。特此声明和提倡,反编译要用于学习,而非商业和非法用途。随后的源代码,将隐去一些敏感的部分。
先送上原型和模仿后的成品的截图,以作比较。
图一:原图,可去插件原作者网站查看。
图二:模仿后成品图
这次模仿,练习了利用sprite 和 movieclip制作自定义部件。sprite是一种特殊的movie clip, 和movie clip的区别就是,sprite没有帧的概念,而movie clip有。相应的,有关时间轴的一些方法,在movie clip的类中才能找到。
我们分析这个Tag Cloud插件后,会得出结论,这个插件是由tag和cloud两层来构成的(-_-||不用看图,单从字面上都可以看出来)。tag就是sprite,不用是什么动画,简单的一个 text field就可以了。动画的效果,在cloud层来实现。那么cloud就是movie clip, 一帧一帧的,是动画。
那么,我们的实现步骤就一目了然了。由简单到复杂。
第一步:建立tag sprite
package com.chestnut
{
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.text.TextFieldAutoSize;
import flash.text.TextFormat;
//扩展sprite类
public class Tag extends Sprite
{
//这里使用嵌入式字体,在字体变换大小时,能让flash变得流畅些
[Embed(source="C:/WINDOWS/Fonts/ARIALBD.TTF", fontFamily="Arial", fontWeight="Bold")]
private var _arial_str:String;
//3D空间坐标
private var _tagX:Number = 0;
private var _tagY:Number = 0;
private var _tagZ:Number = 0;
//tag文字部分
private var _textField:TextField;
private var _active:Boolean;
private var _color:Number;
private var _highlightColor:Number;
//tag背景部分
private var _back:Sprite;
private var _text:String;
public function Tag(text:String, color:Number, highlightColor:Number)
{
_text = text;
_color = color;
_highlightColor = highlightColor;
_active = false;
//create a new text field,建立一个新的text field
_textField = new TextField();
_textField.autoSize = TextFieldAutoSize.LEFT;
_textField.selectable = false;
//apply a text format to the text field,设置文字的样式
var textFormat:TextFormat = new TextFormat();
textFormat.font = "Arial";
textFormat.bold = true;
textFormat.color = color;
textFormat.size = 16;
_textField.defaultTextFormat = textFormat;
//如果不用嵌入式字体,这里一定要用false, 否则不出字
_textField.embedFonts = true;
_textField.text = text;
addChild(_textField);
//set text field position
_textField.x = (-this.width) / 2;
_textField.y = (-this.height) / 2;
//set background,设置标签背景,这里是画了一个方框
_back = new Sprite();
_back.graphics.beginFill(_highlightColor, 0);
_back.graphics.lineStyle(0, _highlightColor);
_back.graphics.drawRect(0, 0, _textField.textWidth + 20, _textField.textHeight + 5);
_back.graphics.endFill();
addChildAt(_back, 0);
_back.x = -_textField.textWidth / 2 - 10;
_back.y = -_textField.textHeight / 2 - 2;
_back.visible = false;
this.buttonMode = true;
//定义相应的鼠标事件
addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
return;
}
private function mouseOutHandler(event:MouseEvent):void
{
_back.visible = false;
_textField.textColor = _color;
_active = false;
return;
}
private function mouseOverHandler(event:MouseEvent):void
{
_back.visible = true;
_textField.textColor = _highlightColor;
_active = true;
return;
}
/**
* 原作者的代码是当点击某个标签后,将去含有标签内容的blog,这次是练习,故此省略。
*/
private function mouseUpHandler(event:MouseEvent):void
{
return;
}
private function getNumberFromString(text:String):Number
{
return Number(text.match(/(\d|\.|\,)/g).join("").split(",").join("."));
}
//get set functions
public function set tagX(value:Number):void
{
_tagX = value;
}
public function get tagX():Number
{
return _tagX;
}
public function set tagY(value:Number):void
{
_tagY = value;
}
public function get tagY():Number
{
return _tagY;
}
public function set tagZ(value:Number):void
{
_tagZ = value;
}
public function get tagZ():Number
{
return _tagZ;
}
public function set active(value:Boolean):void
{
_active = value;
}
public function get active():Boolean
{
return _active;
}
}
}第二步:建立tag cloud movie clip
tag cloud调用tag sprite的实例,在时间轴上,让许多tag按照球形的轨迹一帧一帧的变化。当然,tag的变化有位置上的,有颜色上的,还有大小。
那个movie clip类的重点就是Event.ENTER_FRAME事件的监听方程。
WordPress插件的工作原理是,tag cloud会读取WordPress生成的一个tag的xml文件,tagcloud.xml。在xml文件中,可以写入字体的大小,颜色等。当tag cloud被调用时,flash会读取xml文件,对tag的样式进行相应的设置。我们看到的原图,字体的大小,和颜色比模仿出来的要多变些。练习中,文字的大小和颜色都写死了。
下面的代码,就省略了读取xml文件的部分。
package com.chestnut
{
import flash.display.MovieClip;
import flash.display.Stage;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
public class TagCloud extends MovieClip
{
private var active:Boolean;
private var highlightColor:Number;
private var mcList:Array;
public var tagText:TextField;
private var d:Number;
//3D位移的变量
private var sa:Number;
private var sc:Number;
private var cb:Number;
private var ca:Number;
private var sb:Number;
private var cc:Number;
//旋转速度
private var tspeed:Number;
private var tcolor:Number;
private var tcolor2:Number;
//球半径
private var radius:Number;
private var lasta:Number;
private var lastb:Number;
//tag的载体,新的一层,方便管理
private var holder:MovieClip;
private var distr:Boolean;
//translating from degrees to radians
private var dtr:Number;
public function TagCloud()
{
var array:Array;
tcolor = 0x71DCDE;
tcolor2 = 10048768;
highlightColor = 1;
tspeed = 2;
distr = true;
}
/**
* 如果标签少,不适用此方法也可以。此方法,可以放置标签过多时,相互的叠压,而看不到想看的标签。
*/
private function depthSort():void
{
var indexTmp:Number = 0;
var index:Number = 0;
mcList.sortOn("tagZ", Array.DESCENDING | Array.NUMERIC);
while(index < mcList.length)
{
holder.setChildIndex(mcList[index], index);
if(mcList[index].active)
{
indexTmp = index;
}
index++;
}
holder.setChildIndex(mcList[indexTmp], (mcList.length - 1));
return;
}
/**
* 标签云的初始化,把关键字随意的放置成球形。
*/
public function init():void
{
var swfStage:Stage = this.stage;
swfStage.scaleMode = StageScaleMode.NO_SCALE;
swfStage.align = StageAlign.TOP_LEFT;
radius = 120;
dtr = Math.PI / 180;
d = 500;
//设置3D位移的系数
sineCosine(0, 0, 0);
//初始化tag array
mcList = [];
active = false;
//写死关键字
var tagArray:Array = ["Chestnut@riashanghai", "marked", "return", "HCC", "here", "Netherlands",
"Ever", "dawn", "computer", "enthusiasts", "gather", "Utrecht",
"year", "admire", "models", "stuff", "discount",
"age", "internet", "prices", "being", "web", "shops",
"blogs", "detailing", "gadget", "long", "before", "shops",
"formula", "lost", "appeal", "found", "interesting", "about",
"event", "however", "browse", "through", "refurbished", "stuff"];
var tag:Tag;
lasta = 1;
lastb = 1;
holder = new MovieClip();
addChild(holder);
resizeHolder();
//每个关键字都转化成tag
for each(var str:String in tagArray)
{
tag = new Tag(str, getColorFromGradient(1), getColorFromGradient(highlightColor));
holder.addChild(tag);
mcList.push(tag);
}
//把tag放到初始化的位置
positionAll();
//加入帧的监听和鼠标事件监听
addEventListener(Event.ENTER_FRAME, updateTags);
stage.addEventListener(Event.MOUSE_LEAVE, mouseExitHandler);
stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
stage.addEventListener(Event.RESIZE, resizeHandler);
}
/**
* 计算颜色的算法 */
private function getColorFromGradient(value:Number):Number
{
...
}
/**
* 每一帧要执行的方程,关键所在。根据鼠标的位置所在,进行相应的旋转。
*/
private function updateTags(event:Event):void
{
var rx:Number = 0;
var ry:Number = 0;
var rz:Number = 0;
var index:Number = 0;
var ans6:Number = 0;
var ans7:Number = 0;
var ans8:Number = 0;
var ans9:Number = 0;
var ans10:Number = 0;
var newX:Number = 0;
var newY:Number = 0;
var newZ:Number = 0;
var newScale:Number = 0;
//根据鼠标位置,来计算要移动的角度
if(active)
{
...
}else
{
...
}
lasta = rx;
lastb = ry;
if(Math.abs(rx) > 0.01 || Math.abs(ry) > 0.01)
{
rz = 0;
sineCosine(rx, ry, rz);
index = 0;
while(index < mcList.length)
{
//计算要移动到的新位置
ans6 = mcList[index].tagX;
...
//设置标签到新的位置
mcList[index].tagX = newX;
...
//设置透视效果
newScale = d / (d + newZ);
...
//设置透明度
mcList[index].alpha = newScale * 0.5;
index++;
}
depthSort();
}
return;
}
/**
* 调整holder层的大小
*/
private function resizeHolder():void
{
var _scaleX:Number;
holder.x = stage.stageWidth * 0.5;
holder.y = stage.stageHeight * 0.5;
if(stage.stageWidth > stage.stageHeight)
{
_scaleX = stage.stageHeight * 0.002;
}
else
{
_scaleX = stage.stageWidth * 0.002;
}
holder.scaleY = _scaleX;
holder.scaleY = _scaleX;
return;
}
private function mouseMoveHandler(event:MouseEvent):void
{
active = true;
return;
}
/**
* 把tag随机的放置成球形的形状
*/
private function positionAll():void
{
var phi:Number = 0;
var theta:Number = 0;
var max:Number = 0;
var i:Number = 0;
max = mcList.length;
mcList.sort(function(){
return Math.random() < 0.5 ? (1) : (-1);
});
//设置每个tag的位置
while (i < max)
{
...
}
return;
}
/**
* Flash likes radians, but it is much easier to work with degrees.
* If you want to rotate a point by one degree you simply increment a variable by one,
* easily done with the "++" operator.
* However if you wanted to rotate a point by the equivalent of one degree in radians,
* you would need to increment an angle by 0.0174532925199433.
* The variables a, b, and c are the angle increments at which the points are going to be rotating.
* I set them to be zero initially.
* 我从ahab's math tutorial找到的解释。就是说3D空间的点的移动,用角度操作要比弧度容易些。
* a b c 就是一个点要在x y z上要移动的角度。这里得到角度的cos sin值,做位移矩阵乘法时待用。
*/
private function sineCosine(a:Number, b:Number, c:Number):void
{
sa = Math.sin(a * dtr);
ca = Math.cos(a * dtr);
sb = Math.sin(b * dtr);
cb = Math.cos(b * dtr);
sc = Math.sin(c * dtr);
cc = Math.cos(c * dtr);
return;
}
private function mouseExitHandler(event:Event):void
{
active = false;
return;
}
private function resizeHandler(event:Event) : void
{
resizeHolder();
return;
}
}
}第三步:在主程序中,调用tag cloud类的实例就可以了。
备注:没有在本社区发表过文章的用户,请不要找作者所要源代码。汲取信息的同时,也需要您的付出。