# 【程序化动画の震动】
震动算法,配合曲线调整震动节奏。
ShakeAnimation.ts
module vp
{
export type ShakeParams = {
directionCount: number; // 方向数量(必须大于1) 默认8
shakeAmount: number; // 变化次数 默认60
interval: number; // 每次变化时间间隔(毫秒) 默认15
distance: number; // 变化的幅度 默认 5
};
/**
* 抖动的动画
*/
export class ShakeAnimation
{
/** 抖动基础参数 */
private _sp:ShakeParams;
/** 变化曲线参数 */
private _cp:CurveParam;
/** 初始位置 */
private _basePos:Phaser.Math.Vector2 = new Phaser.Math.Vector2(0,0);
public get basePos():Phaser.Math.Vector2
{
return this._basePos;
}
/** 抖动的偏移数组 */
private _shakePosArr:Phaser.Math.Vector2[] = [];
/** 抖动方向数组 */
private _shakeDirectionArr:Phaser.Math.Vector2[] = [];
/** 设置为未播放中 */
private _isPlaying:boolean = false;
public get isPlaying():boolean
{
return this._isPlaying;
}
/** 开始播放的时间 */
private _startPlayTime:number = 0;
/** 当前的时间 */
private _curTime:number = 0;
/** 当前输出 */
private _curResult:Phaser.Math.Vector2 = new Phaser.Math.Vector2();
/** 当前抖动的偏移 */
private get curShakePos():Phaser.Math.Vector2 | undefined
{
const len = this._shakePosArr.length;
if(len < 1)return undefined;
const curProgress = this.curProgress;
if(curProgress >= 1){
return undefined;// 已经播放完毕
}
const idx = Math.floor(len * curProgress);
return this._shakePosArr[idx];
}
private _curProgress:number = 0;
/** 当前播放的进度 0 ~ 1 */
public get curProgress():number
{
const total = this.duraction;
const overTime = this._curTime - this._startPlayTime;
let progress = overTime / total;
if(progress < 0)progress = 0;
if(progress > 1)progress = 1;
this._curProgress = progress;
return progress;
}
/** 总持续的时间,单位毫秒 */
public get duraction():number
{
return this._sp.interval * this._sp.shakeAmount;
}
public init(sp:ShakeParams,cp:CurveParam,baseX:number,baseY:number):void
{
this._isPlaying = false;
this._sp = sp;
this._cp = cp;
this._basePos.x = baseX;
this._basePos.y = baseY;
this._shakeDirectionArr = [];
const dc = this._sp.directionCount;
const pi = 3.1415926536;
for (let i = 0; i < dc; i++) {
const radin = (i / (dc - 1)) * pi;
const dir = new Phaser.Math.Vector2();
dir.x = Math.cos(radin);
dir.y = Math.sin(radin);
this._shakeDirectionArr.push(dir);
}
const dirLen = this._shakeDirectionArr.length;
this._shakePosArr = [];
const shakeAmount = this._sp.shakeAmount;
const distance = this._sp.distance;
for (let i = 0; i < shakeAmount; i++) {
const shakePos = new Phaser.Math.Vector2();
const dirIndex = i % dirLen;
const dir = this._shakeDirectionArr[dirIndex];
shakePos.x = dir.x * distance;
shakePos.y = dir.y * distance;
this._shakePosArr.push(shakePos);
}
// 打乱抖动数组
this._shakePosArr.sort(() => Math.random() - 0.5);
}
public play():void
{
this._startPlayTime = new Date().getTime();
this._curTime = this._startPlayTime;
this._isPlaying = true;
}
public stop():void
{
this._isPlaying = false;
}
/**
*
* time 当前的时间
* delta 与上一帧的时间差,单位毫秒
*
*/
public update(delta: number):Phaser.Math.Vector2
{
if(!this._isPlaying)return;
this._curTime += delta;
const cur = this.curShakePos;
if(!cur){
// 播放完毕
this._isPlaying = false;
this._curResult.x = this._basePos.x;
this._curResult.y = this._basePos.y;
return this._curResult;
}
let value = this._curProgress;
if(this._cp){
value = CurveLibrary.getCurveResult(value,this._cp);
}
const offsetX = this._lerp(0,cur.x,value);
const offsetY = this._lerp(0,cur.y,value);
this._curResult.x = this._basePos.x + offsetX;
this._curResult.y = this._basePos.y + offsetY;
return this._curResult;
}
private _lerp(a:number,b:number,t:number):number
{
return (1 - t) * a + t * b;
}
}
}
CurveLibrary.ts
module vp
{
export type CurveFunc = (x:number)=>number;
export enum PartType
{
BOTH,
LEFT,
RIGHT
}
export type CurveParam =
{
idx:number;
partType:PartType;
reverse:boolean;
};
/**
* 曲线预置库
*/
export class CurveLibrary
{
/** 5x7 */
public static MAX_COUNT:number = 35;
/** 单排数量 */
public static X_NUM:number = 7;
private static _CurveFuncArr: CurveFunc[] = [
(x)=>(1.0 - Math.pow(Math.abs(x),0.5)),
(x)=>(1.0 - Math.pow(Math.abs(x),1.0)),
(x)=>(1.0 - Math.pow(Math.abs(x),1.5)),
(x)=>(1.0 - Math.pow(Math.abs(x),2.0)),
(x)=>(1.0 - Math.pow(Math.abs(x),2.5)),
(x)=>(1.0 - Math.pow(Math.abs(x),3.0)),
(x)=>(1.0 - Math.pow(Math.abs(x),3.5)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),0.5)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),1.0)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),1.5)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),2.0)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),2.5)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),3.0)),
(x)=>(Math.pow(Math.cos(3.1415926536 * x / 2.0),3.5)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),0.5)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),1.0)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),1.5)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),2.0)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),2.5)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),3.0)),
(x)=>(1.0 - Math.pow(Math.abs(Math.sin(3.1415926536 * x / 2.0)),3.5)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),0.5)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),1.0)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),1.5)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),2.0)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),2.5)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),3.0)),
(x)=>(Math.pow(Math.min(Math.cos(3.1415926536 * x / 2.0),1.0-Math.abs(x)),3.5)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),0.5)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),1.0)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),1.5)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),2.0)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),2.5)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),3.0)),
(x)=>(1.0 - Math.pow(Math.max(0.0,Math.abs(x) * 2.0 - 1.0),3.5)),
];
private static getCurveFuncWithIndex(idx: number): CurveFunc {
return this._CurveFuncArr[idx];
}
/**
* 获取计算结果
* lineX 在 0 ~ 1之间
*/
public static getCurveResult(lineX:number,cp:CurveParam):number
{
if(lineX > 1)lineX = 1;
if(lineX < 0)lineX = 0;
let remapLineX = lineX;
if(cp.partType === PartType.BOTH){
// 把 lineX 映射到 -1 ~ 1
remapLineX = lineX * 2 - 1;
}else if(cp.partType === PartType.LEFT){
// 把 lineX 映射到 -1 ~ 0
remapLineX = lineX - 1;
}
const cf = CurveLibrary.getCurveFuncWithIndex(cp.idx);
const baseResult = cf(remapLineX);
if(cp.reverse){
return 1.0 - baseResult;
}
return baseResult;
}
}
}
← 【程序化动画の弹簧】 【自定义曲线】 →