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

如何实现SpeedDial悬浮时按钮依次平滑显示的过渡动画

条件渲染{hovered && children}会直接在状态切换时增删DOM元素,元素没有过渡执行的时间窗口,是动画无法生效的根本原因。纯靠visibility: hidden也无法实现过渡,因为visibility是离散属性,只有显示/隐藏两个状态,没有中间过渡值。

实现方案

要实现按钮依次平滑显现的效果,核心逻辑是:

  • 移除条件渲染逻辑,让操作按钮始终挂载在DOM中,通过CSS类控制显隐状态
  • 组合opacity(透明度)、transform: scale()(缩放)两个可连续过渡的属性实现平滑显现效果,搭配visibility和pointer-events保证按钮隐藏时不响应交互、不触发误触
  • 给每个操作按钮设置递增的动画延迟,实现从靠近主按钮到远离主按钮依次出现的错落效果
具体代码修改

1. 修改SpeedDial.js

移除子元素的条件渲染,将展开状态透传给所有子按钮,给每个按钮传入索引值计算动画延迟,同时修复原代码中加号旋转样式的语法错误:

import React, { useState } from "react";
import Icon from "./Icon";

import "./SpeedDial.css";

export default function SpeedDial({ children, direction, style }) {
  const [hovered, setHovered] = useState(false);

  return (
    <div
      className={`speed-dial ${direction}`}
      onMouseEnter={() => setHovered(true)}
      onMouseLeave={() => setHovered(false)}
      onClick={() => setHovered(!hovered)}
      style={style}
    >
      <button className="main-btn">
        <Icon
          name="plus"
          style={{
            transform: hovered ? "rotate(45deg)" : "none",
            transition: "transform ease .25s"
          }}
        />
      </button>

      <div className={`action-wrapper ${direction} ${hovered ? 'open' : ''}`}>
        {React.Children.map(children, (child, index) => {
          return React.cloneElement(child, {
            index: index,
            dialOpen: hovered
          })
        })}
      </div>
    </div>
  );
}

SpeedDial.defaultProps = {
  direction: "right",
  onClick: () => {}
};

2. 修改SpeedDialAction.js

接收传入的索引和展开状态,移除标签的条件渲染,通过CSS变量绑定每个按钮的动画延迟:

import React from "react";
import Icon from "./Icon";

import "./SpeedDialAction.css";

export default function SpeedDialAction({
  icon,
  label,
  hovered,
  setActiveAction,
  direction,
  onClick,
  index,
  dialOpen
}) {
  return (
    <div 
      className={`speed-dial-action ${dialOpen ? 'open' : ''}`} 
      onClick={onClick}
      style={{'--delay': `${index * 50}ms`}}
    >
      <div className={`label ${direction} ${hovered ? 'show' : ''}`}>{label}</div>
      <button
        onMouseEnter={() => setActiveAction(label)}
        onMouseLeave={() => setActiveAction("")}
        className={direction}
      >
        <Icon name={icon} />
      </button>
    </div>
  );
}

注:50ms的单步延迟和官方组件的动画节奏完全一致,可根据需要自行调整数值。

3. 补全CSS过渡样式

首先在SpeedDial.css末尾追加left方向的容器布局样式:

.speed-dial > .action-wrapper.left {
  flex-direction: row-reverse;
  align-items: center;
}

然后替换SpeedDialAction.css中的重复样式,追加过渡效果:

.speed-dial-action {
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  /* 初始隐藏状态 */
  opacity: 0;
  transform: scale(0.4);
  visibility: hidden;
  pointer-events: none;
  /* 过渡配置,采用Material Design标准缓动曲线 */
  transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) var(--delay, 0ms);
}

/* 展开时的显示状态 */
.speed-dial-action.open {
  opacity: 1;
  transform: scale(1);
  visibility: visible;
  pointer-events: auto;
}

.speed-dial-action button {
  box-shadow: rgb(0 0 0 / 20%) 0px 3px 5px -1px,
    rgb(0 0 0 / 14%) 0px 6px 10px 0px, rgb(0 0 0 / 12%) 0px 1px 18px 0px;
  background: white;
  height: 45px;
  width: 45px;
  border-radius: 50%;
  font-size: 20px;
  cursor: pointer;
}

.speed-dial-action button:hover {
  background: #d8d8d8;
}

.speed-dial-action button.left {
  margin-right: 15px;
}

.speed-dial-action button.right {
  margin-left: 15px;
}

.speed-dial-action button.bottom {
  margin-top: 15px;
}

.speed-dial-action button.top {
  margin-bottom: 15px;
}

/* 标签淡入效果 */
.speed-dial-action .label {
  background: #565656;
  color: white;
  padding: 5px 8px;
  position: absolute;
  border-radius: 4px;
  text-transform: capitalize;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}

.speed-dial-action .label.show {
  opacity: 1;
}

.speed-dial-action .label.left {
  top: -40px;
}

.speed-dial-action .label.right {
  top: -40px;
}
最终效果

修改完成后即可实现和官方组件一致的交互:

  • 悬浮主按钮时,操作按钮从靠近主按钮的位置开始,每隔50ms依次完成淡入+缩放显现,无滑出效果
  • 鼠标移出时,按钮按反向顺序依次隐藏,隐藏状态下不会触发误触
  • 主按钮加号旋转效果、操作按钮悬浮标签淡入效果均正常生效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:36:23