animate-shake 不会自动触发,因它非 tailwind 内置类,需在 tailwind.config.js 中显式声明 keyframes 和 animation 才能生效。

为什么 animate-shake 不会自动触发?
Tailwind 默认不提供表单错误抖动动画,animate-shake 也不是内置类名——它需要手动注册。直接写 animate-shake 会被 PurgeCSS 清掉,浏览器里完全没效果,控制台也无报错,容易误以为是 JS 没触发。
真正起作用的是自定义的 @keyframes shake + 对应的 animation 类。必须在 tailwind.config.js 的 theme.extend.animation 和 theme.extend.keyframes 里显式声明,否则编译后不存在。
- 漏写
keyframes定义 → 动画无行为 - 只写
animation但没加前缀(如shake而非animate-shake)→ 类名不匹配 - 用
content或hidden类强制隐藏元素时,抖动可能被截断或不可见
如何添加可复用的抖动动画类?
在 tailwind.config.js 中扩展:
module.exports = {
theme: {
extend: {
keyframes: {
shake: {
'0%, 100%': { transform: 'translateX(0)' },
'25%': { transform: 'translateX(-4px)' },
'50%': { transform: 'translateX(4px)' },
'75%': { transform: 'translateX(-4px)' },
}
},
animation: {
shake: 'shake 0.4s cubic-bezier(.36,.07,.19,.97) both'
}
}
}
}
这样就能安全使用 animate-shake 类了。注意:时间设为 0.4s 是权衡结果——太短(0.2s)人眼难识别,太长(0.6s)易干扰操作;cubic-bezier 选 .36,.07,.19,.97 是为了模拟物理回弹感,比线性更自然。
怎么让抖动只在错误时生效,且不打断用户输入?
抖动是视觉反馈,不是校验逻辑本身。别把 animate-shake 硬绑在 input 上并靠 JS 频繁增删 class,容易和 focus/blur 冲突,导致输入框失焦或重复触发。
- 只在提交后校验失败时添加
animate-shake,且加once控制(比如用setTimeout1 秒后移除) - 对
<input>使用focus:outline-none,避免抖动时 outline 跳变 - 若用 React/Vue,确保 class 切换不引起重渲染(例如用
ref直接操作 DOM 或用className条件表达式) - 移动端慎用:部分 iOS Safari 对
transform抖动响应迟钝,可降级为border-color-red-500+text-red-600
抖动动画和表单库(如 react-hook-form)怎么配合?
以 react-hook-form 为例,抖动不该由 register() 自动管理,而应在 formState.errors 出现后、且用户未再次编辑该字段时触发:
const { register, formState: { errors } } = useForm();
const inputRef = useRef();
useEffect(() => {
if (errors.email && !inputRef.current?.matches(':focus')) {
inputRef.current?.classList.add('animate-shake');
setTimeout(() => {
inputRef.current?.classList.remove('animate-shake');
}, 400);
}
}, [errors.email]);
关键点:!inputRef.current?.matches(':focus') 防止用户正在输入时抖动干扰;400ms 与动画时长严格一致,避免残留 class 影响后续状态。
真实项目里最容易被忽略的是“抖动完成后的状态清理”——如果用户快速连续提交,旧的 setTimeout 还没执行完,新的抖动又来了,class 可能卡在添加未移除状态,导致后续所有校验都失效。得用 clearTimeout 做防重叠处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











