2021SC@SDUSC
目录
一.clay.animation.Blend2DClip类概述
二.clay.animation.Blend2DClip类的作用
三.clay.animation.Blend2DClip类源码分析
2.addInput(position, inputClip, offsetopt) → {clay.animation.Blend2DClip.IClipInput}
3.clone(cloneInputs) → {clay.animation.Blend2DClip}
一.clay.animation.Blend2DClip类概述
clay.animation.Blend2DClip继承自clay.animation.Clip,是剪辑Clip的二维形式Blend2DClip,可为动画添加二维剪辑。
二.clay.animation.Blend2DClip类的作用
Blend2DClip类能够进行创建一个动画的二维剪辑,克隆二维剪辑,暂停二维剪辑的播放,恢复二维剪辑的播放,设置二维剪辑的缓动效果,设置二维剪辑的循环次数,设置二维剪辑的播放时间以及插入二维剪辑等操作。
三.clay.animation.Blend2DClip类源码分析
1.new Blend2DClip(opts opt)
该函数为Blend2DClip类的构造函数,其参数opts,类型为object(对象),参数opts对象内含有的成员变量如下:
变量名 | 变量类型 | 是否必须 | 构造后默认值 | 描述 |
name | string | 否 | '' | 剪辑的名称 |
target | object | 是 | opts.target | 目标对象 |
life | number | 否 | 1000 | 动画的生命时长 |
delay | number | 否 | 0 | 剪辑的时长 |
gap | number | 否 | 0 | 剪辑的间隔 |
playbackRatio | number | 否 | 1 | 播放的速度 |
loop | boolean | number | 否 | false | boolean表示是否循环,number表示循环的次数 |
easing | string | function | 否 | 缓动效果 | |
onframe | function | 否 | noop | 动画的帧 |
onfinish | function | 否 | noop | 剪辑结束 |
onrestart | function | 否 | noop | 剪辑开始 |
inputs | Array.<object> | 否 | []空数组 | 二维剪辑的对象数组 |
position | clay.Vector2 | 否 | new Vector2() | 剪辑的二维位置 |
output | clay.animation.Clip | 否 | null | 剪辑的输出 |
其源码如下:
var Blend2DClip = function (opts) {
opts = opts || {};
Clip.call(this, opts);
/**
* Output clip must have blend2D method
* @type {clay.animation.Clip}
*/
this.output = opts.output || null;
/**
* @type {clay.animation.Blend2DClip.IClipInput[]}
*/
this.inputs = opts.inputs || [];
/**
* @type {clay.Vector2}
*/
this.position = new Vector2();
this._cacheTriangle = null;
this._triangles = [];
this._updateTriangles();
}
从源码中可以看到,Blend2DClip共有5个成员变量,在构造函数中接收一个object类型的参数,该参数内共有14个成员变量。Blend2DClip类继承Clip类,当将opts赋给构造函数后,首先调用父类Clip的构造函数,使用父类Clip的构造方法获得成员变量和值,然后将opts内的其它成员变量的值赋给Blend2DClip对象,若其中一些成员变量为空,则使用Clip的默认值。
2.addInput(position, inputClip, offsetopt) → {clay.animation.Blend2DClip.IClipInput}
该函数能够向动画中添加剪辑,从而实现往动画中增加一段剪辑的效果。
参数有position,表示在动画中添加剪辑的位置,类型为Vector2;inputClip表示在动画中添加的剪辑,类型为Clip;offset表示所添加剪辑在动画中的偏移,类型为number,若为空则默认为0。
该函数的源代码如下:
Blend2DClip.prototype.addInput = function (position, inputClip, offset) {
var obj = {
position : position,
clip : inputClip,
offset : offset || 0
};
this.inputs.push(obj);
this.life = Math.max(inputClip.life, this.life);
// TODO Change to incrementally adding
this._updateTriangles();
return obj;
}
从源代码中可以看出,首先,将接收到的形式参数position,inputClip和offset封装为一个对象obj,对象中共有3个键值对,键值对的属性分别为接收到的形式参数的名称,值为形式参数的值,若offset无值,则默认为0。
然后,将obj对象加入到本对象的inputs数组中,比较接收到的参数inputClip和本对象的life大小,取最大值并作为本对象的life,接着,执行本对象的_updateTriangles()方法,该方法源代码如下:
Blend2DClip.prototype._updateTriangles = function () {
var inputs = this.inputs.map(function (a) {
return a.position;
});
this._triangles = delaunay.triangulate(inputs, 'array');
}
从代码中可以看出,该方法获取inputs数组中每一个对象的position值,并形成map表,键为inputs对象,值为键对象的位置position,同时将该键值对形成对象依次加入到_triangles数组中。
从而实现二维动画剪辑的添加。
最后,将本对象返回,完成二维剪辑的添加。
3.clone(cloneInputs) → {clay.animation.Blend2DClip}
该函数的作用是克隆一个Blend2DClip,实现二维剪辑的克隆。
参数为cloneInputs,表示是否克隆该剪辑,若为true则克隆,类型为boolean
其源代码为:
Blend2DClip.prototype.clone = function (cloneInputs) {
var clip = Clip.prototype.clone.call(this);
clip.output = this.output.clone();
for (var i = 0; i < this.inputs.length; i++) {
var inputClip = cloneInputs ? this.inputs[i].clip.clone(true) : this.inputs[i].clip;
clip.addInput(this.inputs[i].position, inputClip, this.inputs[i].offset);
}
return clip;
}
从源代码中可以看出,首先调用父类的clone方法,克隆本剪辑并赋值给新建的局部变量clip,同时对本剪辑的输出也克隆赋给clip的output,接着调用for循环遍历inputs的每一个对象,若cloneInputs存在,则将inputs第i个对象的克隆赋给新建的局部变量inputClip,否则直接赋给inputs的第i个对象,并调用clip的addInput方法,将inputClip添加到克隆Clip的input中,实现克隆。最后,返回已经克隆好的对象clip。
4.pause()
该函数能够暂停剪辑,从而实现剪辑的暂停,继承自父类。
无参数,可直接调用。
其源代码为:
pause: function () {
this._paused = true;
}
从源代码可以看出,该函数将成员变量_paused设置为true,实现剪辑的暂停。
5.resume()
该函数能够恢复剪辑,从而实现剪辑的恢复,继承自父类。
无参数,可直接调用。
其源代码如下:
resume: function () {
this._paused = false;
}
从源代码可以看出,该函数将成员变量_paused设置为false,从而实现剪辑的恢复
6.setEasing(easing)
该函数的作用是设置剪辑的缓动效果,继承自父类。
参数为easing,表示缓动效果,类型为string或function。
其源代码为:
setEasing: function (easing) {
if (typeof(easing) === 'string') {
easing = Easing[easing];
}
this.easing = easing;
}
从源代码分析可得,该函数首先判断接收的形式参数easing类型是否为string,若是,则将内置的Easing数组中名为easing的方法赋给本对象的easing,若不是,则将easing方法赋给本对象的easing,从而实现内置或自定义的缓动效果。
7.setLoop(loop)
该函数的作用是设置是否循环以及循环的时间,继承自父类。
参数为loop,类型为number,表示循环的时间,类型为boolean,表示是否循环,为真则无限循环。
其源代码为:
setLoop: function (loop) {
this._loop = loop;
if (loop) {
if (typeof loop === 'number') {
this._loopRemained = loop;
}
else {
this._loopRemained = Infinity;
}
}
}
从源代码中可以看出,首先将形参loop赋给本对象的_loop,如果loop为真,判断类型,若为number,则将loop赋值给本对象的_loopRemained,表示循环loop次,若为boolean则赋值Infinity,表示循环无数次。若loop为假,则不循环。
8.setTime(time) → {string}
该函数能够在指定的时间开始动画,继承自父类。
其参数为time,表示开始的时间,类型为number。
其源代码为:
setTime: function (time) {
return this.step(time + this._startTime);
}
由源代码可知,该函数调用step()函数,step()函数如下所述,能够在指定时间播放剪辑,因此,setTime()将time和本对象的_startTime相加,赋给函数step(),表示设置当前播放剪辑的时间。
9.step(time) → {string}
该函数能够在指定时间内添加一段剪辑,继承自父类。
其参数为time,表示剪辑添加的时刻,类型为number
其源代码为:
step: function (time, deltaTime, silent) {
if (!this._initialized) {
this._startTime = time + this.delay;
this._initialized = true;
}
if (this._currentTime != null) {
deltaTime = time - this._currentTime;
}
this._currentTime = time;
if (this._paused) {
return 'paused';
}
if (time < this._startTime) {
return;
}
// PENDIGN Sync ?
this._elapse(time, deltaTime);
var percent = Math.min(this._elapsedTime / this.life, 1);
if (percent < 0) {
return;
}
var schedule;
if (this.easing) {
schedule = this.easing(percent);
}
else {
schedule = percent;
}
if (!silent) {
this.fire('frame', schedule);
}
if (percent === 1) {
if (this._loop && this._loopRemained > 0) {
this._restartInLoop(time);
this._loopRemained--;
return 'restart';
}
else {
// Mark this clip to be deleted
// In the animation.update
this._needsRemove = true;
return 'finish';
}
}
else {
return null;
}
}
由其源代码可知,当执行step()函数后,首先会判断是否初始化,若未初始化,即_initialized为false,则进行初始化,将本对象的_startTime设置为本对象的delay时间加上输入的时间time,并将_initialized设置为true,表示已经初始化。
然后,判断本对象的_currentTime是否不为空,若不为空则将deltaTime的值设置为输入的时间time减去本对象的_currentTime的值。
然后设置当前的时间_currentTime为输入的时间time。如果_paused为真,则返回‘pause’,表示本剪辑已暂停,若为假,则继续判断输入的时间time是否小于剪辑开始时间_starttime,若为真,表示输入不正确,返回,若为假,继续进行后面的操作。
接下来执行elapse()函数,参数为输入的时间time和相减后的时间deltaTime,其代码为:
_elapse: function (time, deltaTime) {
this._elapsedTime += deltaTime * this.playbackRate;
}
该函数能够设置本对象的成员变量_elapsedTime的值增加deltaTime与本对象的成员变量playbackRate的乘积,表示为总剪辑添加一段时间的剪辑,这一段时间的剪辑的播放速度即为playbackRate的值。
因此,向剪辑中添加一段时间后,将_elapsedTime与life的商和1对比,取小的值,并赋给创建的局部变量percent,如果percent小于0则返回,表示当前剪辑的百分比,百分比必须为正数。
然后,创建一个局部变量schedule,如果easing缓动效果存在,将目前剪辑部分(当前百分比)的缓动效果赋值给schedule,否则将百分比赋给schedule,表示当前执行的动画,是缓动效果还是播放动画。
下面,如果silent假,则执行fire()函数,其源代码为:
fire: function (eventType, arg) {
var eventName = 'on' + eventType;
if (this[eventName]) {
this[eventName](this.target, arg);
}
}
该函数将eventType参数前加上on字符串,形成一个局部变量eventName,如果本对象有eventName成员变量,则在eventName成员变量中加入本对象的target以及arg参数,而传入为frame,eventType为onframe,arg为schedule,表示onframe中添加键target和值schedule,实现剪辑中添加相应的行为,表示添加剪辑的帧。
最后,判断percent百分比是否为100%,不是则返回null,是的话继续判断是否循环以及剩余循环次数是否为大于0,是的话执行_restartInLoop函数,其代码为:
_restartInLoop: function (time) {
this._startTime = time + this.gap;
this._elapsedTime = 0;
}
该函数接收time参数,将开始时间设置为time和本对象的gap相加,并把剪辑时间_elapsedTime设置为0,表示重新执行该剪辑。因此,结合而看,如果仍有循环次数,则重新执行剪辑,并将剩余的循环次数减1,返回‘restart’,表示重新开启,若循环次数已经完成,则设置成员变量_needsRemove为true,表示需要执行的剪辑已经完成,并返回‘finish’,表示完成,从而结束该函数。 可见,step()函数可在指定的时间上播放一段剪辑。