dragonbones-runtime
Version:
the tools to build dragonbones file for diffrent framework
757 lines (689 loc) • 24.8 kB
text/typescript
//////////////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2014-present, Egret Technology.
// All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the Egret nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////
namespace egret {
/**
* @version Egret 2.4
* @platform Web,Native
* @includeExample extension/game/display/MovieClip.ts
* @language en_US
*/
/**
* 影片剪辑,可以通过影片剪辑播放序列帧动画。MovieClip 类从以下类继承而来:DisplayObject 和 EventDispatcher。不同于 DisplayObject 对象,MovieClip 对象拥有一个时间轴。
* @extends egret.DisplayObject
* @event egret.Event.COMPLETE 动画播放完成。
* @event egret.Event.LOOP_COMPLETE 动画循环播放完成。循环最后一次只派发 COMPLETE 事件,不派发 LOOP_COMPLETE 事件。
* @see http://edn.egret.com/cn/docs/page/596 MovieClip序列帧动画
* @version Egret 2.4
* @platform Web,Native
* @includeExample extension/game/display/MovieClip.ts
* @language zh_CN
*/
export class MovieClip extends DisplayObject {
//Render Property
$bitmapData: Texture = null;
//Render Property
private offsetPoint: Point = Point.create(0, 0);
//Data Property
$movieClipData: MovieClipData = null;
/**
* @private
*/
private frames: any[] = null;
/**
* @private
*/
$totalFrames: number = 0;
/**
* @version Egret 2.4
* @platform Web,Native
* @private
*/
public frameLabels: any[] = null;
/**
* @private
*/
$frameLabelStart: number = 0;
/**
* @private
*/
$frameLabelEnd: number = 0;
/**
* @version Egret 2.4
* @platform Web,Native
* @private
*/
public frameEvents: any[] = null;
/**
* @private
*/
private frameIntervalTime: number = 0;
/**
* @private
*/
$eventPool: string[] = null;
//Animation Property
$isPlaying: boolean = false;
/**
* @private
*/
private isStopped: boolean = true;
/**
* @private
*/
private playTimes: number = 0;
/**
* @private
*/
$currentFrameNum: number = 0;
/**
* @private
*/
$nextFrameNum: number = 1;
/**
* @private
*/
private displayedKeyFrameNum: number = 0;
/**
* @private
*/
private passedTime: number = 0;
/**
* @private
*/
private $frameRate: number = NaN;
//Construct Function
/**
* 创建新的 MovieClip 实例。创建 MovieClip 之后,调用舞台上的显示对象容器的addElement方法。
* @param movieClipData {movieClipData} 被引用的 movieClipData 对象
* @version Egret 2.4
* @platform Web,Native
*/
constructor(movieClipData?: MovieClipData) {
super();
this.$smoothing = Bitmap.defaultSmoothing;
this.$renderNode = new sys.NormalBitmapNode();
this.setMovieClipData(movieClipData);
}
/**
* @private
*/
$smoothing: boolean;
/**
* Whether or not is smoothed when scaled.
* @version Egret 3.0
* @platform Web
* @language en_US
*/
/**
* 控制在缩放时是否进行平滑处理。
* @version Egret 3.0
* @platform Web
* @language zh_CN
*/
public get smoothing(): boolean {
return this.$smoothing;
}
public set smoothing(value: boolean) {
value = !!value;
if (value == this.$smoothing) {
return;
}
this.$smoothing = value;
}
/**
* @private
*
*/
$init() {
this.$reset();
let movieClipData: MovieClipData = this.$movieClipData;
if (movieClipData && movieClipData.$isDataValid()) {
this.frames = movieClipData.frames;
this.$totalFrames = movieClipData.numFrames;
this.frameLabels = movieClipData.labels;
this.frameEvents = movieClipData.events;
this.$frameRate = movieClipData.frameRate;
this.frameIntervalTime = 1000 / this.$frameRate;
this._initFrame();
}
}
/**
* @private
*
*/
$reset(): void {
this.frames = null;
this.playTimes = 0;
this.$isPlaying = false;
this.setIsStopped(true);
this.$currentFrameNum = 0;
this.$nextFrameNum = 1;
this.displayedKeyFrameNum = 0;
this.passedTime = 0;
this.$eventPool = [];
}
/**
* @private
*
*/
private _initFrame(): void {
if (this.$movieClipData.$isTextureValid()) {
this.advanceFrame();
this.constructFrame();
}
}
/**
* @private
*/
$updateRenderNode(): void {
let texture = this.$bitmapData;
if (texture) {
let offsetX: number = Math.round(this.offsetPoint.x);
let offsetY: number = Math.round(this.offsetPoint.y);
let bitmapWidth: number = texture.$bitmapWidth;
let bitmapHeight: number = texture.$bitmapHeight;
let textureWidth: number = texture.$getTextureWidth();
let textureHeight: number = texture.$getTextureHeight();
let destW: number = Math.round(texture.$getScaleBitmapWidth());
let destH: number = Math.round(texture.$getScaleBitmapHeight());
let sourceWidth: number = texture.$sourceWidth;
let sourceHeight: number = texture.$sourceHeight;
sys.BitmapNode.$updateTextureData(<sys.NormalBitmapNode>this.$renderNode, texture.$bitmapData, texture.$bitmapX, texture.$bitmapY,
bitmapWidth, bitmapHeight, offsetX, offsetY, textureWidth, textureHeight, destW, destH, sourceWidth, sourceHeight, egret.BitmapFillMode.SCALE, this.$smoothing);
}
}
/**
* @private
*/
$measureContentBounds(bounds: Rectangle): void {
let texture = this.$bitmapData;
if (texture) {
let x: number = this.offsetPoint.x;
let y: number = this.offsetPoint.y;
let w: number = texture.$getTextureWidth();
let h: number = texture.$getTextureHeight();
bounds.setTo(x, y, w, h);
}
else {
bounds.setEmpty();
}
}
/**
* @private
*
* @param stage
* @param nestLevel
*/
$onAddToStage(stage: Stage, nestLevel: number): void {
super.$onAddToStage(stage, nestLevel);
if (this.$isPlaying && this.$totalFrames > 1) {
this.setIsStopped(false);
}
}
/**
* @private
*
*/
$onRemoveFromStage(): void {
super.$onRemoveFromStage();
this.setIsStopped(true);
}
//Data Function
/**
* @private
* 返回帧标签为指定字符串的FrameLabel对象
* @param labelName {string} 帧标签名
* @param ignoreCase {boolean} 是否忽略大小写,可选参数,默认false
* @returns {egret.FrameLabel} FrameLabel对象
*/
private getFrameLabelByName(labelName: string, ignoreCase: boolean = false): FrameLabel {
if (ignoreCase) {
labelName = labelName.toLowerCase();
}
let frameLabels = this.frameLabels;
if (frameLabels) {
let outputFramelabel: FrameLabel = null;
for (let i = 0; i < frameLabels.length; i++) {
outputFramelabel = frameLabels[i];
if (ignoreCase ? outputFramelabel.name.toLowerCase() == labelName : outputFramelabel.name == labelName) {
return outputFramelabel;
}
}
}
return null;
}
/**
* @private
* 根据帧标签,设置开始和结束的帧数
* @param labelName {string} 帧标签名
*/
private getFrameStartEnd(labelName: string): void {
let frameLabels = this.frameLabels;
if (frameLabels) {
let outputFramelabel: FrameLabel = null;
for (let i = 0; i < frameLabels.length; i++) {
outputFramelabel = frameLabels[i];
if (labelName == outputFramelabel.name) {
this.$frameLabelStart = outputFramelabel.frame;
this.$frameLabelEnd = outputFramelabel.end;
break;
}
}
}
}
/**
* @private
* 返回指定序号的帧的FrameLabel对象
* @param frame {number} 帧序号
* @returns {egret.FrameLabel} FrameLabel对象
*/
private getFrameLabelByFrame(frame: number): FrameLabel {
let frameLabels = this.frameLabels;
if (frameLabels) {
let outputFramelabel: FrameLabel = null;
for (let i = 0; i < frameLabels.length; i++) {
outputFramelabel = frameLabels[i];
if (outputFramelabel.frame == frame) {
return outputFramelabel;
}
}
}
return null;
}
/**
* @private
* 返回指定序号的帧对应的FrameLabel对象,如果当前帧没有标签,则返回前面最近的有标签的帧的FrameLabel对象
* @method egret.MovieClip#getFrameLabelForFrame
* @param frame {number} 帧序号
* @returns {egret.FrameLabel} FrameLabel对象
*/
private getFrameLabelForFrame(frame: number): FrameLabel {
let outputFrameLabel: FrameLabel = null;
let tempFrameLabel: FrameLabel = null;
let frameLabels = this.frameLabels;
if (frameLabels) {
for (let i = 0; i < frameLabels.length; i++) {
tempFrameLabel = frameLabels[i];
if (tempFrameLabel.frame > frame) {
return outputFrameLabel;
}
outputFrameLabel = tempFrameLabel;
}
}
return outputFrameLabel;
}
//Animation Function
/**
* 继续播放当前动画
* @param playTimes {number} 播放次数。 参数为整数,可选参数,>=1:设定播放次数,<0:循环播放,默认值 0:不改变播放次数(MovieClip初始播放次数设置为1),
* @version Egret 2.4
* @platform Web,Native
*/
public play(playTimes: number = 0): void {
this.$isPlaying = true;
this.setPlayTimes(playTimes);
if (this.$totalFrames > 1 && this.$stage) {
this.setIsStopped(false);
}
}
/**
* 暂停播放动画
* @version Egret 2.4
* @platform Web,Native
*/
public stop(): void {
this.$isPlaying = false;
this.setIsStopped(true);
}
/**
* 将播放头移到前一帧并停止
* @version Egret 2.4
* @platform Web,Native
*/
public prevFrame(): void {
this.gotoAndStop(this.$currentFrameNum - 1);
}
/**
* 跳到后一帧并停止
* @version Egret 2.4
* @platform Web,Native
*/
public nextFrame(): void {
this.gotoAndStop(this.$currentFrameNum + 1);
}
/**
* 将播放头移到指定帧并播放
* @param frame {any} 指定帧的帧号或帧标签
* @param playTimes {number} 播放次数。 参数为整数,可选参数,>=1:设定播放次数,<0:循环播放,默认值 0:不改变播放次数,
* @version Egret 2.4
* @platform Web,Native
*/
public gotoAndPlay(frame: string | number, playTimes: number = 0): void {
if (arguments.length == 0 || arguments.length > 2) {
egret.$error(1022, "MovieClip.gotoAndPlay()");
}
if (typeof frame === "string") {
this.getFrameStartEnd(frame);
} else {
this.$frameLabelStart = 0;
this.$frameLabelEnd = 0;
}
this.play(playTimes);
this.gotoFrame(frame);
}
/**
* 将播放头移到指定帧并停止
* @param frame {any} 指定帧的帧号或帧标签
* @version Egret 2.4
* @platform Web,Native
*/
public gotoAndStop(frame: string | number): void {
if (arguments.length != 1) {
egret.$error(1022, "MovieClip.gotoAndStop()");
}
this.stop();
this.gotoFrame(frame);
}
/**
* @private
*
* @param frame
*/
private gotoFrame(frame: string | number): void {
let frameNum: number;
if (typeof frame === "string") {
frameNum = this.getFrameLabelByName(frame).frame;
} else {
frameNum = parseInt(frame + '', 10);
if (<any>frameNum != frame) {
egret.$error(1022, "Frame Label Not Found");
}
}
if (frameNum < 1) {
frameNum = 1;
} else if (frameNum > this.$totalFrames) {
frameNum = this.$totalFrames;
}
if (frameNum == this.$nextFrameNum) {
return;
}
this.$nextFrameNum = frameNum;
this.advanceFrame();
this.constructFrame();
this.handlePendingEvent();
}
/**
* @private
*/
private lastTime: number = 0;
/**
* @private
*
* @param advancedTime
* @returns
*/
private advanceTime(timeStamp: number): boolean {
let self = this;
let advancedTime: number = timeStamp - self.lastTime;
self.lastTime = timeStamp;
let frameIntervalTime: number = self.frameIntervalTime;
let currentTime = self.passedTime + advancedTime;
self.passedTime = currentTime % frameIntervalTime;
let num: number = currentTime / frameIntervalTime;
if (num < 1) {
return false;
}
while (num >= 1) {
num--;
self.$nextFrameNum++;
if (self.$nextFrameNum > self.$totalFrames || (self.$frameLabelStart > 0 && self.$nextFrameNum > self.$frameLabelEnd)) {
if (self.playTimes == -1) {
self.$eventPool.push(Event.LOOP_COMPLETE);
self.$nextFrameNum = 1;
}
else {
self.playTimes--;
if (self.playTimes > 0) {
self.$eventPool.push(Event.LOOP_COMPLETE);
self.$nextFrameNum = 1;
}
else {
self.$nextFrameNum = self.$totalFrames;
self.$eventPool.push(Event.COMPLETE);
self.stop();
break;
}
}
}
if (self.$currentFrameNum == self.$frameLabelEnd) {
self.$nextFrameNum = self.$frameLabelStart;
}
self.advanceFrame();
}
self.constructFrame();
self.handlePendingEvent();
return false;
}
/**
* @private
*
*/
private advanceFrame(): void {
this.$currentFrameNum = this.$nextFrameNum;
let event = this.frameEvents[this.$nextFrameNum];
if (event && event != "") {
MovieClipEvent.dispatchMovieClipEvent(this, MovieClipEvent.FRAME_LABEL, event);
}
}
/**
* @private
*
*/
private constructFrame() {
let self = this;
let currentFrameNum: number = self.$currentFrameNum;
if (self.displayedKeyFrameNum == currentFrameNum) {
return;
}
self.$bitmapData = self.$movieClipData.getTextureByFrame(currentFrameNum);
self.$movieClipData.$getOffsetByFrame(currentFrameNum, self.offsetPoint);
self.displayedKeyFrameNum = currentFrameNum;
self.$renderDirty = true;
let p = self.$parent;
if (p && !p.$cacheDirty) {
p.$cacheDirty = true;
p.$cacheDirtyUp();
}
let maskedObject = self.$maskedObject;
if (maskedObject && !maskedObject.$cacheDirty) {
maskedObject.$cacheDirty = true;
maskedObject.$cacheDirtyUp();
}
}
/**
* @private
*
*/
public $renderFrame(): void {
let self = this;
self.$bitmapData = self.$movieClipData.getTextureByFrame(self.$currentFrameNum);
self.$renderDirty = true;
let p = self.$parent;
if (p && !p.$cacheDirty) {
p.$cacheDirty = true;
p.$cacheDirtyUp();
}
let maskedObject = self.$maskedObject;
if (maskedObject && !maskedObject.$cacheDirty) {
maskedObject.$cacheDirty = true;
maskedObject.$cacheDirtyUp();
}
}
/**
* @private
*
*/
private handlePendingEvent(): void {
if (this.$eventPool.length != 0) {
this.$eventPool.reverse();
let eventPool = this.$eventPool;
let length = eventPool.length;
let isComplete = false;
let isLoopComplete = false;
for (let i = 0; i < length; i++) {
let event: string = eventPool.pop();
if (event == Event.LOOP_COMPLETE) {
isLoopComplete = true;
} else if (event == Event.COMPLETE) {
isComplete = true;
} else {
this.dispatchEventWith(event);
}
}
if (isLoopComplete) {
this.dispatchEventWith(Event.LOOP_COMPLETE);
}
if (isComplete) {
this.dispatchEventWith(Event.COMPLETE);
}
}
}
//Properties
/**
* MovieClip 实例中帧的总数
* @version Egret 2.4
* @platform Web,Native
*/
public get totalFrames(): number {
return this.$totalFrames;
}
/**
* MovieClip 实例当前播放的帧的序号
* @version Egret 2.4
* @platform Web,Native
*/
public get currentFrame(): number {
return this.$currentFrameNum;
}
/**
* MovieClip 实例当前播放的帧的标签。如果当前帧没有标签,则 currentFrameLabel返回null。
* @version Egret 2.4
* @platform Web,Native
*/
public get currentFrameLabel(): string {
let label = this.getFrameLabelByFrame(this.$currentFrameNum);
return label && label.name;
}
/**
* 当前播放的帧对应的标签,如果当前帧没有标签,则currentLabel返回包含标签的先前帧的标签。如果当前帧和先前帧都不包含标签,currentLabel返回null。
* @version Egret 2.4
* @platform Web,Native
*/
public get currentLabel(): string {
let label: FrameLabel = this.getFrameLabelForFrame(this.$currentFrameNum);
return label ? label.name : null;
}
/**
* MovieClip 实例的帧频
* @version Egret 2.4
* @platform Web,Native
*/
public get frameRate(): number {
return this.$frameRate;
}
public set frameRate(value: number) {
if (value == this.$frameRate) {
return;
}
this.$frameRate = value;
this.frameIntervalTime = 1000 / this.$frameRate;
}
/**
* MovieClip 实例当前是否正在播放
* @version Egret 2.4
* @platform Web,Native
*/
public get isPlaying(): boolean {
return this.$isPlaying;
}
/**
* MovieClip数据源
*/
public set movieClipData(value: MovieClipData) {
this.setMovieClipData(value);
}
/**
* @version Egret 2.4
* @platform Web,Native
*/
public get movieClipData(): MovieClipData {
return this.$movieClipData;
}
/**
* @private
*
* @param value
*/
private setMovieClipData(value: MovieClipData) {
if (this.$movieClipData == value) {
return;
}
this.$movieClipData = value;
this.$init();
}
/**
* @private
*
* @param value
*/
private setPlayTimes(value: number) {
if (value < 0 || value >= 1) {
this.playTimes = value < 0 ? -1 : Math.floor(value);
}
}
/**
* @private
*
* @param value
*/
private setIsStopped(value: boolean) {
if (this.isStopped == value) {
return;
}
this.isStopped = value;
if (value) {
ticker.$stopTick(this.advanceTime, this);
} else {
this.playTimes = this.playTimes == 0 ? 1 : this.playTimes;
this.lastTime = egret.getTimer();
ticker.$startTick(this.advanceTime, this);
}
}
}
}