├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── example
├── favicon.ico
└── index.html
├── lib
├── egret-spine.d.ts
├── egret-spine.js
└── egret-spine.js.map
├── src
├── EventEmitter.ts
├── SkeletonAnimation.ts
├── SkeletonRenderer.ts
├── Track.ts
└── egret.d.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 |
10 | pids
11 | logs
12 | results
13 |
14 | npm-debug.log
15 | node_modules
16 | built
17 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "spine-runtimes"]
2 | path = spine-runtimes
3 | url = https://github.com/EsotericSoftware/spine-runtimes.git
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Spine Runtimes License Agreement
2 | Last updated May 1, 2019. Replaces all prior versions.
3 |
4 | Copyright (c) 2013-2019, Esoteric Software LLC
5 |
6 | Integration of the Spine Runtimes into software or otherwise creating
7 | derivative works of the Spine Runtimes is permitted under the terms and
8 | conditions of Section 2 of the Spine Editor License Agreement:
9 | http://esotericsoftware.com/spine-editor-license
10 |
11 | Otherwise, it is permitted to integrate the Spine Runtimes into software
12 | or otherwise create derivative works of the Spine Runtimes (collectively,
13 | "Products"), provided that each user of the Products must obtain their own
14 | Spine Editor license and redistribution of the Products in any form must
15 | include this license and copyright notice.
16 |
17 | THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
18 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
20 | NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
23 | INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
26 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # egret-spine
2 | A spine runtime for Egret (Demo: https://fightingcat.github.io/egret-spine/example in progress...).
3 |
4 | ## Installing
5 | Considering that Egret doesn't use module, neither does official spine core runtime, this runtime doesn't either, for now.
6 |
7 | See [Egret document](http://developer.egret.com/cn/github/egret-docs/Engine2D/projectConfig/libraryProject/index.html) for integrating to Egret project.
8 |
9 | Or just include the .js file in HTML, if you only intend to build for web, like:
10 | ```HTML
11 |
12 | ```
13 |
14 | ## Getting started
15 | This implementation doesn't involve resource downloading, your actual code may varies from this boilerplate.
16 |
17 | ```typescript
18 | function loadImage(url: string): Promise {
19 | return new Promise(resolve => {
20 | let loader = new egret.ImageLoader();
21 | let texture = new egret.Texture();
22 |
23 | loader.once(egret.Event.COMPLETE, () => {
24 | texture.bitmapData = loader.data;
25 | resolve(texture);
26 | }, null);
27 |
28 | loader.load(url);
29 | });
30 | }
31 |
32 | function loadText(url: string): Promise {
33 | return new Promise(resolve => {
34 | let request = new egret.HttpRequest();
35 | request.responseType = egret.HttpResponseType.TEXT;
36 | request.open(url, 'GET');
37 | request.send();
38 | request.once(egret.Event.COMPLETE, () => {
39 | resolve(request.response);
40 | }, null)
41 | });
42 | }
43 |
44 | async function runDemo() {
45 | let json = await loadText('assets/demo.json');
46 | let atlas = await loadText('assets/demo.atlas');
47 | let texAtlas = spine.createTextureAtlas(atlas, {
48 | "demo.png": await loadImage('assets/demo.png')
49 | });
50 | let skelData = spine.createSkeletonData(json, texAtlas);
51 |
52 | let animation = new spine.SkeletonAnimation(skelData);
53 |
54 | animation.play('animation');
55 | egret.lifecycle.stage.addChild(animation);
56 | }
57 |
58 | runDemo();
59 | ```
60 | [More example code](https://github.com/fightingcat/egret-spine/blob/master/example/src/Main.ts).
61 |
62 | ## Learn more
63 | Several classes or structures have been added, all be declared within namespace spine to minimize impact.
64 |
65 | + **createSkeletonData** Helper for creating skeleton data.
66 | + **createTextureAtlas** Helper for creating texture atlas.
67 | + **SkeletonAnimation** A user-friendly animation manager.
68 | + **SkeletonRenderer** A mere skeleton renderer
69 | + **SlotRenderer** Slot renderer for `SkeletonRenderer`.
70 | + **EventEmitter** Embbeded implemation of event emitter.
71 | + **SpineEvent** Enums of animation events.
72 | + **Track** Track abstraction for `SkeletonAnimation`.
73 |
--------------------------------------------------------------------------------
/example/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/YowaiCoder/egret-spine/f9ef79d28e2f283ef2c9d7a9f4de9cef231ee700/example/favicon.ico
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Egret Spine Demo
9 |
10 |
11 |
12 | in progress...
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/EventEmitter.ts:
--------------------------------------------------------------------------------
1 | namespace spine {
2 | type ListenerList = { fn: Function, ctx: unknown, once?: boolean }[];
3 |
4 | export class EventEmitter {
5 | private _listeners = new Map();
6 |
7 | public on(event: T, fn: Function, ctx?: unknown, once?: boolean): this {
8 | const listeners = this._listeners.get(event);
9 |
10 | if (listeners) {
11 | listeners.push({ fn, ctx, once });
12 | }
13 | else {
14 | this._listeners.set(event, [{ fn, ctx, once }]);
15 | }
16 | return this;
17 | }
18 |
19 | public once(event: T, fn: Function, ctx?: unknown): this {
20 | const listeners = this._listeners.get(event);
21 |
22 | if (listeners) {
23 | listeners.push({ fn, ctx, once: true });
24 | }
25 | else {
26 | this._listeners.set(event, [{ fn, ctx, once: true }]);
27 | }
28 | return this;
29 | }
30 |
31 | public off(event: T, fn?: Function, ctx?: unknown, once?: boolean): this {
32 | const listeners = this._listeners.get(event);
33 |
34 | if (arguments.length === 0) {
35 | this._listeners.clear();
36 | }
37 | else if (arguments.length === 1) {
38 | this._listeners.delete(event);
39 | }
40 | else if (listeners) {
41 | if (arguments.length === 2) {
42 | for (let i = 0; i < listeners.length; i++) {
43 | if (fn === listeners[i].fn) {
44 | listeners.splice(i--, 1);
45 | }
46 | }
47 | }
48 | else if (arguments.length === 3) {
49 | for (let i = 0; i < listeners.length; i++) {
50 | const it = listeners[i];
51 |
52 | if (fn === it.fn && ctx === it.ctx) {
53 | listeners.splice(i--, 1);
54 | }
55 | }
56 | }
57 | else {
58 | for (let i = 0; i < listeners.length; i++) {
59 | const it = listeners[i];
60 |
61 | if (fn === it.fn && ctx === it.ctx && (!once || it.once)) {
62 | listeners.splice(i--, 1);
63 | }
64 | }
65 | }
66 | }
67 | return this;
68 | }
69 |
70 | public emit(event: T, a?: unknown, b?: unknown, c?: unknown): this {
71 | const listeners = this._listeners.get(event);
72 |
73 | if (listeners) {
74 | if (arguments.length === 1) {
75 | for (let i = 0; i < listeners.length; i++) {
76 | const it = listeners[i];
77 |
78 | if (it.once) listeners.splice(i--, 1);
79 | it.fn.call(it.ctx);
80 | }
81 | }
82 | else if (arguments.length === 2) {
83 | for (let i = 0; i < listeners.length; i++) {
84 | const it = listeners[i];
85 |
86 | if (it.once) listeners.splice(i--, 1);
87 | it.fn.call(it.ctx, a);
88 | }
89 | }
90 | else if (arguments.length === 3) {
91 | for (let i = 0; i < listeners.length; i++) {
92 | const it = listeners[i];
93 |
94 | if (it.once) listeners.splice(i--, 1);
95 | it.fn.call(it.ctx, a, b);
96 | }
97 | }
98 | else if (arguments.length === 4) {
99 | for (let i = 0; i < listeners.length; i++) {
100 | const it = listeners[i];
101 |
102 | if (it.once) listeners.splice(i--, 1);
103 | it.fn.call(it.ctx, a, b, c);
104 | }
105 | }
106 | else {
107 | const args = Array.prototype.slice.call(arguments, 1);
108 |
109 | for (let i = 0; i < listeners.length; i++) {
110 | const it = listeners[i];
111 |
112 | if (it.once) listeners.splice(i--, 1);
113 | it.fn.apply(it.ctx, args);
114 | }
115 | }
116 | }
117 | return this;
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/src/SkeletonAnimation.ts:
--------------------------------------------------------------------------------
1 | namespace spine {
2 | export class SkeletonAnimation extends egret.DisplayObjectContainer {
3 | public readonly renderer: SkeletonRenderer;
4 | public readonly state: AnimationState;
5 | public readonly stateData: AnimationStateData;
6 | public readonly skeleton: Skeleton;
7 | public readonly skeletonData: SkeletonData;
8 | private lastTime: number = 0;
9 |
10 | public constructor(skeletonData: SkeletonData) {
11 | super();
12 | this.renderer = new SkeletonRenderer(skeletonData);
13 | this.state = this.renderer.state;
14 | this.stateData = this.renderer.stateData;
15 | this.skeleton = this.renderer.skeleton;
16 | this.skeletonData = this.renderer.skeletonData;
17 | this.addChild(this.renderer);
18 | this.addEventListener(egret.Event.ADDED_TO_STAGE, this.onAddedToStage, this);
19 | }
20 |
21 | public get flipX(): boolean {
22 | return this.renderer.scaleX == -1;
23 | }
24 |
25 | public set flipX(flip: boolean) {
26 | this.renderer.scaleX = flip ? -1 : 1;
27 | }
28 |
29 | public get flipY(): boolean {
30 | return this.renderer.scaleY == 1;
31 | }
32 |
33 | public set flipY(flip: boolean) {
34 | this.renderer.scaleY = flip ? 1 : -1;
35 | }
36 |
37 | public setTimeScale(scale: number) {
38 | this.state.timeScale = scale;
39 | }
40 |
41 | public play(anim: string, loop = 0, trackID = 0): Track {
42 | return this.start(trackID).add(anim, loop);
43 | }
44 |
45 | public start(trackID = 0): Track {
46 | this.skeleton.setToSetupPose();
47 | return new Track(this, trackID);
48 | }
49 |
50 | public stop(track: number) {
51 | this.state.clearTrack(track);
52 | }
53 |
54 | public stopAll(reset?: boolean) {
55 | this.state.clearTracks();
56 | if (reset) this.skeleton.setToSetupPose();
57 | }
58 |
59 | private onAddedToStage() {
60 | this.lastTime = Date.now() / 1000;
61 | this.addEventListener(egret.Event.ENTER_FRAME, this.onEnterFrame, this);
62 | this.addEventListener(egret.Event.REMOVED_FROM_STAGE, this.onRemovedFromStage, this);
63 | }
64 |
65 | private onRemovedFromStage() {
66 | this.removeEventListener(egret.Event.ENTER_FRAME, this.onEnterFrame, this);
67 | this.removeEventListener(egret.Event.REMOVED_FROM_STAGE, this.onRemovedFromStage, this);
68 | }
69 |
70 | private onEnterFrame() {
71 | const now = Date.now() / 1000;
72 | this.renderer.update(now - this.lastTime);
73 | this.lastTime = now;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/SkeletonRenderer.ts:
--------------------------------------------------------------------------------
1 | namespace spine {
2 |
3 | const QuadIndices = [0, 1, 2, 2, 3, 0];
4 |
5 | class EgretTexture extends Texture {
6 | public smoothing: boolean = false;
7 |
8 | public constructor(readonly original: egret.Texture) {
9 | super(original.bitmapData.source);
10 | }
11 |
12 | public setFilters(minFilter: TextureFilter, magFilter: TextureFilter): void {
13 | const { Nearest, MipMapNearestNearest } = TextureFilter;
14 | const minSmoothing = minFilter !== Nearest && minFilter !== MipMapNearestNearest;
15 | const magSmoothing = magFilter !== Nearest && magFilter !== MipMapNearestNearest;
16 |
17 | this.smoothing = minSmoothing || magSmoothing;
18 | }
19 |
20 | public setWraps(uWrap: TextureWrap, vWrap: TextureWrap): void { }
21 |
22 | public dispose(): void { }
23 | }
24 |
25 | export function createSkeletonData(jsonData: string | {}, atlas: TextureAtlas) {
26 | const skelJson = new SkeletonJson(new AtlasAttachmentLoader(atlas));
27 | return skelJson.readSkeletonData(jsonData);
28 | }
29 |
30 | export function createTextureAtlas(atlasData: string, textures: Record) {
31 | return new TextureAtlas(atlasData, (file: string) => {
32 | return new EgretTexture(textures[file]);
33 | });
34 | }
35 |
36 | export class SkeletonRenderer extends egret.DisplayObjectContainer {
37 | public readonly skeleton: Skeleton;
38 | public readonly skeletonData: SkeletonData;
39 | public readonly state: AnimationState;
40 | public readonly stateData: AnimationStateData;
41 | public readonly slotRenderers: SlotRenderer[] = [];
42 |
43 | public constructor(skeletonData: SkeletonData) {
44 | super();
45 | this.scaleY = -1;
46 | this.touchEnabled = true;
47 | this.skeletonData = skeletonData;
48 | this.stateData = new AnimationStateData(skeletonData);
49 | this.state = new AnimationState(this.stateData);
50 | this.skeleton = new Skeleton(skeletonData);
51 | this.skeleton.updateWorldTransform();
52 | this.skeleton.setSlotsToSetupPose();
53 |
54 | for (const slot of this.skeleton.slots) {
55 | const renderer = new SlotRenderer(slot);
56 |
57 | renderer.renderSlot();
58 | this.addChild(renderer);
59 | this.slotRenderers.push(renderer);
60 | }
61 | }
62 |
63 | public findSlotRenderer(name: string): SlotRenderer | undefined {
64 | return this.slotRenderers.find(it => it.name === name);
65 | }
66 |
67 | public update(dt: number) {
68 | this.state.update(dt);
69 | this.state.apply(this.skeleton);
70 | this.skeleton.updateWorldTransform();
71 |
72 | const drawOrder = this.skeleton.drawOrder;
73 |
74 | for (let i = 0; i < drawOrder.length; i++) {
75 | const index = drawOrder[i].data.index;
76 | const renderer = this.slotRenderers[index];
77 |
78 | if (renderer.zIndex !== i) {
79 | renderer.zIndex = i;
80 | }
81 | renderer.renderSlot();
82 | }
83 | }
84 | }
85 |
86 | export class SlotRenderer extends egret.Mesh {
87 | public premultipliedAlpha: boolean = false;
88 | private attachment?: Attachment;
89 |
90 | public constructor(readonly slot: Slot) {
91 | super();
92 | this.name = slot.data.name;
93 | if (slot.data.blendMode === BlendMode.Additive) {
94 | this.blendMode = egret.BlendMode.ADD;
95 | }
96 | }
97 |
98 | public renderSlot() {
99 | const boneActive = this.slot.bone.active;
100 | const attachment = this.slot.attachment;
101 |
102 | if (boneActive && attachment) {
103 | if (attachment === this.attachment) return;
104 |
105 | if (attachment instanceof RegionAttachment) {
106 | this.updateColor(attachment.color);
107 | this.updateRegionAttachment(attachment);
108 | this.visible = true;
109 | return;
110 | }
111 | if (attachment instanceof MeshAttachment) {
112 | this.updateColor(attachment.color);
113 | this.updateMeshAttachment(attachment);
114 | this.visible = true;
115 | return;
116 | }
117 | // TODO: implement our own clipper
118 | // if (attachment instanceof ClippingAttachment) { }
119 | }
120 | // no supported attachment.
121 | this.visible = false;
122 | }
123 |
124 | // two color tinting is not supported (due to bad performance of CustomFilter)
125 | private updateColor(color: Color) {
126 | const premultipliedAlpha = this.premultipliedAlpha;
127 | const skelColor = this.slot.bone.skeleton.color;
128 | const slotColor = this.slot.color;
129 |
130 | const alpha = skelColor.a * slotColor.a * color.a;
131 | const scale = premultipliedAlpha ? 255 * alpha : 255;
132 | const r = skelColor.r * slotColor.r * color.r * scale;
133 | const g = skelColor.g * slotColor.g * color.g * scale;
134 | const b = skelColor.b * slotColor.b * color.b * scale;
135 |
136 | this.tint = (r << 16) + (g << 8) + (b | 0);
137 | this.alpha = premultipliedAlpha ? 1 : alpha;
138 | }
139 |
140 | private updateRegionAttachment(attachment: RegionAttachment) {
141 | const region = attachment.region as TextureAtlasRegion;
142 | const texture = region.texture as EgretTexture;
143 | const meshNode = this.$renderNode as egret.sys.MeshNode;
144 |
145 | meshNode.uvs = attachment.uvs as number[];
146 | meshNode.indices = QuadIndices;
147 | meshNode.vertices.length = 6;
148 | attachment.computeWorldVertices(this.slot.bone, meshNode.vertices, 0, 2);
149 | this.$updateVertices();
150 | this.texture = texture.original;
151 | this.smoothing = texture.smoothing;
152 | }
153 |
154 | private updateMeshAttachment(attachment: MeshAttachment) {
155 | const length = attachment.worldVerticesLength;
156 | const region = attachment.region as TextureAtlasRegion;
157 | const texture = region.texture as EgretTexture;
158 | const meshNode = this.$renderNode as egret.sys.MeshNode;
159 |
160 | meshNode.uvs = attachment.uvs as number[];
161 | meshNode.indices = attachment.triangles;
162 | meshNode.vertices.length = length;
163 | attachment.computeWorldVertices(this.slot, 0, length, meshNode.vertices, 0, 2);
164 | this.$updateVertices();
165 | this.texture = texture.original;
166 | this.smoothing = texture.smoothing;
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/src/Track.ts:
--------------------------------------------------------------------------------
1 | namespace spine {
2 | interface AnimationRecord {
3 | name: string;
4 | loop: number;
5 | listener?: AnimationListener;
6 | }
7 |
8 | export const enum SpineEvent {
9 | PlayStart,
10 | PlayEnd,
11 | LoopStart,
12 | LoopEnd,
13 | Interrupt,
14 | Custom,
15 | TrackEnd,
16 | }
17 |
18 | export interface AnimationListener {
19 | playStart?: () => void;
20 | playEnd?: () => void;
21 | loopStart?: () => void;
22 | loopEnd?: () => void;
23 | interrupt?: () => void;
24 | custom?: (event: Event) => void;
25 | }
26 |
27 | export class Track extends EventEmitter {
28 | public readonly trackID: number;
29 | public readonly skelAnimation: SkeletonAnimation;
30 | private stateListener: AnimationStateListener;
31 | private trackEntry: TrackEntry | undefined;
32 | private animations: AnimationRecord[] = [];
33 | private disposed: boolean = false;
34 | private loop: number = 0;
35 |
36 | public constructor(skelAnimation: SkeletonAnimation, trackID: number) {
37 | super();
38 | this.trackID = trackID;
39 | this.skelAnimation = skelAnimation;
40 | this.stateListener = {
41 | complete: () => this.onComplete(),
42 | interrupt: () => this.onInterrupt(),
43 | event: (_, event) => this.onCustomEvent(event),
44 | start: undefined!, end: undefined!, dispose: undefined!
45 | };
46 | }
47 |
48 | public waitPlayStart() {
49 | return new Promise(resolve => this.once(SpineEvent.PlayStart, resolve));
50 | }
51 |
52 | public waitPlayEnd() {
53 | return new Promise(resolve => this.once(SpineEvent.PlayEnd, resolve));
54 | }
55 |
56 | public waitLoopStart() {
57 | return new Promise(resolve => this.once(SpineEvent.LoopStart, resolve));
58 | }
59 |
60 | public waitLoopEnd() {
61 | return new Promise(resolve => this.once(SpineEvent.LoopEnd, resolve));
62 | }
63 |
64 | public waitInterrupt() {
65 | return new Promise(resolve => this.once(SpineEvent.Interrupt, resolve));
66 | }
67 |
68 | public waitTrackEnd() {
69 | return new Promise(resolve => this.once(SpineEvent.TrackEnd, resolve));
70 | }
71 |
72 | public waitEvent() {
73 | return new Promise(resolve => this.once(SpineEvent.Custom, resolve));
74 | }
75 |
76 | public waitNamedEvent(name: string) {
77 | return new Promise(resolve => {
78 | const callback = (event: Event) => {
79 | if (event.data.name == name) {
80 | this.off(SpineEvent.Custom, callback);
81 | resolve(event);
82 | }
83 | }
84 | this.on(SpineEvent.Custom, callback);
85 | });
86 | }
87 |
88 | public add(name: string, loop: number = 1, listener?: AnimationListener) {
89 | if (!this.disposed) {
90 | this.animations.push({ name, loop, listener });
91 | if (this.animations.length == 1) {
92 | this.playNextAnimation();
93 | }
94 | }
95 | return this;
96 | }
97 |
98 | private setAnimation(name: string, loop: boolean) {
99 | if (this.trackEntry) this.trackEntry.listener = undefined!;
100 | this.trackEntry = this.skelAnimation.state.setAnimation(this.trackID, name, loop)!;
101 | this.trackEntry.listener = this.stateListener;
102 | this.skelAnimation.renderer.update(0);
103 | }
104 |
105 | private playNextAnimation() {
106 | if (!this.disposed && this.animations.length > 0) {
107 | const { name, listener } = this.animations[0];
108 |
109 | if (listener) {
110 | if (listener.playStart) this.on(SpineEvent.PlayStart, listener.playStart, listener);
111 | if (listener.playEnd) this.on(SpineEvent.PlayEnd, listener.playEnd, listener);
112 | if (listener.loopStart) this.on(SpineEvent.LoopStart, listener.loopStart, listener);
113 | if (listener.loopEnd) this.on(SpineEvent.LoopEnd, listener.loopEnd, listener);
114 | if (listener.interrupt) this.on(SpineEvent.Interrupt, listener.interrupt, listener);
115 | if (listener.custom) this.on(SpineEvent.Custom, listener.custom, listener);
116 | }
117 | this.loop = 0;
118 | this.setAnimation(name, false);
119 | this.emit(SpineEvent.PlayStart);
120 | this.emit(SpineEvent.LoopStart);
121 | }
122 | }
123 |
124 | private onComplete() {
125 | if (!this.disposed) {
126 | const animation = this.animations[0];
127 |
128 | this.emit(SpineEvent.LoopEnd);
129 |
130 | if (++this.loop != animation.loop) {
131 | this.setAnimation(animation.name, false);
132 | this.emit(SpineEvent.LoopStart);
133 | }
134 | else {
135 | const listener = animation.listener;
136 |
137 | this.emit(SpineEvent.PlayEnd);
138 | this.animations.shift();
139 |
140 | if (listener) {
141 | this.off(SpineEvent.PlayStart, listener.playStart);
142 | this.off(SpineEvent.PlayEnd, listener.playEnd);
143 | this.off(SpineEvent.LoopStart, listener.loopStart);
144 | this.off(SpineEvent.LoopEnd, listener.loopEnd);
145 | this.off(SpineEvent.Interrupt, listener.interrupt);
146 | this.off(SpineEvent.Custom, listener.custom);
147 | }
148 | if (this.animations.length > 0) {
149 | this.playNextAnimation();
150 | }
151 | else {
152 | this.trackEntry!.listener = undefined!;
153 | this.trackEntry = undefined;
154 | this.disposed = true;
155 | this.emit(SpineEvent.TrackEnd);
156 | }
157 | }
158 | }
159 | }
160 |
161 | private onInterrupt() {
162 | if (!this.disposed) {
163 | this.disposed = true;
164 | this.animations.length = 0;
165 | this.emit(SpineEvent.Interrupt);
166 | }
167 | }
168 |
169 | private onCustomEvent(event: Event) {
170 | if (!this.disposed) {
171 | this.emit(SpineEvent.Custom, event);
172 | }
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "System",
4 | "target": "ES6",
5 | "strict": false,
6 | "declaration": true,
7 | "sourceMap": true,
8 | "inlineSources": true,
9 | "outFile": "lib/egret-spine.js",
10 | },
11 | "include": [
12 | "spine-runtimes/spine-ts/core",
13 | "src",
14 | ]
15 | }
--------------------------------------------------------------------------------