framer-motion-animator

安装量: 1.6K
排名: #965

安装

npx skills add https://github.com/patricio0312rev/skills --skill framer-motion-animator

Framer Motion Animator

Build delightful animations and interactions with Framer Motion's declarative API.

Core Workflow Identify animation needs: Entrance, exit, hover, gestures Choose animation type: Simple, variants, gestures, layout Define motion values: Opacity, scale, position, rotation Add transitions: Duration, easing, spring physics Orchestrate sequences: Stagger, delay, parent-child Optimize performance: GPU-accelerated properties Installation npm install framer-motion

Basic Animations Simple Animation import { motion } from 'framer-motion';

// Animate on mount export function FadeIn({ children }: { children: React.ReactNode }) { return ( {children} ); }

// Animate on hover export function ScaleOnHover({ children }: { children: React.ReactNode }) { return ( {children} ); }

Exit Animations with AnimatePresence import { motion, AnimatePresence } from 'framer-motion';

export function Modal({ isOpen, onClose, children }: ModalProps) { return ( {isOpen && ( <> {/ Backdrop /}

      {/* Modal */}
      <motion.div
        initial={{ opacity: 0, scale: 0.95, y: 20 }}
        animate={{ opacity: 1, scale: 1, y: 0 }}
        exit={{ opacity: 0, scale: 0.95, y: 20 }}
        transition={{ type: 'spring', damping: 25, stiffness: 300 }}
        className="fixed inset-0 z-50 flex items-center justify-center"
      >
        <div className="bg-white rounded-xl p-6 max-w-md w-full">
          {children}
        </div>
      </motion.div>
    </>
  )}
</AnimatePresence>

); }

Variants Pattern Staggered Children const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.1, delayChildren: 0.2, }, }, };

const itemVariants = { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0, transition: { type: 'spring', stiffness: 300, damping: 24 }, }, };

export function StaggeredList({ items }: { items: string[] }) { return ( {items.map((item, index) => ( {item} ))} ); }

Interactive Variants const buttonVariants = { initial: { scale: 1 }, hover: { scale: 1.05 }, tap: { scale: 0.95 }, disabled: { opacity: 0.5, scale: 1 }, };

export function AnimatedButton({ children, disabled, onClick, }: ButtonProps) { return ( {children} ); }

Page Transitions Next.js App Router // app/template.tsx 'use client';

import { motion } from 'framer-motion';

export default function Template({ children }: { children: React.ReactNode }) { return ( {children} ); }

Shared Layout Animations import { motion, LayoutGroup } from 'framer-motion';

export function Tabs({ tabs, activeTab, onTabChange }: TabsProps) { return (

{tabs.map((tab) => ( ))}
); }

Gesture Animations Drag export function DraggableCard() { return ( ); }

Swipe to Dismiss export function SwipeToDelete({ onDelete, children }: SwipeProps) { return ( { if (info.offset.x < -100) { onDelete(); } }} className="relative" > {children} Delete ); }

Scroll Animations Scroll-Triggered import { motion, useInView } from 'framer-motion'; import { useRef } from 'react';

export function FadeInWhenVisible({ children }: { children: React.ReactNode }) { const ref = useRef(null); const isInView = useInView(ref, { once: true, margin: '-100px' });

return ( {children} ); }

Scroll Progress import { motion, useScroll, useTransform } from 'framer-motion';

export function ParallaxHero() { const { scrollY } = useScroll(); const y = useTransform(scrollY, [0, 500], [0, 150]); const opacity = useTransform(scrollY, [0, 300], [1, 0]);

return (

Parallax Hero

); }

export function ScrollProgress() { const { scrollYProgress } = useScroll();

return ( ); }

Animation Hooks useAnimate (Imperative) import { useAnimate } from 'framer-motion';

export function SubmitButton() { const [scope, animate] = useAnimate();

const handleClick = async () => { // Sequence of animations await animate(scope.current, { scale: 0.95 }, { duration: 0.1 }); await animate(scope.current, { scale: 1 }, { type: 'spring' });

// Success animation
await animate(
  scope.current,
  { backgroundColor: '#22c55e' },
  { duration: 0.2 }
);

};

return ( Submit ); }

useMotionValue & useTransform import { motion, useMotionValue, useTransform } from 'framer-motion';

export function RotatingCard() { const x = useMotionValue(0); const rotateY = useTransform(x, [-200, 200], [-45, 45]); const opacity = useTransform(x, [-200, 0, 200], [0.5, 1, 0.5]);

return ( ); }

Reusable Animation Components AnimatedContainer // components/AnimatedContainer.tsx import { motion, Variants } from 'framer-motion';

const animations: Record = { fadeIn: { hidden: { opacity: 0 }, visible: { opacity: 1 }, }, fadeInUp: { hidden: { opacity: 0, y: 20 }, visible: { opacity: 1, y: 0 }, }, fadeInDown: { hidden: { opacity: 0, y: -20 }, visible: { opacity: 1, y: 0 }, }, scaleIn: { hidden: { opacity: 0, scale: 0.8 }, visible: { opacity: 1, scale: 1 }, }, slideInLeft: { hidden: { opacity: 0, x: -50 }, visible: { opacity: 1, x: 0 }, }, slideInRight: { hidden: { opacity: 0, x: 50 }, visible: { opacity: 1, x: 0 }, }, };

interface AnimatedContainerProps { children: React.ReactNode; animation?: keyof typeof animations; delay?: number; duration?: number; className?: string; }

export function AnimatedContainer({ children, animation = 'fadeInUp', delay = 0, duration = 0.5, className, }: AnimatedContainerProps) { return ( {children} ); }

AnimatedList // components/AnimatedList.tsx import { motion } from 'framer-motion';

const containerVariants = { hidden: { opacity: 0 }, visible: { opacity: 1, transition: { staggerChildren: 0.05, }, }, };

const itemVariants = { hidden: { opacity: 0, x: -20 }, visible: { opacity: 1, x: 0 }, };

interface AnimatedListProps { items: T[]; renderItem: (item: T, index: number) => React.ReactNode; keyExtractor: (item: T, index: number) => string; className?: string; }

export function AnimatedList({ items, renderItem, keyExtractor, className, }: AnimatedListProps) { return ( {items.map((item, index) => ( {renderItem(item, index)} ))} ); }

Transition Presets // lib/transitions.ts export const transitions = { spring: { type: 'spring', stiffness: 300, damping: 24, }, springBouncy: { type: 'spring', stiffness: 500, damping: 15, }, springStiff: { type: 'spring', stiffness: 700, damping: 30, }, smooth: { type: 'tween', duration: 0.3, ease: 'easeInOut', }, snappy: { type: 'tween', duration: 0.15, ease: [0.25, 0.1, 0.25, 1], }, } as const;

// Usage

Reduced Motion Support import { useReducedMotion } from 'framer-motion';

export function AccessibleAnimation({ children }: { children: React.ReactNode }) { const shouldReduceMotion = useReducedMotion();

return ( {children} ); }

Best Practices Use GPU-accelerated properties: opacity, transform (not width, height) Add layout for smooth resizing: Automatic layout animations Use AnimatePresence: For exit animations Prefer springs: More natural than tween for UI Respect reduced motion: Use useReducedMotion hook Avoid animating layout thrashing: Don't animate top, left, width Use layoutId: For shared element transitions Stagger children: For list animations Output Checklist

Every animation implementation should include:

Appropriate animation type (simple, variants, gestures) Smooth transitions with proper easing Exit animations with AnimatePresence Reduced motion support GPU-accelerated properties only Spring physics for natural feel Staggered children for lists Performance tested on low-end devices

返回排行榜