@@ -88,12 +88,12 @@ export default class PixiStage {
8888 public frameDuration = 16.67 ;
8989 public notUpdateBacklogEffects = false ;
9090 public readonly figureContainer : PIXI . Container ;
91- public figureObjects : Array < IStageObject > = [ ] ;
91+ public figureObjects = this . createReactiveList < IStageObject > ( [ ] ) ;
9292 public stageWidth = SCREEN_CONSTANTS . width ;
9393 public stageHeight = SCREEN_CONSTANTS . height ;
9494 public assetLoader = new PIXI . Loader ( ) ;
9595 public readonly backgroundContainer : PIXI . Container ;
96- public backgroundObjects : Array < IStageObject > = [ ] ;
96+ public backgroundObjects = this . createReactiveList < IStageObject > ( [ ] ) ;
9797 public mainStageObject : IStageObject ;
9898 /**
9999 * 添加 Spine 立绘
@@ -104,11 +104,15 @@ export default class PixiStage {
104104 public addSpineFigure = addSpineFigureImpl . bind ( this ) ;
105105 public addSpineBg = addSpineBgImpl . bind ( this ) ;
106106 // 注册到 Ticker 上的函数
107- private stageAnimations : Array < IStageAnimationObject > = [ ] ;
107+ private stageAnimations = this . createReactiveList < IStageAnimationObject > ( [ ] ) ;
108108 private loadQueue : { url : string ; callback : ( ) => void ; name ?: string } [ ] = [ ] ;
109109 private live2dFigureRecorder : Array < ILive2DRecord > = [ ] ;
110110 // 锁定变换对象(对象可能正在执行动画,不能应用变换)
111111 private lockTransformTarget : Array < string > = [ ] ;
112+ // 手动请求渲染防抖标记
113+ private isRenderPending = false ;
114+ // 更新 ticker 状态的防抖标记
115+ private isTickerUpdatePending = false ;
112116
113117 /**
114118 * 暂时没用上,以后可能用
@@ -121,6 +125,7 @@ export default class PixiStage {
121125 const app = new PIXI . Application ( {
122126 backgroundAlpha : 0 ,
123127 preserveDrawingBuffer : true ,
128+ autoStart : false ,
124129 } ) ;
125130 // @ts -ignore
126131
@@ -194,7 +199,22 @@ export default class PixiStage {
194199 this . callLoader ( ) ;
195200 } ;
196201 reload ( ) ;
197- this . initialize ( ) . then ( ( ) => { } ) ;
202+ this . initialize ( ) . then ( ( ) => { } ) ;
203+ this . requestRender ( ) ;
204+ }
205+
206+ public requestRender ( ) {
207+ if ( this . isRenderPending ) return ;
208+ this . isRenderPending = true ;
209+
210+ Promise . resolve ( ) . then ( ( ) => {
211+ requestAnimationFrame ( ( ) => {
212+ this . isRenderPending = false ;
213+ if ( ! this . currentApp ?. ticker . started ) {
214+ this . currentApp ?. render ( ) ;
215+ }
216+ } ) ;
217+ } ) ;
198218 }
199219
200220 public getFigureObjects ( ) {
@@ -346,6 +366,7 @@ export default class PixiStage {
346366 return ;
347367 }
348368 sprite . texture = texture ;
369+ this . requestRender ( ) ;
349370 } ) ;
350371 }
351372
@@ -374,6 +395,7 @@ export default class PixiStage {
374395 return ;
375396 }
376397 sprite . texture = texture ;
398+ this . requestRender ( ) ;
377399 } ) ;
378400 }
379401
@@ -436,6 +458,7 @@ export default class PixiStage {
436458
437459 // 挂载
438460 thisBgContainer . addChild ( bgSprite ) ;
461+ this . requestRender ( ) ;
439462 }
440463 } , 0 ) ;
441464 } ;
@@ -610,6 +633,7 @@ export default class PixiStage {
610633 }
611634 thisFigureContainer . pivot . set ( 0 , this . stageHeight / 2 ) ;
612635 thisFigureContainer . addChild ( figureSprite ) ;
636+ this . requestRender ( ) ;
613637 }
614638 } , 0 ) ;
615639 } ;
@@ -1100,6 +1124,60 @@ export default class PixiStage {
11001124 console . error ( 'Failed to load figureCash:' , error ) ;
11011125 }
11021126 }
1127+
1128+ private createReactiveList < T extends object > ( array : T [ ] ) : T [ ] {
1129+ return new Proxy ( array , {
1130+ // eslint-disable-next-line max-params
1131+ set : ( target , property , value , receiver ) => {
1132+ const result = Reflect . set ( target , property , value , receiver ) ;
1133+ if ( property !== 'length' ) {
1134+ this . updateTickerStatus ( ) ;
1135+ } else {
1136+ this . updateTickerStatus ( ) ;
1137+ }
1138+ return result ;
1139+ } ,
1140+ deleteProperty : ( target , property ) => {
1141+ const result = Reflect . deleteProperty ( target , property ) ;
1142+ this . updateTickerStatus ( ) ;
1143+ return result ;
1144+ } ,
1145+ } ) ;
1146+ }
1147+
1148+ private updateTickerStatus ( ) {
1149+ if ( this . isTickerUpdatePending ) return ;
1150+ this . isTickerUpdatePending = true ;
1151+
1152+ Promise . resolve ( ) . then ( ( ) => {
1153+ this . isTickerUpdatePending = false ;
1154+ const app = this . currentApp ;
1155+ if ( ! app ) return ;
1156+
1157+ const hasActiveAnimations = this . stageAnimations . length > 0 ;
1158+ const hasLive2D = this . figureObjects . some ( ( fig ) => fig . sourceType === 'live2d' ) ;
1159+ const hasSpine = this . figureObjects . some ( ( fig ) => fig . sourceType === 'spine' ) ;
1160+ const hasDynamicBg = this . backgroundObjects . some ( ( bg ) => bg . sourceType === 'video' || bg . sourceType === 'gif' ) ;
1161+ const hasGifFigure = this . figureObjects . some ( ( fig ) => fig . sourceType === 'gif' ) ;
1162+
1163+ const shouldRun = hasActiveAnimations || hasLive2D || hasSpine || hasDynamicBg || hasGifFigure ;
1164+
1165+ if ( shouldRun ) {
1166+ if ( ! app . ticker . started ) {
1167+ app . ticker . start ( ) ;
1168+ logger . debug ( 'Ticker: STARTED' ) ;
1169+ }
1170+ } else {
1171+ if ( app . ticker . started ) {
1172+ app . ticker . stop ( ) ;
1173+ this . currentApp ?. render ( ) ;
1174+ logger . debug ( 'Ticker: STOPPED' ) ;
1175+ } else {
1176+ this . requestRender ( ) ;
1177+ }
1178+ }
1179+ } ) ;
1180+ }
11031181}
11041182
11051183function updateCurrentBacklogEffects ( newEffects : IEffect [ ] ) {
0 commit comments