Skip to content

Conversation

@TremblingMoeNew
Copy link

@TremblingMoeNew TremblingMoeNew commented Dec 6, 2025

#807

介绍

对时间线动画中的变换参数进行解耦,不再捆绑更新所有参数。并添加了setTransform、setTempAnimation、setAnimation指令的平行变换动画支持,允许对同一个目标同时进行多条针对不同变换参数的变换动画指令

更改

  1. Perform执行完成时的unmount动作改为直接使用perform对象、而非使用名称,从而修正了原先连续执行设置了-continue参数的setTransform指令时无法正确运行的问题
  2. 修改时间线动画相关系统,不再强制捆绑更新全部参数
  3. 为setTransform、setTempAnimation、setAnimation指令添加了-parallel参数。添加该参数时,该指令不会打断对同一target正在执行的其他变换动画指令,从而进行互不干扰的并行运行。

注意事项

  1. 用户需自行保证平行执行的变换没有对同样的属性进行修改
  2. 用户需自行注意并规避指令执行被nextSentence()调用打断造成演出中断这一旧有问题的影响

@gemini-code-assist
Copy link

Summary of Changes

Hello @TremblingMoeNew, 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 significantly enhances the animation and transformation system within WebGAL by introducing a more modular and flexible approach. The core improvement lies in decoupling how animation effects are applied, allowing for precise, partial updates to an object's transform properties rather than wholesale replacements. This change not only prevents unintended overwrites but also paves the way for more sophisticated animation control. Additionally, the PR adds crucial support for parallel animations, enabling multiple visual effects to run simultaneously, and refines animation naming for better system stability.

Highlights

  • Decoupled Animation Transformations: Animation transformation logic has been refactored to allow for more granular updates. Instead of overwriting entire transform objects, specific properties like scale, position, and other direct transform attributes are now updated selectively, preventing unintended side effects and enabling partial modifications.
  • Support for Parallel Animations: A new isParallel property has been introduced to the IPerform interface, allowing animations to run concurrently without automatically unmounting existing performs. This enhances the flexibility of complex animation sequences.
  • Dynamic and Unique Animation Naming: Animation names are now generated using uuid() instead of Math.random(), ensuring unique identifiers for each animation and improving reliability.
  • Refined ITransform Interface: The ITransform interface has been updated to make all its properties optional, aligning with the new decoupled approach where not all transform properties need to be present or updated simultaneously.
  • Improved Animation Duration Calculation: A new utility function, getAnimateDurationFromObj, has been added to calculate animation duration directly from an animation object, replacing the previous method that relied on an animation name.
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 by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

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 pull request 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 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. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

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.

@TremblingMoeNew TremblingMoeNew changed the title feat: ecouple feat: Decouple properties in timeline animations & support parallel animation Dec 6, 2025
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 introduces a feature to "ecouple" animations, allowing for more flexible and parallel animations. A significant change is making all properties of the ITransform interface optional, which provides more flexibility but also introduces risks if not handled carefully. My review focuses on several critical issues where undefined values are not handled correctly, which could lead to runtime errors. I've provided suggestions to make the code more robust against these undefined values, particularly when using lodash functions like pickBy and omitBy, and when dealing with non-null assertions. Other changes, such as using uuid for animation names and refactoring for efficiency, are good improvements.

Comment on lines 36 to 37
const targetScale = pickBy(targetSetEffect.transform.scale, (source, key)=> unionScaleKeys.has(key))
const targetPosition = pickBy(targetSetEffect.transform.position, (source, key)=> unionPositionKeys.has(key))

Choose a reason for hiding this comment

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

critical

The properties scale and position on targetSetEffect.transform are optional and can be undefined. Calling pickBy with undefined as the first argument will cause a runtime error.

You should provide a fallback empty object to pickBy to prevent this.

Suggested change
const targetScale = pickBy(targetSetEffect.transform.scale, (source, key)=> unionScaleKeys.has(key))
const targetPosition = pickBy(targetSetEffect.transform.position, (source, key)=> unionPositionKeys.has(key))
const targetScale = pickBy(targetSetEffect.transform.scale || {}, (source, key)=> unionScaleKeys.has(key))
const targetPosition = pickBy(targetSetEffect.transform.position || {}, (source, key)=> unionPositionKeys.has(key))

Comment on lines 76 to 77
if (target.scale) Object.assign(targetScale!, omitBy(source.scale,isUndefined));
if (target.position) Object.assign(targetPosition!, omitBy(source.position,isUndefined));

Choose a reason for hiding this comment

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

critical

The properties scale and position on source are optional and can be undefined. Calling omitBy with undefined as the first argument will cause a runtime error.

You should provide a fallback empty object to omitBy to handle cases where these properties are not present on the source object.

Suggested change
if (target.scale) Object.assign(targetScale!, omitBy(source.scale,isUndefined));
if (target.position) Object.assign(targetPosition!, omitBy(source.position,isUndefined));
if (target.scale) Object.assign(targetScale!, omitBy(source.scale || {},isUndefined));
if (target.position) Object.assign(targetPosition!, omitBy(source.position || {},isUndefined));

Comment on lines 35 to 39
const targetScale = pickBy(targetEffect!.transform!.scale, (source, key)=> has(applyFrame.scale, key))
const targetPosition = pickBy(targetEffect!.transform!.position, (source, key)=> has(applyFrame.position, key))
const effectWithDuration = { ...pickBy(targetEffect!.transform!, (source, key)=> has(applyFrame, key) ), duration: 0, ease };
effectWithDuration.scale = targetScale
effectWithDuration.position = targetPosition

Choose a reason for hiding this comment

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

critical

There are multiple potential runtime errors in this block due to unsafe handling of optional properties. The ITransform interface properties are all optional, so targetEffect.transform, targetEffect.transform.scale, targetEffect.transform.position, applyFrame.scale, and applyFrame.position can all be undefined.

  • Using non-null assertions (!) on targetEffect.transform is unsafe, as IEffect defines transform as optional.
  • Calling pickBy or has with undefined will cause a crash.

You should add defensive checks and fallbacks for all these optional properties.

Suggested change
const targetScale = pickBy(targetEffect!.transform!.scale, (source, key)=> has(applyFrame.scale, key))
const targetPosition = pickBy(targetEffect!.transform!.position, (source, key)=> has(applyFrame.position, key))
const effectWithDuration = { ...pickBy(targetEffect!.transform!, (source, key)=> has(applyFrame, key) ), duration: 0, ease };
effectWithDuration.scale = targetScale
effectWithDuration.position = targetPosition
const currentTransform = targetEffect?.transform || {};
const targetScale = pickBy(currentTransform.scale || {}, (source, key)=> has(applyFrame.scale || {}, key));
const targetPosition = pickBy(currentTransform.position || {}, (source, key)=> has(applyFrame.position || {}, key));
const effectWithDuration = { ...pickBy(currentTransform, (source, key)=> has(applyFrame, key) ), duration: 0, ease };
effectWithDuration.scale = targetScale;
effectWithDuration.position = targetPosition;

Comment on lines 129 to 131
if (transform!.scale) Object.assign(targetScale!, omitBy(transform!.scale,isUndefined));
if (transform!.position) Object.assign(targetPosition!, omitBy(transform!.position,isUndefined));
Object.assign(state.effects[effectIndex]!.transform!, omitBy(transform,isUndefined))

Choose a reason for hiding this comment

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

critical

The transform property on the updateEffect payload is optional and can be undefined. Using non-null assertions (!) on it will lead to a runtime error if transform is not provided in the action payload. Additionally, Object.assign on line 131 is not guarded and will fail if transform is undefined.

You should use optional chaining (?.) and guard the Object.assign call to safely handle cases where transform is undefined.

Suggested change
if (transform!.scale) Object.assign(targetScale!, omitBy(transform!.scale,isUndefined));
if (transform!.position) Object.assign(targetPosition!, omitBy(transform!.position,isUndefined));
Object.assign(state.effects[effectIndex]!.transform!, omitBy(transform,isUndefined))
if (transform?.scale) Object.assign(targetScale!, omitBy(transform.scale,isUndefined));
if (transform?.position) Object.assign(targetPosition!, omitBy(transform.position,isUndefined));
if (transform) Object.assign(state.effects[effectIndex]!.transform!, omitBy(transform,isUndefined));

…tial runtime error caused by undefined parameters reported by review bot
@TremblingMoeNew
Copy link
Author

TremblingMoeNew commented Dec 7, 2025

添加了setAnimation的-parallel支持,并添加了一个演示平行变换动画使用的demo剧本文件
之前setTransform和setTempAnimation改动里的getAnimateDurationFromObj函数似乎是多余的,用原来的getAnimateDuration也一样行,但改都改了……

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.

解耦变换参数并支持对同一容器不同参数设置平行动画

1 participant