Communitygithub.com

David-Li0406/meta-skill-evloving

SwiftUI animations, @Animatable macro, withAnimation, transitions, PhaseAnimator, KeyframeAnimator, and interactive motion design. Use when user asks about animations, transitions, @Animatable, withAnimation, spring animations, keyframes, or motion design.

Qu'est-ce que meta-skill-evloving ?

meta-skill-evloving is a Claude Code agent skill that swiftUI animations, @Animatable macro, withAnimation, transitions, PhaseAnimator, KeyframeAnimator, and interactive motion design. Use when user asks about animations, transitions, @Animatable, withAnimation, spring animations, keyframes, or motion design.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/David-Li0406/meta-skill-evloving/tree/HEAD/skill-flow/data/skills-refined-36k/skillsmp/animations-transitions

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

SwiftUI Animations and Transitions

Comprehensive guide to SwiftUI animations, the new @Animatable macro (iOS 26), transitions, and motion design best practices for modern iOS development.

Prerequisites

  • iOS 17+ for PhaseAnimator/KeyframeAnimator
  • iOS 26+ for @Animatable macro
  • Xcode 26+

@Animatable Macro (iOS 26 - NEW!)

The Revolution in Custom Animations

iOS 26 introduces the @Animatable macro, eliminating the tedious boilerplate previously required for animating custom shapes and views.

Before iOS 26 (Manual Approach)

// OLD WAY - Lots of boilerplate
struct PieSlice: Shape {
    var startAngle: Angle
    var endAngle: Angle

    // Manual animatableData implementation required
    var animatableData: AnimatablePair<Double, Double> {
        get {
            AnimatablePair(startAngle.radians, endAngle.radians)
        }
        set {
            startAngle = Angle(radians: newValue.first)
            endAngle = Angle(radians: newValue.second)
        }
    }

    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2
        path.move(to: center)
        path.addArc(center: center, radius: radius,
                    startAngle: startAngle, endAngle: endAngle,
                    clockwise: false)
        path.closeSubpath()
        return path
    }
}

After iOS 26 (With @Animatable)

// NEW WAY - Just add @Animatable
@Animatable
struct PieSlice: Shape {
    var startAngle: Angle
    var endAngle: Angle

    func path(in rect: CGRect) -> Path {
        var path = Path()
        let center = CGPoint(x: rect.midX, y: rect.midY)
        let radius = min(rect.width, rect.height) / 2
        path.move(to: center)
        path.addArc(center: center, radius: radius,
                    startAngle: startAngle, endAngle: endAngle,
                    clockwise: false)
        path.closeSubpath()
        return path
    }
}

@AnimatableIgnored

Exclude properties from animation:

@Animatable
struct CustomShape: Shape {
    var animatedValue: CGFloat
    @AnimatableIgnored var staticConfiguration: Bool  // Not animated

    func path(in rect: CGRect) -> Path {
        // Use both values, but only animatedValue will animate
    }
}

Supported Types for Animation

The @Animatable macro automatically handles:

  • CGFloat, Double, Float
  • Angle
  • CGSize, CGPoint, CGRect
  • UnitPoint
  • Color (component interpolation)
  • Custom types conforming to VectorArithmetic

Complex Example with Multiple Properties

@Animatable
struct MorphingShape: Shape {
    var cornerRadius: CGFloat
    var insetAmount: CGFloat
    var rotation: Angle
    @AnimatableIgnored var fillColor: Color

    func path(in rect: CGRect) -> Path {
        let insetRect = rect.insetBy(dx: insetAmount, dy: insetAmount)
        var path = Path(roundedRect: insetRect, cornerRadius: cornerRadius)

        let transform = CGAffineTransform(rotationAngle: rotation.radians)
        return path.applying(transform)
    }
}

// Usage
struct MorphingView: View {
    @State private var isExpanded = false

    var body: some View {
        MorphingShape(
            cornerRadius: isExpanded ? 50 : 10,
            insetAmount: isExpanded ? 20 : 50,
            rotation: isExpanded ? .degrees(45) : .zero,
            fillColor: .blue
        )
        .fill(.blue)
        .frame(width: 200, height: 200)
        .onTapGesture {
            withAnimation(.spring(duration: 0.6, bounce: 0.3)) {
                isExpanded.toggle()
            }
        }
    }
}

withAnimation

Basic Usage

struct ContentView: View {
    @State private var isExpanded = false

    var body: some View {
        VStack {
            Rectangle()
                .frame(width: isExpanded ? 200 : 100,
                       height: isExpanded ? 200 : 100)

            Button("Toggle") {
                withAnimation {
                    isExpanded.toggle()
                }
            }
        }
    }
}

With Animation Type

withAnimation(.spring(duration: 0.5, bounce: 0.3)) {
    isExpanded.toggle()
}

withAnimation(.easeInOut(duration: 0.3)) {
    opacity = 1.0
}

withAnimation(.linear(duration: 1.0)) {
    progress = 1.0
}

Completion Handler (iOS 17+)

withAnimation(.easeInOut(duration: 0.5)) {
    showDetails = true
} completion: {
    // Called when animation completes
    print("Animation finished")
    fetchMoreData()
}

Nested Animations with Different Timings

Button("Animate") {
    withAnimation(.spring(duration: 0.4)) {
        isExpanded = true
    }

    withAnimation(.easeOut(duration: 0.6).delay(0.2)) {
        opacity = 1.0
    }
}

Animation Types

Built-in Animations

// Linear - constant speed
.linear(duration: 0.3)

// Ease variants - acceleration/deceleration
.easeIn(duration: 0.3)      // Slow start
.easeOut(duration: 0.3)     // Slow end
.easeInOut(duration: 0.3)   // Slow both

// Default (ease in/out)
.default

Spring Animations (Modern)

// Modern spring with duration and bounce
.spring(duration: 0.5, bounce: 0.3)

// Bounce values:
// 0.0 = no bounce (critically damped)
// 0.5 = medium bounce
// 1.0 = maximum bounce (never settles)

// Extra bounce parameter for overshoot
.spring(duration: 0.5, bounce: 0.4, blendDuration: 0.2)

Spring Presets

// Bouncy - playful, energetic
.bouncy
.bouncy(duration: 0.4, extraBounce: 0.1)

// Snappy - quick, responsive
.snappy
.snappy(duration: 0.3, extraBounce: 0.05)

// Smooth - gentle, elegant
.smooth
.smooth(duration: 0.5, extraBounce: 0.0)

Interactive Spring

// For gesture-driven animations
.interactiveSpring()
.interactiveSpring(response: 0.3, dampingFraction: 0.7)

// Best for drag gestures
.interactiveSpring(response: 0.15, dampingFraction: 0.86, blendDuration: 0.25)

Custom Timing Curves

// Bezier curve timing
.timingCurve(0.2, 0.8, 0.2, 1.0, duration: 0.5)

// Parameters: (x1, y1, x2, y2)
// Start: (0, 0), End: (1, 1)
// Control points define the curve shape

Animation Modifiers

// Delay before starting
.spring().delay(0.2)

// Speed multiplier
.spring().speed(2.0)  // 2x faster

// Repeat
.linear(duration: 1.0).repeatCount(3)
.linear(duration: 1.0).repeatForever()

// Autoreverse
.easeInOut(duration: 0.5).repeatForever(autoreverses: true)

Explicit Animation Modifier

Preferred Approach

struct ContentView: View {
    @State private var scale = 1.0

    var body: some View {
        Circle()
            .scaleEffect(scale)
            // Explicit: animate only when scale changes
            .animation(.spring, value: scale)
            .onTapGesture {
                scale = scale == 1.0 ? 1.5 : 1.0
            }
    }
}

Why Explicit is Better

// AVOID: Implicit animation (animates everything)
Circle()
    .animation(.spring)  // Deprecated warning in newer iOS

// PREFER: Explicit animation (precise control)
Circle()
    .animation(.spring, value: specificValue)

Multiple Explicit Animations

struct MultiAnimatedView: View {
    @State private var scale = 1.0
    @State private var opacity = 1.0
    @State private var rotation = 0.0

    var body: some View {
        Rectangle()
            .scaleEffect(scale)
            .opacity(opacity)
            .rotationEffect(.degrees(rotation))
            // Different animations for different properties
            .animation(.bouncy, value: scale)
            .animation(.easeOut(duration: 0.2), value: opacity)
            .animation(.spring(duration: 1.0), value: rotation)
    }
}

Transitions

Basic Transitions

struct ContentView: View {
    @State private var showDetail = false

    var body: some View {
        VStack {
            if showDetail {
                DetailView()
                    .transition(.slide)
            }

            Button("Toggle") {
                withAnimation {
                    showDetail.toggle()
                }
            }
        }
    }
}

Built-in Transitions

.transition(.opacity)           // Fade in/out
.transition(.scale)             // Scale from center
.transition(.scale(scale: 0.5)) // Scale from 50%
.transition(.slide)             // Slide from leading edge
.transition(.move(edge: .top))  // Move from specific edge
.transition(.push(from: .bottom)) // Push with replacement
.transition(.offset(x: 100, y: 0))  // Custom offset

Combined Transitions

// Combine multiple transitions
.transition(.scale.combined(with: .opacity))

// Chain combinations
.transition(
    .scale(scale: 0.8)
    .combined(with: .opacity)
    .combined(with: .offset(y: 20))
)

Asymmetric Transitions

// Different transitions for insert vs removal
.transition(.asymmetric(
    insertion: .scale.combined(with: .opacity),
    removal: .slide
))

// Common pattern: slide in from one side, out the other
.transition(.asymmetric(
    insertion: .push(from: .trailing),
    removal: .push(from: .leading)
))

Custom Transitions

extension AnyTransition {
    static var flipFromBottom: AnyTransition {
        .modifier(
            active: FlipModifier(angle: -90),
            identity: FlipModifier(angle: 0)
        )
    }
}

struct FlipModifier: ViewModifier {
    let angle: Double

    func body(content: Content) -> some View {
        content
            .rotation3DEffect(
                .degrees(angle),
                axis: (x: 1, y: 0, z: 0)
            )
            .opacity(angle == 0 ? 1 : 0)
    }
}

// Usage
DetailView()
    .transition(.flipFromBottom)

Phase Animator (iOS 17+)

Basic Usage

enum AnimationPhase: CaseIterable {
    case initial
    case middle
    case final

    var scale: CGFloat {
        switch self {
        case .initial: return 1.0
        case .middle: return 1.2
        case .final: return 1.0
        }
    }

    var opacity: Double {
        switch self {
        case .initial: return 1.0
        case .middle: return 0.5
        case .final: return 1.0
        }
    }
}

struct PulsingView: View {
    var body: some View {
        PhaseAnimator(AnimationPhase.allCases) { phase in
            Circle()
                .fill(.blue)
                .scaleEffect(phase.scale)
                .opacity(phase.opacity)
        }
    }
}

Triggered Animation

struct TriggerableAnimation: View {
    @State private var trigger = false

    var body: some View {
        VStack {
            PhaseAnimator(
                AnimationPhase.allCases,
                trigger: trigger
            ) { phase in
                Star()
                    .scaleEffect(phase.scale)
                    .rotationEffect(.degrees(phase.rotation))
            }

            Button("Animate") {
                trigger.toggle()
            }
        }
    }
}

Custom Animation Per Phase

PhaseAnimator(AnimationPhase.allCases) { phase in
    ContentView(phase: phase)
} animation: { phase in
    switch phase {
    case .initial: .spring(duration: 0.3)
    case .middle: .easeOut(duration: 0.2)
    case .final: .bouncy
    }
}

Keyframe Animator (iOS 17+)

Basic Keyframe Animation

struct AnimationValues {
    var scale = 1.0
    var rotation = 0.0
    var yOffset = 0.0
}

struct BouncingView: View {
    @State private var trigger = false

    var body: some View {
        Circle()
            .fill(.blue)
            .frame(width: 100, height: 100)
            .keyframeAnimator(
                initialValue: AnimationValues(),
                trigger: trigger
            ) { content, value in
                content
                    .scaleEffect(value.scale)
                    .rotationEffect(.degrees(value.rotation))
                    .offset(y: value.yOffset)
            } keyframes: { _ in
                KeyframeTrack(\.scale) {
                    SpringKeyframe(1.2, duration: 0.2)
                    SpringKeyframe(0.9, duration: 0.15)
                    SpringKeyframe(1.0, duration: 0.15)
                }

                KeyframeTrack(\.rotation) {
                    LinearKeyframe(0, duration: 0.1)
                    SpringKeyframe(10, duration: 0.15)
                    SpringKeyframe(-10, duration: 0.15)
                    SpringKeyframe(0, duration: 0.1)
                }

                KeyframeTrack(\.yOffset) {
                    SpringKeyframe(-30, duration: 0.2)
                    SpringKeyframe(0, duration: 0.3)
                }
            }
            .onTapGesture {
                trigger.toggle()
            }
    }
}

Keyframe Types

KeyframeTrack(\.value) {
    // Linear interpolation
    LinearKeyframe(targetValue, duration: 0.3)

    // Spring-based interpolation
    SpringKeyframe(targetValue, duration: 0.3)
    SpringKeyframe(targetValue, duration: 0.3, spring: .bouncy)

    // Cubic bezier interpolation
    CubicKeyframe(targetValue, duration: 0.3)

    // Move without animation
    MoveKeyframe(targetValue)
}

Complex Multi-Track Animation

struct ComplexAnimationValues {
    var xOffset = 0.0
    var yOffset = 0.0
    var scale = 1.0
    var opacity = 1.0
    var blur = 0.0
}

struct ComplexAnimation: View {
    @State private var animating = false

    var body: some View {
        Image(systemName: "star.fill")
            .font(.system(size: 50))
            .keyframeAnimator(
                initialValue: ComplexAnimationValues(),
                repeating: animating
            ) { content, value in
                content
                    .offset(x: value.xOffset, y: value.yOffset)
                    .scaleEffect(value.scale)
                    .opacity(value.opacity)
                    .blur(radius: value.blur)
            } keyframes: { _ in
                KeyframeTrack(\.xOffset) {
                    LinearKeyframe(0, duration: 0.25)
                    LinearKeyframe(100, duration: 0.5)
                    LinearKeyframe(100, duration: 0.25)
                    LinearKeyframe(0, duration: 0.5)
                }

                KeyframeTrack(\.yOffset) {
                    SpringKeyframe(-50, duration: 0.5)
                    SpringKeyframe(0, duration: 0.5)
                }

                KeyframeTrack(\.scale) {
                    SpringKeyframe(1.5, duration: 0.25)
                    SpringKeyframe(1.0, duration: 0.25)
                    SpringKeyframe(1.2, duration: 0.25)
                    SpringKeyframe(1.0, duration: 0.25)
                }
            }
            .onAppear {
                animating = true
            }
    }
}

Matched Geometry Effect

Hero Transitions

struct HeroTransition: View {
    @Namespace private var animation
    @State private var isExpanded = false

    var body: some View {
        VStack {
            if isExpanded {
                // Expanded state
                RoundedRectangle(cornerRadius: 20)
                    .fill(.blue)
                    .matchedGeometryEffect(id: "card", in: animation)
                    .frame(width: 300, height: 400)
            } else {
                // Collapsed state
                RoundedRectangle(cornerRadius: 10)
                    .fill(.blue)
                    .matchedGeometryEffect(id: "card", in: animation)
                    .frame(width: 100, height: 100)
            }
        }
        .onTapGesture {
            withAnimation(.spring(duration: 0.5, bounce: 0.3)) {
                isExpanded.toggle()
            }
        }
    }
}

Tab Bar Selection Indicator

struct TabBar: View {
    @Namespace private var animation
    @State private var selectedTab = 0
    let tabs = ["Home", "Search", "Profile"]

    var body: some View {
        HStack {
            ForEach(Array(tabs.enumerated()), id: \.offset) { index, title in
                Button(title) {
                    withAnimation(.spring(duration: 0.3)) {
                        selectedTab = index
                    }
                }
                .padding()
                .background {
                    if selectedTab == index {
                        Capsule()
                            .fill(.blue.opacity(0.2))
                            .matchedGeometryEffect(id: "background", in: animation)
                    }
                }
            }
        }
    }
}

Properties for Matched Geometry

.matchedGeometryEffect(
    id: "identifier",
    in: namespace,
    properties: .frame,        // What to match: .frame, .position, .size
    anchor: .center,           // Anchor point for matching
    isSource: true             // Whether this is the source of truth
)

Content Transition

Text Morphing

struct CounterView: View {
    @State private var count = 0

    var body: some View {
        Text("\(count)")
            .font(.largeTitle)
            .contentTransition(.numericText())
            .onTapGesture {
                withAnimation {
                    count += 1
                }
            }
    }
}

Available Content Transitions

// Numeric text morphing
.contentTransition(.numericText())
.contentTransition(.numericText(value: count))
.contentTransition(.numericText(countsDown: true))

// Interpolate between text
.contentTransition(.interpolate)

// Identity (no transition)
.contentTransition(.identity)

// Opacity crossfade
.contentTransition(.opacity)

// Symbol effect
.contentTransition(.symbolEffect(.replace))

Symbol Effects

SF Symbol Animations

struct SymbolEffectsView: View {
    @State private var isActive = false

    var body: some View {
        VStack(spacing: 30) {
            // Bounce effect
            Image(systemName: "bell.fill")
                .symbolEffect(.bounce, value: isActive)

            // Pulse effect
            Image(systemName: "heart.fill")
                .symbolEffect(.pulse)

            // Variable color
            Image(systemName: "wifi")
                .symbolEffect(.variableColor.iterative)

            // Scale effect
            Image(systemName: "star.fill")
                .symbolEffect(.scale.up, isActive: isActive)

            // Replace effect
            Image(systemName: isActive ? "checkmark.circle" : "circle")
                .contentTransition(.symbolEffect(.replace))

            Button("Toggle") {
                withAnimation {
                    isActive.toggle()
                }
            }
        }
        .font(.largeTitle)
    }
}

Symbol Effect Options

// Bounce variations
.symbolEffect(.bounce)
.symbolEffect(.bounce.up)
.symbolEffect(.bounce.down)
.symbolEffect(.bounce.byLayer)
.symbolEffect(.bounce.wholeSymbol)

// Variable color
.symbolEffect(.variableColor)
.symbolEffect(.variableColor.iterative)
.symbolEffect(.variableColor.reversing)
.symbolEffect(.variableColor.cumulative)

// Scale
.symbolEffect(.scale.up)
.symbolEffect(.scale.down)

// Pulse
.symbolEffect(.pulse)
.symbolEffect(.pulse.byLayer)

Interactive Animations

Drag Gesture Animation

struct DraggableCard: View {
    @State private var offset = CGSize.zero
    @State private var isDragging = false

    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .fill(.blue)
            .frame(width: 200, height: 300)
            .offset(offset)
            .scaleEffect(isDragging ? 1.05 : 1.0)
            .animation(.interactiveSpring(response: 0.3), value: isDragging)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        offset = value.translation
                        isDragging = true
                    }
                    .onEnded { value in
                        isDragging = false
                        withAnimation(.spring(duration: 0.5, bounce: 0.3)) {
                            offset = .zero
                        }
                    }
            )
    }
}

Gesture State for Smooth Tracking

struct SmoothDrag: View {
    @GestureState private var dragOffset = CGSize.zero
    @State private var position = CGSize.zero

    var body: some View {
        Circle()
            .fill(.blue)
            .frame(width: 100, height: 100)
            .offset(
                x: position.width + dragOffset.width,
                y: position.height + dragOffset.height
            )
            .animation(.interactiveSpring(), value: dragOffset)
            .gesture(
                DragGesture()
                    .updating($dragOffset) { value, state, _ in
                        state = value.translation
                    }
                    .onEnded { value in
                        position.width += value.translation.width
                        position.height += value.translation.height
                    }
            )
    }
}

Velocity-Based Animation

struct VelocityDrag: View {
    @State private var offset = CGSize.zero

    var body: some View {
        RoundedRectangle(cornerRadius: 20)
            .fill(.blue)
            .frame(width: 200, height: 300)
            .offset(offset)
            .gesture(
                DragGesture()
                    .onChanged { value in
                        offset = value.translation
                    }
                    .onEnded { value in
                        // Use velocity for natural-feeling spring back
                        let velocity = CGSize(
                            width: value.predictedEndTranslation.width - value.translation.width,
                            height: value.predictedEndTranslation.height - value.translation.height
                        )

                        withAnimation(.spring(
                            response: 0.5,
                            dampingFraction: 0.7,
                            blendDuration: 0
                        )) {
                            offset = .zero
                        }
                    }
            )
    }
}

Scroll Animations

Scroll Transition (iOS 17+)

struct ScrollTransitionView: View {
    var body: some View {
        ScrollView {
            LazyVStack(spacing: 20) {
                ForEach(0..<20) { index in
                    RoundedRectangle(cornerRadius: 12)
                        .fill(.blue.gradient)
                        .frame(height: 100)
                        .scrollTransition { content, phase in
                            content
                                .opacity(phase.isIdentity ? 1 : 0.5)
                                .scaleEffect(phase.isIdentity ? 1 : 0.9)
                                .blur(radius: phase.isIdentity ? 0 : 2)
                        }
                }
            }
            .padding()
        }
    }
}

Visual Effect Modifier (iOS 17+)

struct ParallaxScroll: View {
    var body: some View {
        ScrollView {
            LazyVStack(spacing: 0) {
                ForEach(0..<10) { index in
                    Image("photo\(index)")
                        .resizable()
                        .aspectRatio(contentMode: .fill)
                        .frame(height: 300)
                        .clipped()
                        .visualEffect { content, proxy in
                            content
                                .offset(y: parallaxOffset(proxy))
                        }
                }
            }
        }
    }

    func parallaxOffset(_ proxy: GeometryProxy) -> CGFloat {
        let frame = proxy.frame(in: .scrollView)
        return -frame.minY * 0.3
    }
}

Performance Best Practices

1. Use Explicit Animations

// GOOD: Explicit animation tied to specific value
.animation(.spring, value: isExpanded)

// AVOID: Implicit animation (deprecated, less performant)
.animation(.spring)

2. Animate Efficiently

// GOOD: Animate transforms (GPU-accelerated)
.scaleEffect(scale)
.rotationEffect(.degrees(rotation))
.offset(x: offsetX, y: offsetY)
.opacity(opacity)

// AVOID: Animating layout-affecting properties when possible
// (These trigger re-layout each frame)
.frame(width: animatedWidth)
.padding(animatedPadding)

3. Limit Animation Scope

// GOOD: Animation on specific view
ChildView()
    .animation(.spring, value: childState)

// AVOID: Animation on parent affecting all children
ParentView()
    .animation(.spring, value: anyChange)  // All children animate

4. Use drawingGroup for Complex Graphics

// For complex composited views
ComplexAnimatedShape()
    .drawingGroup()  // Renders to offscreen buffer

5. Keep Durations Short

// RECOMMENDED: Under 0.4 seconds for UI feedback
.spring(duration: 0.3, bounce: 0.2)

// Reserve longer animations for:
// - Onboarding flows
// - Celebrations
// - State transitions

6. Test on Device

// Simulator timing is NOT accurate
// Always test animations on physical device
// Different devices have different performance characteristics

Common Patterns

Loading Spinner

struct LoadingSpinner: View {
    @State private var isAnimating = false

    var body: some View {
        Circle()
            .trim(from: 0, to: 0.7)
            .stroke(.blue, lineWidth: 4)
            .frame(width: 40, height: 40)
            .rotationEffect(.degrees(isAnimating ? 360 : 0))
            .animation(
                .linear(duration: 1.0).repeatForever(autoreverses: false),
                value: isAnimating
            )
            .onAppear {
                isAnimating = true
            }
    }
}

Pulsing Indicator

struct PulsingDot: View {
    @State private var isPulsing = false

    var body: some View {
        Circle()
            .fill(.green)
            .frame(width: 12, height: 12)
            .scaleEffect(isPulsing ? 1.2 : 1.0)
            .opacity(isPulsing ? 0.6 : 1.0)
            .animation(
                .easeInOut(duration: 0.8).repeatForever(autoreverses: true),
                value: isPulsing
            )
            .onAppear {
                isPulsing = true
            }
    }
}

Shake Effect

struct ShakeEffect: GeometryEffect {
    var amount: CGFloat = 10
    var shakesPerUnit = 3
    var animatableData: CGFloat

    func effectValue(size: CGSize) -> ProjectionTransform {
        ProjectionTransform(CGAffineTransform(translationX:
            amount * sin(animatableData * .pi * CGFloat(shakesPerUnit)),
            y: 0))
    }
}

// Usage
TextField("Email", text: $email)
    .modifier(ShakeEffect(animatableData: shakeAmount))
    .onChange(of: hasError) {
        withAnimation(.default) {
            shakeAmount = hasError ? 1 : 0
        }
    }

Official Resources

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

David-Li0406/meta-skill-evloving

Browser automation with persistent page state. Use when users ask to navigate websites, fill forms, take screenshots, extract web data, test web apps, or automate browser workflows. Trigger phrases include "go to [url]", "click on", "fill out the form", "take a screenshot", "scrape", "automate", "test the website", "log into", or any browser interaction request.

David-Li0406/meta-skill-evloving

Create technical diagrams using Mermaid syntax for architecture, sequences, ERDs, flowcharts, and state machines. Use for visualizing system design, data flows, and processes. Triggers: diagram, mermaid, architecture diagram, sequence diagram, flowchart, ERD, entity relationship, state diagram, C4 model, component diagram, visualize, draw.

David-Li0406/meta-skill-evloving

Comprehensive document creation, editing, and analysis with support for tracked changes, comments, formatting preservation, and text extraction. When Claude needs to work with professional documents (.docx files) for: (1) Creating new documents, (2) Modifying or editing content, (3) Working with tracked changes, (4) Adding comments, or any other document tasks

David-Li0406/meta-skill-evloving

Complete color manipulation and green screen effects system. PROACTIVELY activate for: (1) Green screen/chromakey removal, (2) Color grading with LUTs, (3) Color correction (levels, curves, white balance), (4) Colorkey/color removal effects, (5) Hue/saturation manipulation, (6) Color space conversions (BT.709, BT.2020, HDR), (7) Color isolation effects, (8) Teal and orange look, (9) Vintage/film looks, (10) Color keying for transparency. Provides: chromakey and colorkey filters, LUT application (lut3d), curves and levels adjustment, color balance, selective color manipulation, color space handling, HDR tone mapping, professional color grading chains.

David-Li0406/meta-skill-evloving

Convert files and office documents to Markdown. Supports PDF, DOCX, PPTX, XLSX, images (with OCR), audio (with transcription), HTML, CSV, JSON, XML, ZIP, YouTube URLs, EPubs and more.

David-Li0406/meta-skill-evloving

Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).

David-Li0406/meta-skill-evloving

Build with OpenAI's stateless APIs - Chat Completions (GPT-5, GPT-4o), Embeddings, Images (DALL-E 3), Audio (Whisper + TTS), and Moderation. Includes Node.js SDK and fetch-based approaches for Cloudflare Workers. Use when: implementing chat completions with GPT-5/GPT-4o, streaming responses with SSE, using function calling/tools, creating structured outputs with JSON schemas, generating embeddings for RAG (text-embedding-3-small/large), generating images with DALL-E 3, editing images with GPT-Image-1, transcribing audio with Whisper, synthesizing speech with TTS (11 voices), moderating content (11 safety categories), or troubleshooting rate limits (429), invalid API keys (401), function calling failures, streaming parse errors, embeddings dimension mismatches, or token limit exceeded.

David-Li0406/meta-skill-evloving

Comprehensive PDF manipulation toolkit for extracting text and tables, creating new PDFs, merging/splitting documents, and handling forms. When Claude needs to fill in a PDF form or programmatically process, generate, or analyze PDF documents at scale.

David-Li0406/meta-skill-evloving

Presentation creation, editing, and analysis. When Claude needs to work with presentations (.pptx files) for: (1) Creating new presentations, (2) Modifying or editing content, (3) Working with layouts, (4) Adding comments or speaker notes, or any other presentation tasks

David-Li0406/meta-skill-evloving

Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas

David-Li0406/meta-skill-evloving

Download YouTube videos with customizable quality and format options. Use this skill when the user asks to download, save, or grab YouTube videos. Supports various quality settings (best, 1080p, 720p, 480p, 360p), multiple formats (mp4, webm, mkv), and audio-only downloads as MP3.

David-Li0406/meta-skill-evloving

Best practices for Remotion - Video creation in React

David-Li0406/meta-skill-evloving

Provides bidirectional translation (Chinese/English), grammar checking, and sentence polishing. Supports specific tense constraints, context-aware tone adjustment, and natural expression optimization. Outputs direct text without formatting.

David-Li0406/meta-skill-evloving

指导如何通过自定义注解(如@Unicom)标识Dubbo接口,利用BeanDefinitionRegistryPostProcessor在启动时扫描并动态注册ReferenceBean,实现RPC调用的封装。

David-Li0406/meta-skill-evloving

生成Python脚本,利用FFmpeg将图片序列按3x3布局合并,或将合并图拆分。要求使用subprocess模块执行命令,并支持用户交互式输入路径。

David-Li0406/meta-skill-evloving

SOP for extracting and analyzing promotional activity details, handling time formatting, location mapping, payment logic, and step-by-step process validation.

David-Li0406/meta-skill-evloving

Specializes in creating TikTok short video scripts using an anthropomorphic 'Chef' perspective to explain wildlife food chains and survival environments, utilizing real field footage.

David-Li0406/meta-skill-evloving

针对TikTok平台,以拟人化视角(如动物作为厨师)创作系列短视频脚本。重点介绍野生动物、食物链及生态环境,要求使用野外实拍素材,风格幽默且具有教育意义。

David-Li0406/meta-skill-evloving

Generates KWL (Know, Want to know, Learned) charts for educational videos, specifically BrainPOP, using simple 7th-grade language and short, concise bullet points.

David-Li0406/meta-skill-evloving

Tracks complex, multi-session work using the Beads issue tracker and dependency graphs, and provides persistent memory that survives conversation compaction. Use when work spans multiple sessions, has complex dependencies, or needs persistent context across compaction cycles. Trigger with phrases like "create task for", "what's ready to work on", "show task", "track this work", "what's blocking", or "update status".

Skills associés