Skip to content

Commit 3b417d2

Browse files
author
Hardy--Lee
committed
fix: clear animation before exiting
1 parent fd35764 commit 3b417d2

File tree

8 files changed

+92
-68
lines changed

8 files changed

+92
-68
lines changed

packages/webgal/src/Core/controller/stage/pixi/PixiController.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export interface IStageObject {
3434
uuid: string;
3535
// 一般与作用目标有关
3636
key: string;
37-
pixiContainer: WebGALPixiContainer;
37+
pixiContainer: WebGALPixiContainer | null;
3838
// 相关的源 url
3939
sourceUrl: string;
4040
sourceExt: string;
@@ -235,7 +235,7 @@ export default class PixiStage {
235235
const targetPixiContainer = this.getStageObjByKey(target);
236236
if (targetPixiContainer) {
237237
const container = targetPixiContainer.pixiContainer;
238-
PixiStage.assignTransform(container, effect.transform);
238+
if (container) PixiStage.assignTransform(container, effect.transform);
239239
}
240240
return;
241241
}
@@ -776,6 +776,7 @@ export default class PixiStage {
776776
const figureRecordTarget = this.live2dFigureRecorder.find((e) => e.target === key);
777777
if (target && figureRecordTarget?.motion !== motion) {
778778
const container = target.pixiContainer;
779+
if (!container) return;
779780
const children = container.children;
780781
for (const model of children) {
781782
let category_name = motion;
@@ -799,6 +800,7 @@ export default class PixiStage {
799800
if (target?.sourceType !== 'spine') return;
800801

801802
const container = target.pixiContainer;
803+
if (!container) return;
802804
// Spine figure 结构: Container -> Sprite -> Spine
803805
const sprite = container.children[0] as PIXI.Container;
804806
if (sprite?.children?.[0]) {
@@ -825,6 +827,7 @@ export default class PixiStage {
825827
const figureRecordTarget = this.live2dFigureRecorder.find((e) => e.target === key);
826828
if (target && figureRecordTarget?.expression !== expression) {
827829
const container = target.pixiContainer;
830+
if (!container) return;
828831
const children = container.children;
829832
for (const model of children) {
830833
// @ts-ignore
@@ -840,6 +843,7 @@ export default class PixiStage {
840843
const figureRecordTarget = this.live2dFigureRecorder.find((e) => e.target === key);
841844
if (target && !isEqual(figureRecordTarget?.blink, blinkParam)) {
842845
const container = target.pixiContainer;
846+
if (!container) return;
843847
const children = container.children;
844848
let newBlinkParam: BlinkParam = { ...baseBlinkParam, ...blinkParam };
845849
// 继承现有 BlinkParam
@@ -860,6 +864,7 @@ export default class PixiStage {
860864
const figureRecordTarget = this.live2dFigureRecorder.find((e) => e.target === key);
861865
if (target && !isEqual(figureRecordTarget?.focus, focusParam)) {
862866
const container = target.pixiContainer;
867+
if (!container) return;
863868
const children = container.children;
864869
let newFocusParam: FocusParam = { ...baseFocusParam, ...focusParam };
865870
// 继承现有 FocusParam
@@ -883,6 +888,7 @@ export default class PixiStage {
883888
const target = this.figureObjects.find((e) => e.key === key);
884889
if (target && target.sourceType === 'live2d') {
885890
const container = target.pixiContainer;
891+
if (!container) return;
886892
const children = container.children;
887893
for (const model of children) {
888894
// @ts-ignore
@@ -925,20 +931,26 @@ export default class PixiStage {
925931
const indexBg = this.backgroundObjects.findIndex((e) => e.key === key);
926932
if (indexFig >= 0) {
927933
const bgSprite = this.figureObjects[indexFig];
928-
for (const element of bgSprite.pixiContainer.children) {
929-
element.destroy();
934+
if (bgSprite.pixiContainer) {
935+
for (const element of bgSprite.pixiContainer.children) {
936+
element.destroy();
937+
}
938+
bgSprite.pixiContainer.destroy();
939+
this.figureContainer.removeChild(bgSprite.pixiContainer);
930940
}
931-
bgSprite.pixiContainer.destroy();
932-
this.figureContainer.removeChild(bgSprite.pixiContainer);
941+
bgSprite.pixiContainer = null;
933942
this.figureObjects.splice(indexFig, 1);
934943
}
935944
if (indexBg >= 0) {
936945
const bgSprite = this.backgroundObjects[indexBg];
937-
for (const element of bgSprite.pixiContainer.children) {
938-
element.destroy();
946+
if (bgSprite.pixiContainer) {
947+
for (const element of bgSprite.pixiContainer.children) {
948+
element.destroy();
949+
}
950+
bgSprite.pixiContainer.destroy();
951+
this.backgroundContainer.removeChild(bgSprite.pixiContainer);
939952
}
940-
bgSprite.pixiContainer.destroy();
941-
this.backgroundContainer.removeChild(bgSprite.pixiContainer);
953+
bgSprite.pixiContainer = null;
942954
this.backgroundObjects.splice(indexBg, 1);
943955
}
944956
// /**

packages/webgal/src/Core/controller/stage/pixi/animations/testblur.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export function generateTestblurAnimationObj(targetKey: string, duration: number
1010
* 在此书写为动画设置初态的操作
1111
*/
1212
function setStartState() {
13-
if (target) {
13+
if (target?.pixiContainer) {
1414
target.pixiContainer.alpha = 0;
1515
// @ts-ignore
1616
target.pixiContainer.blur = 0;
@@ -22,7 +22,7 @@ export function generateTestblurAnimationObj(targetKey: string, duration: number
2222
* 在此书写为动画设置终态的操作
2323
*/
2424
function setEndState() {
25-
if (target) {
25+
if (target?.pixiContainer) {
2626
target.pixiContainer.alpha = 1;
2727
// @ts-ignore
2828
target.pixiContainer.blur = 5;
@@ -40,9 +40,10 @@ export function generateTestblurAnimationObj(targetKey: string, duration: number
4040
const currentAddOplityDelta = (duration / baseDuration) * delta;
4141
const increasement = 1 / currentAddOplityDelta;
4242
const decreasement = 5 / currentAddOplityDelta;
43-
if (container.alpha < 1) {
44-
container.alpha += increasement;
45-
}
43+
if (container)
44+
if (container.alpha < 1) {
45+
container.alpha += increasement;
46+
}
4647
// @ts-ignore
4748
if (container.blur < 5) {
4849
// @ts-ignore

packages/webgal/src/Core/controller/stage/pixi/animations/timeline.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export function generateTimelineObj(
4242
times.push(currentDelay / duration);
4343
} else times.push(0);
4444
}
45-
const container = target?.pixiContainer;
4645
let animateInstance: ReturnType<typeof popmotion.animate> | null = null;
4746
// 只有有 duration 的时候才有动画
4847
if (duration > 0) {
@@ -52,13 +51,13 @@ export function generateTimelineObj(
5251
duration,
5352
ease: easeArray,
5453
onUpdate: (updateValue) => {
55-
if (container) {
54+
if (target?.pixiContainer) {
5655
const { scaleX, scaleY, ...val } = updateValue;
5756
// @ts-ignore
58-
PixiStage.assignTransform(container, omitBy(val, isUndefined));
57+
PixiStage.assignTransform(target.pixiContainer, omitBy(val, isUndefined));
5958
// 因为 popmotion 不能用嵌套,scale 要手动设置
60-
if (!isUndefined(scaleX)) container.scale.x = scaleX;
61-
if (!isUndefined(scaleY)) container.scale.y = scaleY;
59+
if (!isUndefined(scaleX)) target.pixiContainer.scale.x = scaleX;
60+
if (!isUndefined(scaleY)) target.pixiContainer.scale.y = scaleY;
6261
}
6362
},
6463
});
@@ -77,13 +76,11 @@ export function generateTimelineObj(
7776
const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined);
7877
// @ts-ignore
7978
PixiStage.assignTransform(target?.pixiContainer, assignValue);
80-
if (target?.pixiContainer) {
81-
if (!isUndefined(scale.x)) {
82-
target.pixiContainer.scale.x = scale.x;
83-
}
84-
if (!isUndefined(scale?.y)) {
85-
target.pixiContainer.scale.y = scale.y;
86-
}
79+
if (!isUndefined(scale.x)) {
80+
target.pixiContainer.scale.x = scale.x;
81+
}
82+
if (!isUndefined(scale?.y)) {
83+
target.pixiContainer.scale.y = scale.y;
8784
}
8885
}
8986
}
@@ -101,13 +98,11 @@ export function generateTimelineObj(
10198
const assignValue = omitBy({ x: position.x, y: position.y, ...state }, isUndefined);
10299
// @ts-ignore
103100
PixiStage.assignTransform(target?.pixiContainer, assignValue);
104-
if (target?.pixiContainer) {
105-
if (!isUndefined(scale.x)) {
106-
target.pixiContainer.scale.x = scale.x;
107-
}
108-
if (!isUndefined(scale?.y)) {
109-
target.pixiContainer.scale.y = scale.y;
110-
}
101+
if (!isUndefined(scale.x)) {
102+
target.pixiContainer.scale.x = scale.x;
103+
}
104+
if (!isUndefined(scale?.y)) {
105+
target.pixiContainer.scale.y = scale.y;
111106
}
112107
}
113108
}

packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftIn.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function generateUniversalSoftInAnimationObj(targetKey: string, duration:
1212
*/
1313
function setStartState() {
1414
elapsedTime = 0; // Reset timer when animation starts
15-
if (target) {
15+
if (target?.pixiContainer) {
1616
// 修正:不再强制设为 0,而是记录当前的透明度
1717
startAlpha = target.pixiContainer.alpha;
1818
}
@@ -22,7 +22,7 @@ export function generateUniversalSoftInAnimationObj(targetKey: string, duration:
2222
* 在此书写为动画设置终态的操作
2323
*/
2424
function setEndState() {
25-
if (target) {
25+
if (target?.pixiContainer) {
2626
// 终态是完全不透明,这保持不变
2727
target.pixiContainer.alpha = 1;
2828
}
@@ -49,7 +49,7 @@ export function generateUniversalSoftInAnimationObj(targetKey: string, duration:
4949
// 公式:最终值 = 初始值 + (目标值 - 初始值) * 进度
5050
// 在这里,目标值是 1,所以公式为:
5151
// alpha = startAlpha + (1 - startAlpha) * easedProgress
52-
sprite.alpha = startAlpha + (1 - startAlpha) * easedProgress;
52+
if (sprite) sprite.alpha = startAlpha + (1 - startAlpha) * easedProgress;
5353
}
5454
}
5555

packages/webgal/src/Core/controller/stage/pixi/animations/universalSoftOff.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function generateUniversalSoftOffAnimationObj(targetKey: string, duration
1212
*/
1313
function setStartState() {
1414
elapsedTime = 0; // 重置计时器
15-
if (target) {
15+
if (target?.pixiContainer) {
1616
// 修正:不再强制设为1,而是记录当前的透明度
1717
startAlpha = target.pixiContainer.alpha;
1818
}
@@ -22,7 +22,7 @@ export function generateUniversalSoftOffAnimationObj(targetKey: string, duration
2222
* 在此书写为动画设置终态的操作
2323
*/
2424
function setEndState() {
25-
if (target) {
25+
if (target?.pixiContainer) {
2626
// 终态是完全透明,这保持不变
2727
target.pixiContainer.alpha = 0;
2828
}
@@ -50,7 +50,7 @@ export function generateUniversalSoftOffAnimationObj(targetKey: string, duration
5050
// 在这里,目标值是 0,所以公式简化为:
5151
// alpha = startAlpha + (0 - startAlpha) * easedProgress
5252
// alpha = startAlpha * (1 - easedProgress)
53-
targetContainer.alpha = startAlpha * (1 - easedProgress);
53+
if (targetContainer) targetContainer.alpha = startAlpha * (1 - easedProgress);
5454
}
5555
}
5656

packages/webgal/src/Core/gameScripts/changeBg/index.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,18 @@ export const changeBg = (sentence: ISentence): IPerform => {
3232
dispatch(unlockCgInUserData({ name: unlockName, url, series }));
3333
}
3434

35-
/**
36-
* 删掉相关 Effects,因为已经移除了
37-
*/
38-
if (webgalStore.getState().stage.bgName !== sentence.content) {
35+
// 检测 url 是否变化
36+
let isUrlChanged = webgalStore.getState().stage.bgName !== sentence.content;
37+
if (isUrlChanged) {
38+
// 清除动画
39+
WebGAL.gameplay.pixiStage?.removeAnimation('bg-main');
40+
// 移除旧的 effect
3941
dispatch(stageActions.removeEffectByTargetId(`bg-main`));
42+
// 标记旧的背景为退出中,防止旧背景继承新参数
43+
const oldStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey('bg-main');
44+
if (oldStageObject) {
45+
oldStageObject.isExiting = true;
46+
}
4047
}
4148

4249
// 处理 transform 和 默认 transform

packages/webgal/src/Core/gameScripts/changeFigure.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { logger } from '@/Core/util/logger';
1212
import { getAnimateDuration } from '@/Core/Modules/animationFunctions';
1313
import { WebGAL } from '@/Core/WebGAL';
1414
import { baseBlinkParam, baseFocusParam, BlinkParam, FocusParam } from '@/Core/live2DCore';
15-
import { WEBGAL_NONE } from '../constants';
15+
import { STAGE_KEYS, WEBGAL_NONE } from '../constants';
1616
/**
1717
* 更改立绘
1818
* @param sentence 语句
@@ -46,9 +46,24 @@ export function changeFigure(sentence: ISentence): IPerform {
4646
}
4747

4848
// id 与 自由立绘
49-
let key = getStringArgByKey(sentence, 'id') ?? '';
50-
const isFreeFigure = key ? true : false;
51-
const id = key ? key : `fig-${pos}`;
49+
let idFromArgs = getStringArgByKey(sentence, 'id') ?? '';
50+
const isFreeFigure = idFromArgs ? true : false;
51+
let key = '';
52+
if (isFreeFigure) {
53+
key = idFromArgs;
54+
} else {
55+
switch (pos) {
56+
case 'center':
57+
key = STAGE_KEYS.FIG_C;
58+
break;
59+
case 'left':
60+
key = STAGE_KEYS.FIG_L;
61+
break;
62+
case 'right':
63+
key = STAGE_KEYS.FIG_R;
64+
break;
65+
}
66+
}
5267

5368
// live2d 或 spine 相关
5469
let motion = getStringArgByKey(sentence, 'motion') ?? '';
@@ -95,9 +110,9 @@ export function changeFigure(sentence: ISentence): IPerform {
95110
const dispatch = webgalStore.dispatch;
96111

97112
const currentFigureAssociatedAnimation = webgalStore.getState().stage.figureAssociatedAnimation;
98-
const filteredFigureAssociatedAnimation = currentFigureAssociatedAnimation.filter((item) => item.targetId !== id);
113+
const filteredFigureAssociatedAnimation = currentFigureAssociatedAnimation.filter((item) => item.targetId !== key);
99114
const newFigureAssociatedAnimationItem = {
100-
targetId: id,
115+
targetId: key,
101116
animationFlag: animationFlag,
102117
mouthAnimation: {
103118
open: mouthOpen,
@@ -112,9 +127,7 @@ export function changeFigure(sentence: ISentence): IPerform {
112127
filteredFigureAssociatedAnimation.push(newFigureAssociatedAnimationItem);
113128
dispatch(setStage({ key: 'figureAssociatedAnimation', value: filteredFigureAssociatedAnimation }));
114129

115-
/**
116-
* 如果 url 没变,不移除
117-
*/
130+
// 检测 url 是否变化
118131
let isUrlChanged = true;
119132
if (key !== '') {
120133
const figWithKey = webgalStore.getState().stage.freeFigure.find((e) => e.key === key);
@@ -140,12 +153,14 @@ export function changeFigure(sentence: ISentence): IPerform {
140153
}
141154
}
142155
}
143-
/**
144-
* 处理 Effects
145-
*/
156+
146157
if (isUrlChanged) {
147-
webgalStore.dispatch(stageActions.removeEffectByTargetId(id));
148-
const oldStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey(id);
158+
// 清除动画
159+
WebGAL.gameplay.pixiStage?.removeAnimation(key);
160+
// 移除旧的 effect
161+
webgalStore.dispatch(stageActions.removeEffectByTargetId(key));
162+
// 标记旧的立绘为退出中,防止旧立绘继承新参数
163+
const oldStageObject = WebGAL.gameplay.pixiStage?.getStageObjByKey(key);
149164
if (oldStageObject) {
150165
oldStageObject.isExiting = true;
151166
}
@@ -196,7 +211,7 @@ export function changeFigure(sentence: ISentence): IPerform {
196211
}
197212
};
198213

199-
function setFigureData() {
214+
function postFigureStateSet() {
200215
if (isUrlChanged) {
201216
// 当 url 发生变化时,即发生新立绘替换
202217
// 应当赋予一些参数以默认值,防止从旧立绘的状态获取数据
@@ -236,26 +251,20 @@ export function changeFigure(sentence: ISentence): IPerform {
236251
*/
237252
const freeFigureItem: IFreeFigure = { key, name: content, basePosition: pos };
238253
setAnimationNames(key, sentence);
239-
setFigureData();
254+
postFigureStateSet();
240255
dispatch(stageActions.setFreeFigureByKey(freeFigureItem));
241256
} else {
242257
/**
243258
* 下面的代码是设置与位置关联的立绘的
244259
*/
245-
const positionMap = {
246-
center: 'fig-center',
247-
left: 'fig-left',
248-
right: 'fig-right',
249-
};
250260
const dispatchMap: Record<string, keyof IStageState> = {
251261
center: 'figName',
252262
left: 'figNameLeft',
253263
right: 'figNameRight',
254264
};
255265

256-
key = positionMap[pos];
257266
setAnimationNames(key, sentence);
258-
setFigureData();
267+
postFigureStateSet();
259268
dispatch(setStage({ key: dispatchMap[pos], value: content }));
260269
}
261270

0 commit comments

Comments
 (0)