安装
npx skills add https://github.com/wshobson/agents --skill interaction-design
- Interaction Design
- Create engaging, intuitive interactions through motion, feedback, and thoughtful state transitions that enhance usability and delight users.
- When to Use This Skill
- Adding microinteractions to enhance user feedback
- Implementing smooth page and component transitions
- Designing loading states and skeleton screens
- Creating gesture-based interactions
- Building notification and toast systems
- Implementing drag-and-drop interfaces
- Adding scroll-triggered animations
- Designing hover and focus states
- Core Principles
- 1. Purposeful Motion
- Motion should communicate, not decorate:
- Feedback
-
- Confirm user actions occurred
- Orientation
-
- Show where elements come from/go to
- Focus
-
- Direct attention to important changes
- Continuity
-
- Maintain context during transitions
- 2. Timing Guidelines
- Duration
- Use Case
- 100-150ms
- Micro-feedback (hovers, clicks)
- 200-300ms
- Small transitions (toggles, dropdowns)
- 300-500ms
- Medium transitions (modals, page changes)
- 500ms+
- Complex choreographed animations
- 3. Easing Functions
- / Common easings /
- --ease-out
- :
- cubic-bezier
- (
- 0.16
- ,
- 1
- ,
- 0.3
- ,
- 1
- )
- ;
- / Decelerate - entering /
- --ease-in
- :
- cubic-bezier
- (
- 0.55
- ,
- 0
- ,
- 1
- ,
- 0.45
- )
- ;
- / Accelerate - exiting /
- --ease-in-out
- :
- cubic-bezier
- (
- 0.65
- ,
- 0
- ,
- 0.35
- ,
- 1
- )
- ;
- / Both - moving between /
- --spring
- :
- cubic-bezier
- (
- 0.34
- ,
- 1.56
- ,
- 0.64
- ,
- 1
- )
- ;
- / Overshoot - playful /
- Quick Start: Button Microinteraction
- import
- {
- motion
- }
- from
- "framer-motion"
- ;
- export
- function
- InteractiveButton
- (
- {
- children
- ,
- onClick
- }
- )
- {
- return
- (
- <
- motion.button
- onClick
- =
- {
- onClick
- }
- whileHover
- =
- {
- {
- scale
- :
- 1.02
- }
- }
- whileTap
- =
- {
- {
- scale
- :
- 0.98
- }
- }
- transition
- =
- {
- {
- type
- :
- "spring"
- ,
- stiffness
- :
- 400
- ,
- damping
- :
- 17
- }
- }
- className
- =
- "
- px-4 py-2 bg-blue-600 text-white rounded-lg
- "
- >
- {
- children
- }
- </
- motion.button
- >
- )
- ;
- }
- Interaction Patterns
- 1. Loading States
- Skeleton Screens
-
- Preserve layout while loading
- function
- CardSkeleton
- (
- )
- {
- return
- (
- <
- div
- className
- =
- "
- animate-pulse
- "
- >
- <
- div
- className
- =
- "
- h-48 bg-gray-200 rounded-lg
- "
- />
- <
- div
- className
- =
- "
- mt-4 h-4 bg-gray-200 rounded w-3/4
- "
- />
- <
- div
- className
- =
- "
- mt-2 h-4 bg-gray-200 rounded w-1/2
- "
- />
- </
- div
- >
- )
- ;
- }
- Progress Indicators
-
- Show determinate progress
- function
- ProgressBar
- (
- {
- progress
- }
- :
- {
- progress
- :
- number
- }
- )
- {
- return
- (
- <
- div
- className
- =
- "
- h-2 bg-gray-200 rounded-full overflow-hidden
- "
- >
- <
- motion.div
- className
- =
- "
- h-full bg-blue-600
- "
- initial
- =
- {
- {
- width
- :
- 0
- }
- }
- animate
- =
- {
- {
- width
- :
- `
- ${
- progress
- }
- %
- `
- }
- }
- transition
- =
- {
- {
- ease
- :
- "easeOut"
- }
- }
- />
- </
- div
- >
- )
- ;
- }
- 2. State Transitions
- Toggle with smooth transition
- :
- function
- Toggle
- (
- {
- checked
- ,
- onChange
- }
- )
- {
- return
- (
- <
- button
- role
- =
- "
- switch
- "
- aria-checked
- =
- {
- checked
- }
- onClick
- =
- {
- (
- )
- =>
- onChange
- (
- !
- checked
- )
- }
- className
- =
- {
- `
- relative w-12 h-6 rounded-full transition-colors duration-200
- ${
- checked
- ?
- "bg-blue-600"
- :
- "bg-gray-300"
- }
- `
- }
- >
- <
- motion.span
- className
- =
- "
- absolute top-1 left-1 w-4 h-4 bg-white rounded-full shadow
- "
- animate
- =
- {
- {
- x
- :
- checked
- ?
- 24
- :
- 0
- }
- }
- transition
- =
- {
- {
- type
- :
- "spring"
- ,
- stiffness
- :
- 500
- ,
- damping
- :
- 30
- }
- }
- />
- </
- button
- >
- )
- ;
- }
- 3. Page Transitions
- Framer Motion layout animations
- :
- import
- {
- AnimatePresence
- ,
- motion
- }
- from
- "framer-motion"
- ;
- function
- PageTransition
- (
- {
- children
- ,
- key
- }
- )
- {
- return
- (
- <
- AnimatePresence
- mode
- =
- "
- wait
- "
- >
- <
- motion.div
- key
- =
- {
- key
- }
- initial
- =
- {
- {
- opacity
- :
- 0
- ,
- y
- :
- 20
- }
- }
- animate
- =
- {
- {
- opacity
- :
- 1
- ,
- y
- :
- 0
- }
- }
- exit
- =
- {
- {
- opacity
- :
- 0
- ,
- y
- :
- -
- 20
- }
- }
- transition
- =
- {
- {
- duration
- :
- 0.3
- }
- }
- >
- {
- children
- }
- </
- motion.div
- >
- </
- AnimatePresence
- >
- )
- ;
- }
- 4. Feedback Patterns
- Ripple effect on click
- :
- function
- RippleButton
- (
- {
- children
- ,
- onClick
- }
- )
- {
- const
- [
- ripples
- ,
- setRipples
- ]
- =
- useState
- (
- [
- ]
- )
- ;
- const
- handleClick
- =
- (
- e
- )
- =>
- {
- const
- rect
- =
- e
- .
- currentTarget
- .
- getBoundingClientRect
- (
- )
- ;
- const
- ripple
- =
- {
- x
- :
- e
- .
- clientX
- -
- rect
- .
- left
- ,
- y
- :
- e
- .
- clientY
- -
- rect
- .
- top
- ,
- id
- :
- Date
- .
- now
- (
- )
- ,
- }
- ;
- setRipples
- (
- (
- prev
- )
- =>
- [
- ...
- prev
- ,
- ripple
- ]
- )
- ;
- setTimeout
- (
- (
- )
- =>
- {
- setRipples
- (
- (
- prev
- )
- =>
- prev
- .
- filter
- (
- (
- r
- )
- =>
- r
- .
- id
- !==
- ripple
- .
- id
- )
- )
- ;
- }
- ,
- 600
- )
- ;
- onClick
- ?.
- (
- e
- )
- ;
- }
- ;
- return
- (
- <
- button
- onClick
- =
- {
- handleClick
- }
- className
- =
- "
- relative overflow-hidden
- "
- >
- {
- children
- }
- {
- ripples
- .
- map
- (
- (
- ripple
- )
- =>
- (
- <
- span
- key
- =
- {
- ripple
- .
- id
- }
- className
- =
- "
- absolute bg-white/30 rounded-full animate-ripple
- "
- style
- =
- {
- {
- left
- :
- ripple
- .
- x
- ,
- top
- :
- ripple
- .
- y
- }
- }
- />
- )
- )
- }
- </
- button
- >
- )
- ;
- }
- 5. Gesture Interactions
- Swipe to dismiss
- :
- function
- SwipeCard
- (
- {
- children
- ,
- onDismiss
- }
- )
- {
- return
- (
- <
- motion.div
- drag
- =
- "
- x
- "
- dragConstraints
- =
- {
- {
- left
- :
- 0
- ,
- right
- :
- 0
- }
- }
- onDragEnd
- =
- {
- (
- _
- ,
- info
- )
- =>
- {
- if
- (
- Math
- .
- abs
- (
- info
- .
- offset
- .
- x
- )
- >
- 100
- )
- {
- onDismiss
- (
- )
- ;
- }
- }
- }
- className
- =
- "
- cursor-grab active:cursor-grabbing
- "
- >
- {
- children
- }
- </
- motion.div
- >
- )
- ;
- }
- CSS Animation Patterns
- Keyframe Animations
- @keyframes
- fadeIn
- {
- from
- {
- opacity
- :
- 0
- ;
- transform
- :
- translateY
- (
- 10
- px
- )
- ;
- }
- to
- {
- opacity
- :
- 1
- ;
- transform
- :
- translateY
- (
- 0
- )
- ;
- }
- }
- @keyframes
- pulse
- {
- 0%
- ,
- 100%
- {
- opacity
- :
- 1
- ;
- }
- 50%
- {
- opacity
- :
- 0.5
- ;
- }
- }
- @keyframes
- spin
- {
- to
- {
- transform
- :
- rotate
- (
- 360
- deg
- )
- ;
- }
- }
- .animate-fadeIn
- {
- animation
- :
- fadeIn
- 0.3
- s
- ease-out
- ;
- }
- .animate-pulse
- {
- animation
- :
- pulse
- 2
- s
- ease-in-out infinite
- ;
- }
- .animate-spin
- {
- animation
- :
- spin
- 1
- s
- linear infinite
- ;
- }
- CSS Transitions
- .card
- {
- transition
- :
- transform
- 0.2
- s
- ease-out
- ,
- box-shadow
- 0.2
- s
- ease-out
- ;
- }
- .card
- :hover
- {
- transform
- :
- translateY
- (
- -4
- px
- )
- ;
- box-shadow
- :
- 0
- 12
- px
- 24
- px
- rgba
- (
- 0
- ,
- 0
- ,
- 0
- ,
- 0.1
- )
- ;
- }
- Accessibility Considerations
- / Respect user motion preferences /
- @media
- (
- prefers-reduced-motion
- :
- reduce
- )
- {
- *
- ,
- *
- ::before
- ,
- *
- ::after
- {
- animation-duration
- :
- 0.01
- ms
- !important
- ;
- animation-iteration-count
- :
- 1
- !important
- ;
- transition-duration
- :
- 0.01
- ms
- !important
- ;
- }
- }
- function
- AnimatedComponent
- (
- )
- {
- const
- prefersReducedMotion
- =
- window
- .
- matchMedia
- (
- "(prefers-reduced-motion: reduce)"
- ,
- )
- .
- matches
- ;
- return
- (
- <
- motion.div
- animate
- =
- {
- {
- opacity
- :
- 1
- }
- }
- transition
- =
- {
- {
- duration
- :
- prefersReducedMotion
- ?
- 0
- :
- 0.3
- }
- }
- />
- )
- ;
- }
- Best Practices
- Performance First
-
- Use
- transform
- and
- opacity
- for smooth 60fps
- Reduce Motion Support
-
- Always respect
- prefers-reduced-motion
- Consistent Timing
-
- Use a timing scale across the app
- Natural Physics
-
- Prefer spring animations over linear
- Interruptible
-
- Allow users to cancel long animations
- Progressive Enhancement
-
- Work without JS animations
- Test on Devices
-
- Performance varies significantly
- Common Issues
- Janky Animations
-
- Avoid animating
- width
- ,
- height
- ,
- top
- ,
- left
- Over-animation
-
- Too much motion causes fatigue
- Blocking Interactions
-
- Never prevent user input during animations
- Memory Leaks
-
- Clean up animation listeners on unmount
- Flash of Content
- Use
will-change
sparingly for optimization
← 返回排行榜