😵‍💫 产品:你能不能让按钮 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。

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

相关推荐
哒哒哒52852012 分钟前
HTTP缓存
前端·面试
T___15 分钟前
从入门到放弃?带你重新认识 Headless UI
前端·设计模式
wordbaby16 分钟前
React Router 中调用 Actions 的三种方式详解
前端·react.js
黄丽萍22 分钟前
前端Vue3项目代码开发规范
前端
葬送的代码人生24 分钟前
AI Coding→像素飞机大冒险:一个让你又爱又恨的小游戏
javascript·设计模式·ai编程
curdcv_po26 分钟前
🏄公司报销,培养我成一名 WebGL 工程师⛵️
前端
Jolyne_37 分钟前
前端常用的树处理方法总结
前端·算法·面试
wordbaby39 分钟前
后端的力量,前端的体验:React Router Server Action 的魔力
前端·react.js
Alang39 分钟前
Mac Mini M4 16G 内存本地大模型性能横评:9 款模型实测对比
前端·llm·aigc
林太白40 分钟前
Rust-连接数据库
前端·后端·rust