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

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

相关推荐
林深现海5 小时前
Jetson Orin nano/nx刷机后无法打开chrome/firefox浏览器
前端·chrome·firefox
黄诂多6 小时前
APP原生与H5互调Bridge技术原理及基础使用
前端
前端市界6 小时前
用 React 手搓一个 3D 翻页书籍组件,呼吸海浪式翻页,交互体验带感!
前端·架构·github
文艺理科生6 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling6 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
C澒6 小时前
Vue 项目渐进式迁移 React:组件库接入与跨框架协同技术方案
前端·vue.js·react.js·架构·系统架构
xiaoxue..6 小时前
合并两个升序链表 与 合并k个升序链表
java·javascript·数据结构·链表·面试
清山博客6 小时前
OpenCV 人脸识别和比对工具
前端·webpack·node.js
要加油哦~6 小时前
AI | 实践教程 - ScreenCoder | 多agents前端代码生成
前端·javascript·人工智能
程序员Sunday7 小时前
说点不一样的。GPT-5.3 与 Claude Opus 4.6 同时炸场,前端变天了?
前端·gpt·状态模式