网站已运行 162 · 16小时 · 47 · 32
目录

GSAP 动画冲突:鼠标快速滑动导致 Tween 失效

使用gsap配合wordpress制作动画效果时,鼠标快速滑动导致动画失效问题

在 WordPress 或 Elementor 页面中使用 GSAP 制作卡片悬停动画时,慢速移动鼠标通常一切正常,但快速划过多张图片后,按钮和文字可能停在错误位置,甚至完全不再播放离开动画。

这不是 GSAP 失效,而是同一个元素在短时间内收到多次动画指令,旧 Tween 尚未结束,新 Tween 又开始修改相同属性,最终形成动画竞争,这就是典型的 GSAP 动画冲突。

问题表现

  • 鼠标快速经过多张卡片后,按钮没有回到初始位置。
  • 文字透明度或位移状态停在动画中间。
  • 再次进入卡片时动画突然跳动,没有从当前状态平滑衔接。
  • 桌面端偶发,鼠标移动越快越容易复现。

问题本质:Tween Conflict

一次快速的进入和离开可能形成下面的执行顺序:

  1. 触发 pointerenter,GSAP 开始执行进入动画。
  2. 进入动画还没完成,鼠标已经离开。
  3. 离开动画又对同一元素的 transformopacity 写入新值。
  4. 多个 Tween 同时竞争相同属性,最终状态取决于最后一次更新。

解决方法一:使用 overwrite

给新的动画添加 overwrite:'auto',可以让 GSAP 自动停止与新动画冲突的旧属性 Tween。

设置含义适用情况
false允许旧动画继续执行多个 Tween 修改不同属性
true停止目标上的其他 Tween需要强制重置全部动画
'auto'只处理与新 Tween 冲突的属性大多数 hover 动画
gsap.to(button, {
    y: 0,
    autoAlpha: 1,
    duration: 0.35,
    ease: 'power2.out',
    overwrite: 'auto'
});

解决方法二:开始新动画前清理旧 Tween

gsap.killTweensOf() 适合需要完全掌控状态的场景。每次进入或离开前,先停止相关元素尚未结束的动画,再创建新的 Tween。

gsap.killTweensOf([button, text]);

对于普通卡片数量,这个操作的性能开销通常可以忽略。真正需要避免的是在 pointermove 这类高频事件里不断创建大量 Tween。

完整的卡片悬停代码

下面的示例使用 transform 和透明度制作动画。请把类名替换成页面中真实的卡片、按钮和文字类名。

document.addEventListener('DOMContentLoaded', () => {
    const cards = document.querySelectorAll('.szymwp-hover-card');

    cards.forEach((card) => {
        if (card.dataset.gsapHoverReady === '1') {
            return;
        }

        const button = card.querySelector('.szymwp-hover-button');
        const text = card.querySelector('.szymwp-hover-text');

        if (!button || !text) {
            return;
        }

        card.dataset.gsapHoverReady = '1';

        gsap.set(button, { y: 24, autoAlpha: 0 });
        gsap.set(text, { y: 0, autoAlpha: 1 });

        const stopCurrentTweens = () => {
            gsap.killTweensOf([button, text]);
        };

        card.addEventListener('pointerenter', () => {
            stopCurrentTweens();

            gsap.to(button, {
                y: 0,
                autoAlpha: 1,
                duration: 0.35,
                ease: 'power2.out',
                overwrite: 'auto'
            });

            gsap.to(text, {
                y: -14,
                autoAlpha: 0,
                duration: 0.25,
                ease: 'power2.out',
                overwrite: 'auto'
            });
        });

        card.addEventListener('pointerleave', () => {
            stopCurrentTweens();

            gsap.to(button, {
                y: 24,
                autoAlpha: 0,
                duration: 0.28,
                ease: 'power2.inOut',
                overwrite: 'auto'
            });

            gsap.to(text, {
                y: 0,
                autoAlpha: 1,
                duration: 0.32,
                ease: 'power2.out',
                overwrite: 'auto'
            });
        });
    });
});

为什么建议用 pointerenter 和 pointerleave?

mouseovermouseout 会在鼠标经过卡片内部子元素时重复触发,容易额外创建 Tween。pointerenterpointerleave 更适合整个卡片的进入与离开状态,同时也统一了鼠标、触控笔等指针事件。

在 Elementor 中使用时的注意事项

  • GSAP 主库必须在自定义动画代码之前加载。
  • 如果 Elementor 也给同一元素设置了 Entrance Animation,应关闭其中一方,避免同时修改 transform。
  • 脚本可能在编辑器预览中重复初始化,所以示例使用 data-gsap-hover-ready 防止重复绑定。
  • 优先动画 transformopacity,不要频繁改变 topbottom 等会触发布局计算的属性。
  • 更新代码后清除 WP Rocket、Elementor CSS 和浏览器缓存。

照顾减少动画偏好

对于启用了“减少动态效果”的用户,可以直接跳过动画并显示最终内容:

if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
    gsap.set('.szymwp-hover-button', { y: 0, autoAlpha: 1 });
}

常见问题

overwrite 和 killTweensOf 需要同时使用吗?

不一定。简单 hover 动画通常只用 overwrite:'auto' 就够了;状态较复杂或旧动画必须彻底停止时,再配合 killTweensOf()

为什么代码正常但 Elementor 中仍然跳动?

检查 Elementor、主题 CSS 和 GSAP 是否同时修改了同一个元素的 transformtransition,并确认脚本没有被重复加载。

数臻源码猫咪图标
目录
数臻源码猫咪图标

目录

标签云: