flash动画
引言
严格来说,这不是一篇教程,这篇文章的旨在目的是向还没有接触过Vector类的闪友介绍、推荐Vector类,利用它的方便,简洁以及它的强大功能作出更加完美的动画,特别是模仿3D的动画,说到Vector类,必须说到一个人——Robert Penner,是他在《Flash Mx编程与创意实现》一书中为它赋予了生命与灵魂(我是这么认为的)(先声明这不是做广告)。这本书在几年前就出版了,应该很多闪友已经看过了,我是在几个月前才知道和看到的(有点井底之蛙,可别取笑我),当我看到其中的Vector类时,眼前确实一亮,我是一名高中物理教师,学过高中物理的闪友都知道矢量在高中物理中的份量,所以我花了大概两天时间,把其中的矢量类部分认真输入电脑几遍,并好好体会了它的实现方法,同时试做了几个常见的动画,等一下我会详细介绍,我会把我输的书本的部分代码贴出来,供共同研究,由于我也是新手,有许多可能是自以为是的地方,希望读者谅解,可别扔东西哦。
正文
读者可以先看第一个例子:http://www.flash8.net/fla/4822.shtml,怎样,还是比较逼真吧。让我们先看一下它的代码:
Vector = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
//构造一个矢量类Vector,它包含3个属性x、y、z
Vector.prototype.rotateXYZ = function(a, b, c) {
var sa = Math.sin(a*Math.PI/180), ca = Math.cos(a*Math.PI/180);
var sb = Math.sin(b*Math.PI/180), cb = Math.cos(b*Math.PI/180);
var sc = Math.sin(c*Math.PI/180), cc = Math.cos(c*Math.PI/180);
with (this) {
//绕x轴
var ry = y*ca-z*sa;
var rz = y*sa+z*ca;
//绕y轴
var rx = rz*sb+x*cb;
z = rz*cb-x*sb;
//绕z轴
x = rx*cc-ry*sc;
y = rx*sc+ry*cc;
}
};
/*类Vector的方法,该方法是使矢量绕x、y、z轴分别以a、b、c的
角度增量旋转,作3D动画必需的方法,可以参考数学矢量的旋转*/
Vector.prototype.getSee = function(ViewDist) {
if (ViewDist == undefined) {
ViewDist = 300;
}
return ViewDist/(ViewDist+this.z);
};
//获得矢量透视度的方法,返回值是一个随z的增大而减小的值,可参看透视学
Vector.prototype.cast = function(p) {
if (p == undefined) {
p = this.getSee();
}
with (this) {
return new constructor(x*p, y*p, 0);
}
};
//根据透视原理,把矢量投影到x、y平面上,flash没有真正的3D
//以上是现成的,和我无关,也就是说,完成刚才的动画效果,我只需写下面几句代码就可以了
Point = new Vector(10, 0, 100);
//创建一个新对象,用来对“地球”的初始坐标定位
this.createEmptyMovieClip("mc1", 2000);
//创建一个空的电影剪辑
mc1.attachMovie("mc2", "mc3", 0);
//在空的电影剪辑中插入mc3,也就是“太阳”,深度为2000
onEnterFrame = function () {
mc._x = P.x*2.5;
mc._y = P.y*2.5;
//定位mc(地球)的初始坐标
this.mc.swapDepths(2000-Math.round(Point.z*10));
/*设置mc的深度,看得出,如果“太阳”在原点,从立体坐标看
,z<0在太阳背面,深度应该大于2000,反之小于2000*/
Point.rotateXYZ(0.5, 4, 0.1);
//让矢量Point绕x、y、z旋转0.5、4、0.1
m = Point.getSee();
//获取Point的透视度m
mc._xscale = mc._yscale=m*40;
//把mc缩放m*40(本例经验值),离读者越远,m越小,mc越小,造成视觉上的立体感
P = Point.cast();
//把Point矢量投影到xy平面,作为下一个时刻mc的坐标
};大家可别被代码吓坏了,从上面我的解释中可以看出,很多代码是不要自己写的,可以说记住就行了,而自己发挥部分,没有太复杂的算法,如果用常规的方法作上面的效果,我想,三角函数,椭圆的参数方程估计少不了吧。
如何,有点感觉了吗,再看第二例:http://www.flash8.net/fla/4848.shtml。立体感出来了吧,看看代码先
//构造一个类
Vector = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
//为类创建一个方法,即矢量的旋转方法,可查一下数学哦
Vector.prototype.rotateXYZ = function(a, b, c) {
var sa = Math.sin(a*Math.PI/180), ca = Math.cos(a*Math.PI/180);
var sb = Math.sin(b*Math.PI/180), cb = Math.cos(b*Math.PI/180);
var sc = Math.sin(c*Math.PI/180), cc = Math.cos(c*Math.PI/180);
with (this) {
//绕x轴
var ry = y*ca-z*sa;
var rz = y*sa+z*ca;
//绕y轴
var rx = rz*sb+x*cb;
z = rz*cb-x*sb;
//绕z轴
x = rx*cc-ry*sc;
y = rx*sc+ry*cc;
}
};
//获取透视度,产生立体效果
Vector.prototype.getSee = function(ViewA4ist) {
if (ViewA4ist == undefined) {
ViewA4ist = 300;
}
return ViewA4ist/(ViewA4ist+this.z);
};
//投影到xy坐标平面内,z=0,因为flash没有真正的3d
Vector.prototype.cast = function(p) {
if (p == undefined) {
p = this.getSee();
}
with (this) {
return new constructor(x*p, y*p, 0);
}
};
//真正我写代码,是从下面开始的
//初始化边长,和某一个顶点A1的坐标
d = 100;
a = -50;
b = -50;
c = -50;
//初始化正方体八个顶点的坐标
A1 = new Vector(a, b, c);
A2 = new Vector(a+d, b, c);
A3 = new Vector(a+d, b+d, c);
A4 = new Vector(a, b+d, c);
A5 = new Vector(a, b+d, c+d);
A6 = new Vector(a+d, b+d, c+d);
A7 = new Vector(a+d, b, c+d);
A8 = new Vector(a, b, c+d);
/*定义画正方体的函数,思路就是把正方体的12条边连出来,方法不唯一,我可是在草稿上画了好几次才画好的哦*/
function draw() {
clear();
lineStyle(1, 100);
moveTo(A1.x, A1.y, A1.z);
lineTo(A2.x, A2.y, A2.z);
lineTo(A3.x, A3.y, A3.z);
lineTo(A4.x, A4.y, A4.z);
lineTo(A5.x, A5.y, A5.z);
lineTo(A8.x, A8.y, A8.z);
lineTo(A7.x, A7.y, A7.z);
lineTo(A6.x, A6.y, A6.z);
lineTo(A3.x, A3.y, A3.z);
moveTo(A1.x, A1.y, A1.z);
lineTo(A4.x, A4.y, A4.z);
moveTo(A1.x, A1.y, A1.z);
lineTo(A8.x, A8.y, A8.z);
moveTo(A7.x, A7.y, A7.z);
lineTo(A2.x, A2.y, A2.z);
moveTo(A5.x, A5.y, A5.z);
lineTo(A6.x, A6.y, A6.z);
}
//随机产生xyz三个方向的旋转角度
e = random(5);
f = random(5);
g = random(5);
/*定义函数,使8个顶点依次按各自的角度旋转后投影作为新的顶点坐标,
调用函数画正方体*/
function mm() {
for (i=1; i<=9; i++) {
this["A"+i].rotateXYZ(e, f, g);
this["A"+i].cast();
}
draw();
}
/*先调用一次函数画出一个立方体,由于还没有调用函数mm,即各点都没有旋转,所以是一个正方形(真正使它旋转,是在按钮里调用了影片剪辑onEnterFrame事件)*/
draw();
(喝杯水先),哈哈,感觉有点强烈吧,同样的工具,在不同的人手里发挥的效应是不一样的,我只是一个新手,做了点东西就是爱丢人现眼,高手别笑我,我只是想抛砖引玉罢了,3D的问题能解决,那二维平面的就没问题了,下面再罗嗦介绍一个二维矢量类的简单应用。
点击这里下载源文件
对高中物理还有映象的闪友可能还记得,这是矢量合成的平行四边行定则嘛,网上早已有人做过了,哈哈,我在这里只是想试一下Vector类的威力罢了(谁丢的香蕉皮?)
看看代码先
_global.Vector = function(x, y) {
this.x = x;
this.y = y;
};
Vector.prototype.reset = function(x, y) {
this.constructor(x, y);
};
//向量相加
Vector.prototype.Addition = function(v) {
with (this) {
return new constructor(x+v.x, y+v.y);
}
};
//向量相减
Vector.prototype.Subtraction = function(v) {
with (this) {
return new constructor(x-v.x, y-v.y);
}
};
//向量角
Vector.prototype.getAngle = function() {
return Math.atan2(this.y, this.x)*180/Math.PI;
};
//向量长度
Vector.prototype.getLength = function() {
with (this) {
return Math.sqrt(x*x+y*y);
}
};
//以上是Robert Penner先生的东西,下面就要靠自己了
//设置各向量及其夹角的初始值
PointA = new Vector(0, 0);
F1 = new Vector(150, 10);
F2 = new Vector(70, 80);
F = F1.Addition(F2);
angle1 = F1.getAngle();
angle2 = F2.getAngle();
angle = F.getAngle();
//设置箭头的方向函数
function setarrow(mc, angle, v) {
with (mc) {
_rotation = angle;
_x = v.x;
_y = v.y;
l = v.getLength();
_xscale = l/5;
_yscale = l/5;
}
}
//调用函数设置箭头方向
setarrow(arrow1, angle1, F1);
setarrow(arrow2, angle2, F2);
setarrow(arrow, angle1, F);
//连线函数
function onLine(v1, v2) {
this.lineStyle(2, 100);
this.moveTo(v1.x, v1.y);
this.lineTo(v2.x, v2.y);
}
//绘图函数
function draw() {
this.clear();
onLine(PointA, F1);
onLine(PointA, F2);
onLine(PointA, F);
onLine(F, F1);
onLine(F, F2);
}
draw();
//以上初始化,完成了一个定态的平行四边形
this.onMouseMove = function() {
if (a == 1 && b == 1) {
F1.reset(this._xmouse, this._ymouse);
F = F1.Addition(F2);
} else if (a == 2 && b == 1) {
F2.reset(this._xmouse, this._ymouse);
F = F2.Addition(F1);
} else if (a == 3 && b == 1) {
F2.reset(this._xmouse, this._ymouse);
F1 = F.Subtraction(F2);
}
angle1 = F1.getAngle();
angle2 = F2.getAngle();
angle = F.getAngle();
setarrow(arrow1, angle1, F1);
setarrow(arrow2, angle2, F2);
setarrow(arrow, angle, F);
//以上是把鼠标的坐标当作一个变量加入,重新初始化各个矢量及其夹角,其中的a在按钮中被赋值
draw();
//调用函数再画一个新的四边形,其中的clear()函数清除了原来的四边形
updateAfterEvent();
};
this.onMouseDown = function() {
b = 1;
};
this.onMouseUp = function() {
b = 2;
};代码好象有点长,但仔细观察不难发现,整个代码没有复杂的算法,都是一些简单方法的直接引用。
不知不觉中浪费了读者这么多时间,不好意思,我们不妨回过头看一看,总结一下,不难得出结论,Vector类的方法在一定程度上避免了太多的复杂的数学算法,对于数学功底不是很好的害怕复杂算法的一部分闪友不失为一种好的方法,可以帮你作出漂亮的flash作品。同一效果实现的方法不是唯一的,所以上面的方法不一定是最好的,对于高手来说,他们习惯了的方法也许才是最好的,看闪吧里,卧虎藏龙,许多高手用一般的方法,凭借过硬的基本功和丰富的想象力,作出了一件件漂亮的作品,真是羡煞旁人也。
参考文献:《Flash Mx编程与创意实现》