1
+ // spineHandlers.ts
1
2
import { WebGALPixiContainer } from '@/Core/controller/stage/pixi/WebGALPixiContainer' ;
2
3
import { v4 as uuid } from 'uuid' ;
3
- import 'pixi-spine' ; // Do this once at the very start of your code. This registers the loader!
4
- import { Spine } from 'pixi-spine' ;
5
4
import * as PIXI from 'pixi.js' ;
6
5
import PixiStage from '@/Core/controller/stage/pixi/PixiController' ;
7
6
import { logger } from '@/Core/util/logger' ;
7
+ // utils/loadPixiSpine.ts
8
+ // @ts -ignore
9
+ let pixiSpineModule : typeof import ( 'pixi-spine' ) | null = null ;
10
+ // @ts -ignore
11
+ let pixiSpineLoading : Promise < typeof import ( 'pixi-spine' ) | null > | null = null ;
8
12
13
+ let spineLoader : undefined | PIXI . Loader ;
14
+
15
+ /**
16
+ * 动态加载 'pixi-spine' 模块,并缓存结果
17
+ * @returns {Promise<typeof import('pixi-spine') | null> }
18
+ */
19
+ // @ts -ignore
20
+ export async function loadPixiSpine ( ) : Promise < typeof import ( 'pixi-spine' ) | null > {
21
+ if ( pixiSpineModule ) {
22
+ return pixiSpineModule ;
23
+ }
24
+
25
+ if ( pixiSpineLoading ) {
26
+ return pixiSpineLoading ;
27
+ }
28
+
29
+ // @ts -ignore
30
+ // pixiSpineLoading = import('pixi-spine')
31
+ // .then((module) => {
32
+ // spineLoader = new PIXI.Loader();
33
+ // pixiSpineModule = module;
34
+ // return module;
35
+ // })
36
+ // .catch((error) => {
37
+ // console.error('Failed to load pixi-spine. Spine features will be disabled.', error);
38
+ // return null;
39
+ // })
40
+ // .finally(() => {
41
+ // pixiSpineLoading = null;
42
+ // });
43
+
44
+ return pixiSpineLoading ;
45
+ }
46
+
47
+ /**
48
+ * 添加 Spine 立绘的实现函数
49
+ * @param key 立绘的标识
50
+ * @param url Spine 数据的 URL
51
+ * @param presetPosition 预设位置
52
+ */
9
53
// eslint-disable-next-line max-params
10
- export function addSpineFigureImpl (
54
+ export async function addSpineFigureImpl (
11
55
this : PixiStage ,
12
56
key : string ,
13
57
url : string ,
14
58
presetPosition : 'left' | 'center' | 'right' = 'center' ,
15
59
) {
16
- console . log ( key , url , presetPosition ) ;
17
60
const spineId = `spine-${ url } ` ;
18
- const loader = this . assetLoader ;
61
+ const pixiSpine = await loadPixiSpine ( ) ;
19
62
// 准备用于存放这个立绘的 Container
20
63
const thisFigureContainer = new WebGALPixiContainer ( ) ;
21
64
@@ -42,73 +85,84 @@ export function addSpineFigureImpl(
42
85
key : key ,
43
86
pixiContainer : thisFigureContainer ,
44
87
sourceUrl : url ,
45
- sourceType : 'live2d' ,
88
+ sourceType : 'spine' , // 修改为 'spine'
46
89
sourceExt : this . getExtName ( url ) ,
47
90
} ) ;
48
91
49
92
// 完成图片加载后执行的函数
50
- const setup = ( ) => {
51
- const spineResource : any = this . assetLoader . resources ?. [ spineId ] ;
52
- // TODO:找一个更好的解法,现在的解法是无论是否复用原来的资源,都设置一个延时以让动画工作正常!
53
- setTimeout ( ( ) => {
54
- if ( spineResource && this . getStageObjByUuid ( figureUuid ) ) {
55
- const figureSpine = new Spine ( spineResource . spineData ) ;
56
- const spineBounds = figureSpine . getLocalBounds ( ) ;
57
- const spineCenterX = spineBounds . x + spineBounds . width / 2 ;
58
- const spineCenterY = spineBounds . y + spineBounds . height / 2 ;
59
- figureSpine . pivot . set ( spineCenterX , spineCenterY ) ;
60
- // TODO: set animation 还没做
61
- // figureSpine.state.setAnimation()
62
- /**
63
- * 重设大小
64
- */
65
- const originalWidth = figureSpine . width ;
66
- const originalHeight = figureSpine . height ;
67
- const scaleX = this . stageWidth / originalWidth ;
68
- const scaleY = this . stageHeight / originalHeight ;
69
- const targetScale = Math . min ( scaleX , scaleY ) ;
70
- const figureSprite = new PIXI . Sprite ( ) ;
71
- figureSprite . addChild ( figureSpine ) ;
72
- figureSprite . scale . x = targetScale ;
73
- figureSprite . scale . y = targetScale ;
74
- figureSprite . anchor . set ( 0.5 ) ;
75
- figureSprite . position . y = this . stageHeight / 2 ;
76
- const targetWidth = originalWidth * targetScale ;
77
- const targetHeight = originalHeight * targetScale ;
78
- thisFigureContainer . setBaseY ( this . stageHeight / 2 ) ;
79
- if ( targetHeight < this . stageHeight ) {
80
- thisFigureContainer . setBaseY ( this . stageHeight / 2 + this . stageHeight - targetHeight / 2 ) ;
81
- }
82
- if ( presetPosition === 'center' ) {
83
- thisFigureContainer . setBaseX ( this . stageWidth / 2 ) ;
84
- }
85
- if ( presetPosition === 'left' ) {
86
- thisFigureContainer . setBaseX ( targetWidth / 2 ) ;
87
- }
88
- if ( presetPosition === 'right' ) {
89
- thisFigureContainer . setBaseX ( this . stageWidth - targetWidth / 2 ) ;
90
- }
91
- thisFigureContainer . pivot . set ( 0 , this . stageHeight / 2 ) ;
92
- thisFigureContainer . addChild ( figureSprite ) ;
93
+ const setup = async ( ) => {
94
+ if ( ! pixiSpine ) {
95
+ // 无法加载 'pixi-spine',跳过 Spine 相关逻辑
96
+ logger . warn ( `Spine module not loaded. Skipping Spine figure: ${ key } ` ) ;
97
+ return ;
98
+ }
99
+
100
+ const { Spine } = pixiSpine ;
101
+ const spineResource : any = spineLoader ! . resources ?. [ spineId ] ;
102
+ if ( spineResource && this . getStageObjByUuid ( figureUuid ) ) {
103
+ const figureSpine = new Spine ( spineResource . spineData ) ;
104
+ const spineBounds = figureSpine . getLocalBounds ( ) ;
105
+ const spineCenterX = spineBounds . x + spineBounds . width / 2 ;
106
+ const spineCenterY = spineBounds . y + spineBounds . height / 2 ;
107
+ figureSpine . pivot . set ( spineCenterX , spineCenterY ) ;
108
+ // TODO: set animation 还没做
109
+ // figureSpine.state.setAnimation(0, figureSpine.spineData.animations[0].name, true)
110
+
111
+ /**
112
+ * 重设大小
113
+ */
114
+ const originalWidth = figureSpine . width ;
115
+ const originalHeight = figureSpine . height ;
116
+ const scaleX = this . stageWidth / originalWidth ;
117
+ const scaleY = this . stageHeight / originalHeight ;
118
+ const targetScale = Math . min ( scaleX , scaleY ) ;
119
+ const figureSprite = new PIXI . Sprite ( ) ;
120
+ figureSprite . addChild ( figureSpine ) ;
121
+ figureSprite . scale . x = targetScale ;
122
+ figureSprite . scale . y = targetScale ;
123
+ figureSprite . anchor . set ( 0.5 ) ;
124
+ figureSprite . position . y = this . stageHeight / 2 ;
125
+ const targetWidth = originalWidth * targetScale ;
126
+ const targetHeight = originalHeight * targetScale ;
127
+ thisFigureContainer . setBaseY ( this . stageHeight / 2 ) ;
128
+ if ( targetHeight < this . stageHeight ) {
129
+ thisFigureContainer . setBaseY ( this . stageHeight / 2 + this . stageHeight - targetHeight / 2 ) ;
93
130
}
94
- } , 0 ) ;
131
+ if ( presetPosition === 'center' ) {
132
+ thisFigureContainer . setBaseX ( this . stageWidth / 2 ) ;
133
+ }
134
+ if ( presetPosition === 'left' ) {
135
+ thisFigureContainer . setBaseX ( targetWidth / 2 ) ;
136
+ }
137
+ if ( presetPosition === 'right' ) {
138
+ thisFigureContainer . setBaseX ( this . stageWidth - targetWidth / 2 ) ;
139
+ }
140
+ thisFigureContainer . pivot . set ( 0 , this . stageHeight / 2 ) ;
141
+ thisFigureContainer . addChild ( figureSprite ) ;
142
+ }
95
143
} ;
96
144
97
145
/**
98
146
* 加载器部分
147
+ * 这里不再使用 this.loadAsset,因为我们可能需要单独管理 Spine 资源
148
+ * 但为了避免性能问题,我们继续使用现有的 loader,并确保资源只加载一次
99
149
*/
100
150
this . cacheGC ( ) ;
101
- if ( ! loader . resources ?. [ url ] ) {
102
- this . loadAsset ( url , setup , spineId ) ;
151
+ if ( ! spineLoader ! . resources ?. [ spineId ] ) {
152
+ spineLoader ! . add ( spineId , url ) . load ( setup ) ;
103
153
} else {
104
154
// 复用
105
- setup ( ) ;
155
+ await setup ( ) ;
106
156
}
107
157
}
108
158
109
- export function addSpineBgImpl ( this : PixiStage , key : string , url : string ) {
159
+ /**
160
+ * 添加 Spine 背景的实现函数
161
+ * @param key 背景的标识
162
+ * @param url Spine 数据的 URL
163
+ */
164
+ export async function addSpineBgImpl ( this : PixiStage , key : string , url : string ) {
110
165
const spineId = `spine-${ url } ` ;
111
- const loader = this . assetLoader ;
112
166
// 准备用于存放这个背景的 Container
113
167
const thisBgContainer = new WebGALPixiContainer ( ) ;
114
168
@@ -130,13 +184,21 @@ export function addSpineBgImpl(this: PixiStage, key: string, url: string) {
130
184
key : key ,
131
185
pixiContainer : thisBgContainer ,
132
186
sourceUrl : url ,
133
- sourceType : 'live2d' ,
187
+ sourceType : 'spine' , // 修改为 'spine'
134
188
sourceExt : this . getExtName ( url ) ,
135
189
} ) ;
136
190
137
191
// 完成图片加载后执行的函数
138
- const setup = ( ) => {
139
- const spineResource : any = this . assetLoader . resources ?. [ spineId ] ;
192
+ const setup = async ( ) => {
193
+ const pixiSpine = await loadPixiSpine ( ) ;
194
+ if ( ! pixiSpine ) {
195
+ // 无法加载 'pixi-spine',跳过 Spine 相关逻辑
196
+ logger . warn ( `Spine module not loaded. Skipping Spine background: ${ key } ` ) ;
197
+ return ;
198
+ }
199
+
200
+ const { Spine } = pixiSpine ;
201
+ const spineResource : any = spineLoader ! . resources ?. [ spineId ] ;
140
202
// TODO:找一个更好的解法,现在的解法是无论是否复用原来的资源,都设置一个延时以让动画工作正常!
141
203
setTimeout ( ( ) => {
142
204
if ( spineResource && this . getStageObjByUuid ( bgUuid ) ) {
@@ -174,12 +236,14 @@ export function addSpineBgImpl(this: PixiStage, key: string, url: string) {
174
236
175
237
/**
176
238
* 加载器部分
239
+ * 这里不再使用 this.loadAsset,因为我们可能需要单独管理 Spine 资源
240
+ * 但为了避免性能问题,我们继续使用现有的 loader,并确保资源只加载一次
177
241
*/
178
242
this . cacheGC ( ) ;
179
- if ( ! loader . resources ?. [ url ] ) {
180
- this . loadAsset ( url , setup , spineId ) ;
243
+ if ( ! spineLoader ! . resources ?. [ spineId ] ) {
244
+ spineLoader ! . add ( spineId , url ) . load ( setup ) ;
181
245
} else {
182
246
// 复用
183
- setup ( ) ;
247
+ await setup ( ) ;
184
248
}
185
249
}
0 commit comments