本页出处
设计风格资料库
内容来自 luckymaomi 的 UI 风格库(复刻版),以 MIT 许可 提供。本站镜像收录,仅作学习参考;这一页的原始地址是 原站页面。
← 回到导览访问原站仓库
返回动效库
微交互入门

抖动提示

元素在水平方向快速抖动,常用于表单校验失败或错误提示的视觉反馈。

点击时500ms

交互预览

动效定义与代码

{
  "slug": "shake",
  "name": "抖动提示",
  "nameEn": "Shake",
  "description": "元素在水平方向快速抖动,常用于表单校验失败或错误提示的视觉反馈。",
  "descriptionEn": "Element shakes horizontally with decreasing amplitude. Commonly used for form validation errors or attention-grabbing feedback.",
  "category": "micro-interaction",
  "tags": [
    "shake",
    "error",
    "feedback",
    "attention",
    "click"
  ],
  "trigger": "on-click",
  "difficulty": "beginner",
  "duration": "500ms",
  "easing": "ease-in-out",
  "cssProperties": [
    "transform"
  ],
  "isGPUAccelerated": true,
  "previewBg": "light",
  "keywords": [
    "shake",
    "vibrate",
    "error",
    "validation",
    "feedback",
    "attention"
  ],
  "useCases": [
    "Form validation error feedback",
    "Wrong password indication",
    "Attention-grabbing notification",
    "Interactive button feedback"
  ],
  "relatedAnimations": [
    "hover-lift",
    "bounce-in"
  ],
  "recommendedStyles": [
    "neo-brutalist",
    "modern-gradient",
    "minimalist-flat"
  ],
  "codeSnippets": [
    {
      "label": "CSS Keyframes",
      "language": "css",
      "code": "@keyframes shake {\n  0%, 100% {\n    transform: translateX(0);\n  }\n  10%, 50% {\n    transform: translateX(-10px);\n  }\n  20%, 60% {\n    transform: translateX(10px);\n  }\n  30%, 70% {\n    transform: translateX(-8px);\n  }\n  40%, 80% {\n    transform: translateX(6px);\n  }\n  90% {\n    transform: translateX(-4px);\n  }\n}\n\n.shake {\n  will-change: transform;\n  animation: shake 500ms ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .shake {\n    animation: none;\n  }\n}"
    },
    {
      "label": "Tailwind CSS",
      "language": "css",
      "code": "/* Add to your global CSS or Tailwind v4 theme */\n@keyframes shake {\n  0%, 100% {\n    transform: translateX(0);\n  }\n  10%, 50% {\n    transform: translateX(-10px);\n  }\n  20%, 60% {\n    transform: translateX(10px);\n  }\n  30%, 70% {\n    transform: translateX(-8px);\n  }\n  40%, 80% {\n    transform: translateX(6px);\n  }\n  90% {\n    transform: translateX(-4px);\n  }\n}\n\n@utility animate-shake {\n  will-change: transform;\n  animation: shake 500ms ease-in-out;\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .shake {\n    animation: none;\n  }\n}"
    },
    {
      "label": "AnimeJS",
      "language": "tsx",
      "code": "\"use client\";\n\nimport { useEffect, useRef, type ReactNode } from \"react\";\n\ntype AnimeAnimation = {\n  cancel(): unknown;\n};\n\nfunction prefersReducedMotion() {\n  return window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n}\n\nexport function ShakeFeedback({ children }: { children: ReactNode }) {\n  const targetRef = useRef<HTMLDivElement>(null);\n  const animationRef = useRef<AnimeAnimation | null>(null);\n\n  useEffect(() => {\n    return () => {\n      animationRef.current?.cancel();\n    };\n  }, []);\n\n  async function triggerShake() {\n    const target = targetRef.current;\n    if (!target || prefersReducedMotion()) return;\n\n    const { animate } = await import(\"animejs\");\n    if (targetRef.current !== target) return;\n\n    animationRef.current?.cancel();\n    target.style.transform = \"\";\n\n    animationRef.current = animate(target, {\n      x: [0, -10, 10, -8, 8, -6, 4, -2, 0],\n      duration: 500,\n      ease: \"inOut(2)\",\n    });\n  }\n\n  return (\n    <div>\n      <div ref={targetRef} className=\"will-change-transform\">\n        {children}\n      </div>\n      <button type=\"button\" onClick={() => void triggerShake()}>\n        Trigger shake\n      </button>\n    </div>\n  );\n}"
    },
    {
      "label": "Framer Motion",
      "language": "tsx",
      "code": "import { motion } from \"framer-motion\";\n\n// Tip: respect user's motion preferences\n// import { useReducedMotion } from \"framer-motion\";\n// const reduced = useReducedMotion();\n// Skip shake animation entirely when reduced is true\n\n<motion.div\n  animate={{\n    x: [0, -10, 10, -8, 6, -4, 0],\n  }}\n  transition={{\n    duration: 0.5,\n    ease: \"easeInOut\",\n    times: [0, 0.15, 0.3, 0.45, 0.6, 0.75, 1],\n  }}\n>\n  Content\n</motion.div>"
    }
  ]
}