贝塞尔曲线类
export type pos = {
x: number;
y: number;
}
export class BezierPath {
/**
* 创建几个选定坐标点沿着贝塞尔曲线的坐标集
* @param anchorpoints 关键坐标点(比如开始点,最高峰值点,结束点)
* @param pointsAmount 需要沿着曲线创建的坐标点数量(数字越大,则运动轨迹越精确)
*/
private static CreateBezierPoints(anchorpoints: pos[], pointsAmount: number): Array<pos> {
const points = new Array<pos>();
let point: pos;
for (let i = 0; i < pointsAmount; ++i) {
point = this.MultiPointBezier(anchorpoints, i / pointsAmount);
points.push(point);
}
return points;
}
private static MultiPointBezier(points: pos[], t: number): pos {
let x = 0;
let y = 0;
let point: pos;
const len = points.length;
for (let i = 0; i < len; ++i) {
point = points[i];
x += point.x * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * this.erxiangshi(len - 1, i);
y += point.y * Math.pow(1 - t, len - 1 - i) * Math.pow(t, i) * this.erxiangshi(len - 1, i);
}
return { x: x, y: y };
}
private static erxiangshi(start: number, end: number): number {
let cs = 1;
let bcs = 1;
while (end > 0) {
cs *= start;
bcs *= end;
start--;
end--;
}
return cs / bcs;
}
/**
* 为MovieClip组件创建贝塞尔曲线运动缓动动画
* @param oObject 运动UI
* @param startPoint 开始位置
* @param highPoint 曲线运动的波峰、最高点
* @param endPoint 结束位置
* @param pointAmount 运动的点位数,越高精度越高,速度越慢,默认60点,大于3点
* @param endFunc 动画结束后的回调函数
*/
public static CreateTweenToBezier(
oObject: fgui.GMoviewClip,
startPoint: pos,
highPoint: pos,
endPoint: pos,
pointAmount = 60,
endFunc ?: Laya.Handler
): void {
if (pointAmount <= 3) {
console.log("输入点小于3,不创建");
return;
}
const points = this.CreateBezierPoints([startPoint, highPoint, endPoint], pointAmount);
let endP = points.shift();
const CMDcontroler = new Array<Laya.Handler>(); //缓动动画管理器
while (points.length > 0) {
CMDcontroler.push(
Laya.Handler.create(
this,
(endP: pos) => {
Laya.Tween.to(
oObject,
{
x: endP.x,
y: endP.y,
}
1,
Laya.Ease.linearIn,
Laya.Handler.create(this, () => {
if (CMDcontroler.length > 0) {
CMDcontroler.shift().run();
} else {
if (endFunc) {
endFunc.run();
}
}
})
);
},
[endP]
)
);
endP = points.shift();
}
CMDcontroler.shift().run();
}
}
动效控制类
import UI_MoneyDownAnim from "FGUI/UI_MoneyDownAnim";
import { BezierPath, pos } from "BezierPath"
export class MoneyDownAnimCtrl {
private _obj: UI_MoneyDownAnim;
private _startPos: pos;
private resPool: Array<fgui.GMovieClip>; //对象缓冲池
private poolSize = 10;
private minNums = 5; //最低金币数
private maxNums = 8; //最多金币数
private gapTimes = 5000; //间隔时间,单位:毫秒
//落点x坐标
private endRangeMinX = 10;
private endRangeMaxX = 390;
//落点y坐标
private endRangeMinY = 270;
private endRangeMaxY = 290;
private items: Array<fgui.GMovieClip>; //实际演示数组
constructor(obj: UI_MoneyDownAnim) {
this._obj = obj;
this._startPos = {
x: obj.m_startPoint.x,
y: obj.m_startPoint.y,
};
this.items = new Array<fgui.GMovieClip>();
this.initResPool();
}
private refreshPos(clip: fgui.GMovieClip): void {
clip.x = this._startPos.x;
clip.y = this._startPos.y;
}
private initClip(clip: fgui.GMovieClip): void {
this._obj.addChild(clip);
clip.visible = false;
clip.playing = false;
clip.width = 30;
clip.height = 30;
this.refreshPos(clip);
}
private initResPool(): void {
this.resPool = new Array<fgui.GMovieClip>();
let clip: fgui.GMovieClip;
for (let i = 0; i < this.poolSize; ++i) {
clip = fgui.UIPackage.createObject("MainMenu", "MoneyAnim").asMovieClip;
this.initClip(clip);
this.resPool.push(clip);
}
}
private getClipFromPool(): fgui.GMovieClip {
let clip: fgui.GMovieClip;
if (this.resPool.length === 0) {
clip = fgui.UIPackage.createObject("MainMenu", "MoneyAnim").asMovieClip);
this.initClip(clip);
} else {
clip = this.resPool.shift();
}
clip.playing = true;
clip.visible = true;
return clip;
}
private removeClipToPool(clip: fgui.GMovieClip): void {
clip.playing = false;
clip.visible = false;
this.refreshPos(clip);
this.resPool.push(clip);
}
private getRandomForm(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1) + min);
}
//获取随机金币数
private refreshItems(): void {
this.items.length = 0;
const nums = this.getRandomForm(this.minNums, this.maxNums);
for (let i = 0; i < nums; ++i) {
this.items.push(this.getClipFormPool());
}
}
private playAnim(): void {
this.refreshItems();
let endPoint: pos;
let highPoint: pos;
this.items.forEach((clip) => {
endPoint = {
x: this.getRandomForm(this.endRangeMinX, this.endRangeMaxX),
y: this.getRandomForm(this.endRangeMinY, this.endRangeMaxY),
};
highPoint = {
x: (endPoint.x + this._startPos.x) / 2,
y: this._startPos.y - this.getRandomForm(100, 170),
};
BezierPath.CreateTweenToBezier(
clip,
this._startPos,
highPoint,
endPoint,
60,
Laya.Handler.create(
this,
(clip: fgui.GMovieClip) => {
this.removeClipToPool(clip);
},
[clip]
)
);
});
}
public play(): void {
Laya.timer.loop(this.gapTimes, this, this.playAnim);
}
public stop(): void {
Laya.timer.clear(this, this.playAnim);
this.items.forEach((val) => {
Laya.Tween.clearAll(val);
this.removeClipToPool(val);
});
}
}