You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用Floating UI时禁用滚动中的transform动画?

问题描述

我使用floating-ui库实现了一个悬浮菜单组件,通过transform属性的动画来平滑调整菜单位置,但滚动时该动画会生效,带来了体验问题。请问在React中能否仅在滚动时为悬浮元素添加类来禁用该动画?

解决方案

完全可以实现,核心思路是监听页面滚动状态,给悬浮元素添加状态标识,再通过CSS在滚动时覆盖动画规则,禁用transform过渡。

1. 完善滚动状态监听逻辑

优化滚动监听逻辑,滚动触发时立即标记为滚动中,滚动停止后延迟一段时间再标记为非滚动(避免滚动过程中状态频繁切换),同时监听编辑器容器的滚动(因为你的编辑器是带滚动条的.Editor):

export const BubbleMenu = ({ editor, children }: Props) => {
  const [open, setOpen] = useState(false);
  const [scrolling, setScrolling] = useState(false);
  const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    const handleScroll = () => {
      setScrolling(true);
      // 清除之前的延迟定时器
      if (scrollTimeoutRef.current) {
        clearTimeout(scrollTimeoutRef.current);
      }
      // 滚动停止后300ms标记为非滚动
      scrollTimeoutRef.current = setTimeout(() => {
        setScrolling(false);
      }, 300);
    };

    // 监听window和编辑器容器的滚动
    const editorContainer = editor.view.dom.closest('.Editor');
    window.addEventListener("scroll", handleScroll);
    editorContainer?.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
      editorContainer?.removeEventListener("scroll", handleScroll);
      if (scrollTimeoutRef.current) {
        clearTimeout(scrollTimeoutRef.current);
      }
    };
  }, [editor]);

  // ... 其余代码保持不变
};

2. 调整CSS规则,滚动时禁用动画

修改SCSS代码,当data-scrolling="true"时,直接移除transform的过渡效果:

/* 原有动画规则保持不变 */
#floating {
  transition-property: opacity, transform;
}
#floating[data-status="open"],
#floating[data-status="close"] {
  transition-property: opacity, transform;
  transition-duration: 250ms;
}
#floating[data-status="initial"],
#floating[data-status="close"] {
  opacity: 0;
}
#floating[data-status="initial"][data-placement^="top"],
#floating[data-status="close"][data-placement^="top"] {
  transform: translateY(-5px);
}
#floating[data-status="initial"][data-placement^="bottom"],
#floating[data-status="close"][data-placement^="bottom"] {
  transform: translateY(5px);
}
#floating[data-status="initial"][data-placement^="left"],
#floating[data-status="close"][data-placement^="left"] {
  transform: translateX(5px);
}
#floating[data-status="initial"][data-placement^="right"],
#floating[data-status="close"][data-placement^="right"] {
  transform: translateX(-5px);
}

/* 滚动时禁用transform动画 */
#floating-wrapper[data-scrolling="true"] {
  transition: none !important;
}
#floating-wrapper[data-scrolling="true"] #floating {
  transition-property: opacity !important; /* 保留透明度动画,仅禁用transform */
}

.Editor {
  max-height: 300px;
  overflow: auto;
}

3. 确保状态正确传递到DOM

你的代码已经在#floating-wrapper上添加了data-scrolling={scrolling},这部分无需修改,确保该属性能正确反映当前滚动状态即可。

原理说明

  • 滚动触发时,scrolling变为true,DOM上的data-scrolling="true"生效,CSS强制移除transform的过渡动画,菜单跟随滚动直接定位,无动画卡顿。
  • 滚动停止300ms后,scrolling恢复为false,动画规则重新生效,菜单的正常位置调整(比如切换选中内容时)会有平滑过渡。

内容的提问来源于stack exchange,提问作者Ugus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 21:24:53