timelineAni.play();
timelineAni.play(true);
//循环播放 10% ~ 98.7% 这一段时间轴
timelineAni.play(true,0.1,0.987);
//单次播放 10% ~ 98.7% 这一段时间轴
timelineAni.play(false,0.1,0.987);
非循环播放结束时触发
//once: 执行一次后 自动移除
timelineAni.once(TimelineSprite.EVENT_ENDED, this, () => {
});
动画帧播放到尾帧时触发
timelineAni.on(TimelineSprite.EVENT_ENDFRAME, this, () => {
});
//移除事件监听
timelineAni.offAll(TimelineSprite.EVENT_ENDFRAME)
/**
* 添加一个事件
* @param pos 位置 百分比 0~1
* @param caller
* @param fun
* @param once 是否执行一次后自动移除
*/
addEvent(pos: number, caller: any, fun: Function, once: boolean = false)
调用案例
//例如 攻击动画 可以在攻击动画播放到一半时 调用
timelineAni.addEvent(0.5, this, () => {
}, false);
clone的性能很高 帧数组是共享的
//clone 会拷贝当前动画的所有状态信息 包括缩放 位置 第几帧 但不会有上一个动画的帧事件
timelineAni.clone()
Timeline动画系统 由 TimelineSprite 和 TimelineFactory 组成
import { TimelineSprite } from "./TimelineSprite";
/**
* 所有的 TimelineSprite 对象 统一调度管理
* 全局只产生一个定时器
*/
export default class TimelineFactory {
/**
* 减少内存碎片 使用单例管理一个类申请的所有内存
*/
private static instance: TimelineFactory;
private elements: TimelineSprite[] = [];
/**
* 隐藏构造方法 不允许自行创建或访问
*/
private constructor() { };
/**
* 不允许业务逻辑自行创建
*/
private static genSingleton() {
TimelineFactory.instance = new TimelineFactory();
Laya.timer.frameLoop(1, TimelineFactory.instance, TimelineFactory.instance.update);
}
/**
* 更新
*/
private update() {
if (this.elements.length > 0) {
let element: TimelineSprite, delta = Laya.timer.delta;
for (let i = 0; i < this.elements.length; i++) {
element = this.elements[i];
element['tm'] += delta;
if( element['tm'] >= element['interval'] ){
element['tm'] -= element['interval'];
element['updateFrame']();
}
}
}
}
public appendTimeline(timeline: TimelineSprite) {
let index = this.elements.indexOf(timeline);
if (index == -1) {
this.elements.push(timeline);
}
}
public removeTimeline(timelineOrIndex: TimelineSprite | number) {
if (typeof timelineOrIndex == "number") {
if (timelineOrIndex >= 0 && timelineOrIndex < this.elements.length) {
this.elements.splice(timelineOrIndex, 1);
}
}
else {
let index = this.elements.indexOf(timelineOrIndex);
if (index != -1) {
this.elements.splice(index, 1);
}
}
}
/**
* 主动释放
* 一般情况下 整个游戏生命周期内 都不需要调用 你可以忽视它
*/
public release() {
this.elements = [];
Laya.timer.clear(TimelineFactory.instance, TimelineFactory.instance.update);
}
}
import TimelineFactory from "./TimelineFactory";
export class TimelineSprite extends Laya.Sprite {
protected interval: number;
protected tm: number;
protected play_begin_pos: number;
protected play_end_pos: number;
protected loop: boolean = false;
protected index: number = 0;
protected frames: string[] = [];
protected frameEvent: { [frameID: number]: Laya.Handler[] } = {};
protected last_frame: Laya.Texture;
/**
* 最后一帧派发
*/
public static readonly EVENT_ENDFRAME = "EVENT_ENDFRAME";
/**
* 非循环播放结束时触发
*/
public static readonly EVENT_ENDED = "EVENT_ENDED";
/**
* 构造方法
* @param frames 动画帧数组
* @param frameRate 帧率 例如: frameRate = 30 那每帧的间隔就是33.33ms ( 1000 / 30 )
*/
constructor(frames: string[], frameRate: number = 30) {
super();
this.frames = frames;
this.interval = Math.floor(1000.0 * 1000 / frameRate) / 1000;
this.tm = 0;
}
/**
* 设置新的动画组
* @param frames 动画帧数组
* @param frameRate 帧率 可以不填 采用上一个动画的方案 默认30帧每秒
*/
setNewAnimationFrames(frames: string[], frameRate?: number) {
if (frameRate)
this.interval = Math.floor(1000.0 * 1000 / frameRate) / 1000;
this.frames = frames;
}
/**
* 播放
* @param loop 是否循环播放 默认false
* @param start 开始播放的位置 百分百 0~1 循环中也是以这个为主
* @param end 结束播放的位置 百分比 0~1
*/
play(loop: boolean = false, start: number = 0, end: number = 1.0) {
this.tm = 0;
this.loop = loop;
this.play_begin_pos = Math.floor(start * this.frames.length);
this.play_end_pos = Math.floor(end * this.frames.length);
this.index = this.play_begin_pos;
this.last_frame = Laya.loader.getRes(this.frames[this.index]);
this.add2aniPool();
}
/**
* 添加一个事件
* @param pos 位置 百分比 0~1
* @param caller
* @param fun
* @param once 是否执行一次后自动移除
*/
addEvent(pos: number, caller: any, fun: Function, once: boolean = false) {
let frameIndex = Math.floor(pos * this.frames.length);
let handlers = this.frameEvent[frameIndex];
if (handlers && handlers.find(handler => {
return handler.caller == caller && handler.method == fun;
}) != null) {
return;
}
handlers = this.frameEvent[frameIndex] || [];
handlers.push(Laya.Handler.create(caller, fun, null, once));
this.frameEvent[frameIndex] = handlers;
}
/**
* 停止播放
* 会停留在当前帧
* 你可以使用 visible = false; 来隐藏 动画展示
*/
stop() {
this.removeFromPool();
}
/**
*
* 更新帧
*/
protected updateFrame(): void {
this.onframeEvent();
++this.index;
if (this.index >= this.play_end_pos) {
this.event(TimelineSprite.EVENT_ENDFRAME);
if (!this.loop) {
this.removeFromPool();
this.event(TimelineSprite.EVENT_ENDED);
return;
}
else {
this.index = this.play_begin_pos;
}
}
this.last_frame = Laya.loader.getRes(this.frames[this.index]);
this.draw();
}
protected onframeEvent() {
const events = this.frameEvent[this.index];
if (events) {
for (let i = 0; i < events.length; i++) {
events[i].run();
if (events[i].once) {
events.splice(i--, 1);
}
}
}
}
private draw() {
this.graphics.clear();
this.graphics.drawImage(this.last_frame);
}
protected add2aniPool() {
!TimelineFactory['instance'] && TimelineFactory['genSingleton']();
TimelineFactory['instance'].appendTimeline(this);
}
protected removeFromPool() {
TimelineFactory['instance'] && TimelineFactory['instance'].removeTimeline(this);
}
public clone() {
let obj = new TimelineSprite(this.frames);
for (let k in this) {
if (typeof k == "number" || typeof k == "boolean") {
obj[k] = this[k];
}
}
return obj;
}
}
gitee
https://gitee.com/welcome2jcSpace/laya_-time-line_-demo