Skip to content

Conversation

HardyNLee
Copy link
Contributor

@HardyNLee HardyNLee commented Jul 16, 2025

介绍

完全修复 syncWithOrigine"动画未结束的情况下进行同步,引起的动画消失的问题" , 包括实验性快速预览 ,这使得 Webgal Terre 预览时不再丢失动画

这个 Bug 实际上是由多个问题叠加导致的

主要更改

  • 将 perform 相关的 time out 塞入 performController.timeoutList
    • 之前这个 timeout list 完全没用上, 导致其实清理 perform list 时没有把相关的 timeout 一起清理, 导致一些动画冲突
  • resetStage 时, 清除所有动画 (stage.removeAllAnimations())
    • 也是解决旧动画导致的冲突
  • 仅在 bgName !== sentence.content 时移除 bg-main 的 effect
    • 解决预览时, 背景会意外清理掉自己的 effect 的情况
  • 立绘和背景仅在 state 里没有自己的 effect 时, 应用出场动画
    • 这是因为, 开启实验性快速预览后, 应用了 back log 的 state, 所以实际上是从 initState 变换到 backlogState, 如果 backlogState 里有 figName bgName , 会触发它们的出场动画, 但是很多情况下, 这些立绘和背景并不是在这一句出场, effect list 是有它们的 effect 的, 于是出场动画的 effect 覆盖了原本应该使用的在 backlog 里的 effect, 导致效果未正确继承

已知问题

  • 目前无法区分现有 effect list 里, effect 作用的目标的 url 是不是现在的新 url, 这导致同目标但不同 url 时, 靠后的 url 的出场动画无法被触发, 触发场景有:
    • 实验性快速预览
    • backlog 跳转(直接到新 url 无出场动画, 到旧 url 再正常播放到新 url 出场动画是正常的)
    • 大概率所有应用了新 state 的情况都是这样

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @HardyNLee, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request resolves a critical animation bug in Webgal Terre, particularly affecting the syncWithOrigine functionality and experimental fast preview mode, where animations would disappear or conflict. The changes focus on robust animation lifecycle management, ensuring proper cleanup of timeouts, comprehensive stage animation resets, and intelligent application of entry animations for stage elements to maintain visual consistency and prevent unintended overrides.

Highlights

  • Improved Animation Management: Perform-related timeouts are now correctly tracked and cleared by adding them to performController.timeoutList. This prevents animation conflicts that previously occurred due to uncleared timeouts.
  • Comprehensive Stage Reset: A new removeAllAnimations() method has been introduced in PixiController and integrated into the resetStage function. This ensures all active animations are explicitly cleared when the stage is reset, resolving issues with lingering old animations.
  • Smarter Background Effect Handling: The logic for removing background effects (bg-main) has been refined. Effects are now only removed if the background content (bgName) has actually changed, preventing unintended clearing of effects during previews where the background remains the same.
  • Conditional Entry Animations: Entry animations for both figures and backgrounds are now applied conditionally. They will only trigger if the stage element (figure or background) does not already have an active effect in the stage.effects state. This is crucial for correct behavior with experimental fast preview, preventing new entry animations from overriding existing effects from the backlog state.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses animation synchronization issues. I've provided suggestions to improve code clarity, efficiency, and maintainability by refactoring duplicated code and adhering to React best practices.

Comment on lines +271 to +275
public removeAllAnimations() {
while (this.stageAnimations.length > 0) {
this.removeAnimationByIndex(0);
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of removeAllAnimations uses a while loop, which can be inefficient if the number of animations is large. A more performant approach would be to iterate through the array once to perform the cleanup actions and then clear the array. This would have a time complexity of O(n).

  public removeAllAnimations() {
    this.stageAnimations.forEach((animation) => {
      this.currentApp?.ticker.remove(animation.animationObject.tickerFunc);
      animation.animationObject.setEndState();
      this.unlockStageObject(animation.targetKey ?? 'default');
    });
    this.stageAnimations.length = 0;
  }

Comment on lines 38 to 41
let isRemoveEffects = true;
if (webgalStore.getState().stage.bgName === sentence.content) {
isRemoveEffects = false;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic can be simplified by placing the condition directly in the if statement, making the code more concise.

  /**
   * 删掉相关 Effects,因为已经移除了
   */
  if (webgalStore.getState().stage.bgName !== sentence.content) {
    dispatch(stageActions.removeEffectByTargetId(`bg-main`));
  }

Comment on lines 30 to 31
const transformState = webgalStore.getState().stage.effects;
const targetEffect = transformState.find((effect) => effect.target === thisBgKey);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To avoid potential race conditions, use the stageState prop for consistency within React's render cycle, rather than calling webgalStore.getState() directly.

Suggested change
const transformState = webgalStore.getState().stage.effects;
const targetEffect = transformState.find((effect) => effect.target === thisBgKey);
const targetEffect = stageState.effects.find((effect) => effect.target === thisBgKey);

Comment on lines 52 to 53
const transformState = webgalStore.getState().stage.effects;
const targetEffect = transformState.find((effect) => effect.target === thisFigKey);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To avoid potential race conditions, use the stageState prop to get the effects list, ensuring consistency with React's data flow.

Suggested change
const transformState = webgalStore.getState().stage.effects;
const targetEffect = transformState.find((effect) => effect.target === thisFigKey);
const targetEffect = stageState.effects.find((effect) => effect.target === thisFigKey);

Comment on lines 52 to 64
const transformState = webgalStore.getState().stage.effects;
const targetEffect = transformState.find((effect) => effect.target === thisFigKey);

if (!targetEffect) {
const { duration, animation } = getEnterExitAnimation(thisFigKey, 'enter');
WebGAL.gameplay.pixiStage!.registerPresetAnimation(
animation,
softInAniKey,
thisFigKey,
stageState.effects,
);
setTimeout(() => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(softInAniKey), duration);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block of code for conditionally playing an entrance animation is repeated multiple times in this file and also in useSetBg.ts. To improve maintainability, extract this logic into a shared helper function.

function playEnterAnimationIfNeeded(
  targetKey: string,
  animationKey: string,
  effects: IEffect[],
) {
  const targetEffect = effects.find((effect) => effect.target === targetKey);
  if (!targetEffect) {
    const { duration, animation } = getEnterExitAnimation(targetKey, 'enter');
    WebGAL.gameplay.pixiStage!.registerPresetAnimation(
      animation,
      animationKey,
      targetKey,
      effects,
    );
    setTimeout(
      () => WebGAL.gameplay.pixiStage!.removeAnimationWithSetEffects(animationKey),
      duration,
    );
  }
}

// Usage:
playEnterAnimationIfNeeded(thisFigKey, softInAniKey, stageState.effects);

if (script.args.find((e) => e.key === 'continue' && e.value === true)) perform.goNextWhenOver = true;

this.performList.push(perform);
this.timeoutList.push(perform.stopTimeout);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

注意到 48 行在 perform 中记录了 stopTimeout。这里的记录是必须的吗?

Copy link
Contributor Author

@HardyNLee HardyNLee Jul 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是有作用的, 有些 stopFunction 关联的是 removeAnimation, 它是根据 target 名称移除动画的, 这使得新的同目标的动画, 会被旧的没清理的 timeout 的 stopFunction 给清掉, 实际测试不加这行的话, 连续快速同步同目标的动画, 会被旧的 timeout 给打断
编辑: 不是同 target, 而是同 key, 而自从 #711 后, setTransform, setTempAnimation, setAnimation 的 key 不在用随机数, 而是相同的了, 合并 #717 后 changeBg 和 changeFigure 也是相同的了

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我的意思是,这里是否不用单独开这个 timeoutList 记录,需要用的时候通过遍历演出表 performList.map(p=>p.stopTimeout) 就可以拿到所有的 timeout 了

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

看了下代码,这个 timeoutList 确实是很早就存在的,但是一直没有使用,可能是因为 performList 中已经有足够信息了。额外维护一个 list 会增加复杂度。因为遍历 performList 理论上应该可以获得所有 timeout 。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已移除 perform controller 中的 timeout list

@MakinoharaShoko
Copy link
Member

此外,建议装一个 eslint 插件,以使得提交的代码进一步符合 WebGAL 项目的规范。

const dispatch = webgalStore.dispatch;
if (name !== '') dispatch(unlockCgInUserData({ name, url, series }));

let isRemoveEffects = true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRemoveEffects 只在这里使用了,那么这里的代码应该有进一步简化的空间

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已修改

@MakinoharaShoko
Copy link
Member

b3a51c98607847501d509e1a4b89a5d0 立绘和背景的默认进入动画注册的是 presetAnimation,本身已经考虑了处理已存在的 effect,并跳过动画的问题。所以,试验性快速预览应该是遇到了其他问题。也许,只需要 resetStage 的时候把动画全干掉,timeout 全取消,就可以了?

@HardyNLee
Copy link
Contributor Author

立绘和背景的默认进入动画注册的是 presetAnimation,本身已经考虑了处理已存在的 effect,并跳过动画的问题。所以,试验性快速预览应该是遇到了其他问题。

嗯, 注册预设动画确实没问题, 在最新的提交中, 已将改动范围缩减至 getEnterExitAnimation 中

也许,只需要 resetStage 的时候把动画全干掉,timeout 全取消,就可以了?

这个还是不行, 测试结果是, 仍然要判断是否存在 effect, 否则依然会触发实验性快速预览的 bug, 不过我目前也有点盲人摸象了, 不确定真正的原因是什么

@MakinoharaShoko MakinoharaShoko merged commit abe73d2 into OpenWebGAL:dev Jul 22, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants