😵‍💫 产品:你能不能让按钮 hover 有个动效?我直接做了个 UI 框架

起因:一句话的"需求"

一天,产品在设计稿下方评论说:

"按钮 hover 能不能有点交互感?像你之前那个小 demo 那种~"

我本来以为就是加个 transition,小事一桩。

css 复制代码
button {
  transition: all 0.2s ease;
}
button:hover {
  transform: scale(1.05);
}

上线后产品说:"嗯,这个感觉不错耶,那我们把所有按钮 hover 都做成统一的吧。"

我知道,我完了。


没有人只加一个动效:于是我干脆封装了

在统一动效过程中,我意识到各类按钮(Primary、Danger、Ghost)对 hover 的预期不同。

于是我封装了一个 motion map:

ts 复制代码
const hoverMotionMap = {
  primary: 'scale',
  danger: 'shake',
  ghost: 'glow',
}

再写一个小 hook:

ts 复制代码
export function useHoverMotion(type: 'primary' | 'danger' | 'ghost') {
  const motion = hoverMotionMap[type] || 'scale'
  return `hover-motion-${motion}`
}

组件里使用起来:

html 复制代码
<template>
  <button :class="motionClass">确认</button>
</template>

<script setup lang="ts">
import { useHoverMotion } from '@/hooks/useHoverMotion'
const motionClass = useHoverMotion('primary')
</script>

你知道的,一旦封装就止不住了

hook 写了,动效也开始丰富了起来。

1️⃣ scale(标准场景)

css 复制代码
.hover-motion-scale:hover {
  transform: scale(1.05);
}

2️⃣ shake(误操作提示)

css 复制代码
.hover-motion-shake:hover {
  animation: shake 0.25s ease-in-out;
}
@keyframes shake {
  0% { transform: translateX(0); }
  25% { transform: translateX(-3px); }
  50% { transform: translateX(3px); }
  75% { transform: translateX(-2px); }
  100% { transform: translateX(0); }
}

3️⃣ glow(高亮视觉)

css 复制代码
.hover-motion-glow:hover {
  box-shadow: 0 0 12px rgba(0, 123, 255, 0.6);
}

然后我干脆加了 Design Token 方案

考虑到未来可能有改版,我定义了 hover-effects.token.ts

ts 复制代码
export const hoverEffects = {
  scale: {
    transform: 'scale(1.05)',
    transition: 'all 0.2s ease',
  },
  glow: {
    boxShadow: '0 0 12px rgba(0, 123, 255, 0.6)',
    transition: 'all 0.2s ease',
  },
  shake: {
    animation: 'shake 0.25s ease-in-out',
  },
}

然后配合 Tailwind plugin 动态生成类名。


想着都封成组件了,不如抽出一套 UI 库

就这样,我加了:

  • <UIButton type="primary" motion="scale" />
  • <UIInput hover="glow" />
  • <UIForm label-align="right" hover="none" />

还写了 storybook 做展示:

ts 复制代码
export default {
  title: 'UI/Button',
  component: UIButton,
}

export const HoverScale = () => <UIButton motion="scale">确认</UIButton>

中途遇到的"意想不到"问题

① 部分动效在移动端体验极差

iOS 的 :hover 会粘在上面,导致 glow 效果没法清除,我写了:

css 复制代码
@media (hover: none) {
  .hover-motion-glow:hover {
    box-shadow: none;
  }
}

② 某些动效导致 layout shift

比如 scale 会撑开父容器,后来我改用 transform-origin: center; 再加内边距预留。

③ 动效冲突问题

Hover 动效和 active/click 效果容易冲突。我写了 motion 状态做协调:

ts 复制代码
enum MotionState {
  Idle = 'idle',
  Hovered = 'hovered',
  Pressed = 'pressed',
}

温馨提示😭:

千万别让产品看到你写得有点好看的 demo。

📌 你可以继续看我的系列文章

相关推荐
Bdygsl1 小时前
前端开发:CSS(2)—— 选择器
前端·css
斯~内克1 小时前
CSS包含块与百分比取值机制完全指南
前端·css·tensorflow
百万蹄蹄向前冲7 小时前
秋天的第一口代码,Trae SOLO开发体验
前端·程序员·trae
努力奋斗18 小时前
VUE-第二季-02
前端·javascript·vue.js
路由侠内网穿透8 小时前
本地部署 SQLite 数据库管理工具 SQLite Browser ( Web ) 并实现外部访问
运维·服务器·开发语言·前端·数据库·sqlite
一只韩非子8 小时前
程序员太难了!Claude 用不了?两招解决!
前端·claude·cursor
JefferyXZF8 小时前
Next.js项目结构解析:理解 App Router 架构(二)
前端·全栈·next.js
Sane8 小时前
react函数组件怎么模拟类组件生命周期?一个 useEffect 搞定
前端·javascript·react.js
gnip9 小时前
可重试接口请求
前端·javascript
若梦plus9 小时前
模块化与package.json
前端