Skip to main content
Animation constructors create AnimationComponent values that define the timing and feel of state transitions. Pass them to withAnimation or the .animation() modifier.

Spring

A spring animation with response and damping parameters. This is the most common animation type.
Spring(options?: {
    response?: number;
    dampingFraction?: number;
    blendDuration?: number;
}): AnimationComponent;
response
number
default:"0.55"
Duration of the spring’s settle time in seconds. Lower values produce faster animations.
dampingFraction
number
default:"0.825"
Damping ratio. 0 = no damping (infinite oscillation), 1 = critical damping (no bounce).
blendDuration
number
Duration in seconds for blending between animations.
const body = () => {
    const [scale, setScale] = useState(1.0)

    return VStack([
        Circle()
            .fill(Color("blue"))
            .frame({ width: 100, height: 100 })
            .scaleEffect(scale),
        Button("Bounce", () => {
            withAnimation(Spring({ dampingFraction: 0.5 }), () => {
                setScale(scale === 1.0 ? 1.5 : 1.0)
            })
        })
    ])
}

InterpolatingSpring

A spring animation defined by physical stiffness, damping, and mass for precise control.
InterpolatingSpring(options: {
    stiffness: number;
    damping: number;
    mass: number;
}): AnimationComponent;
stiffness
number
required
Spring stiffness coefficient. Higher values produce faster, stiffer springs.
damping
number
required
Damping coefficient. Higher values produce less bounce.
mass
number
required
Mass of the spring. Higher values produce slower animations with more momentum.
const body = () => {
    const [offset, setOffset] = useState(0)

    return VStack([
        Circle()
            .fill(Color("red"))
            .frame({ width: 50, height: 50 })
            .offset({ x: offset, y: 0 }),
        Button("Animate", () => {
            withAnimation(InterpolatingSpring({
                stiffness: 50,
                damping: 10,
                mass: 2
            }), () => {
                setOffset(offset === 0 ? 200 : 0)
            })
        })
    ])
}

EaseIn

An ease-in timing curve that starts slow and accelerates toward the end.
EaseIn(options?: { duration?: number }): AnimationComponent;
duration
number
Animation duration in seconds.
const body = () => {
    const [show, setShow] = useState(false)

    return VStack([
        Text("Hello")
            .opacity(show ? 1 : 0),
        Button("Fade In", () => {
            withAnimation(EaseIn({ duration: 1.0 }), () => {
                setShow(true)
            })
        })
    ])
}

EaseInOut

An ease-in-out timing curve that starts and ends slow with acceleration in the middle.
EaseInOut(options?: { duration?: number }): AnimationComponent;
duration
number
Animation duration in seconds.
const body = () => {
    const [rotation, setRotation] = useState(0)

    return VStack([
        Rectangle()
            .fill(Color("purple"))
            .frame({ width: 100, height: 100 })
            .rotationEffect(rotation),
        Button("Rotate", () => {
            withAnimation(EaseInOut({ duration: 0.8 }), () => {
                setRotation(rotation + 180)
            })
        })
    ])
}

EaseOut

An ease-out timing curve that starts fast and decelerates toward the end.
EaseOut(options?: { duration?: number }): AnimationComponent;
duration
number
Animation duration in seconds.
const body = () => {
    const [offset, setOffset] = useState(0)

    return VStack([
        Rectangle()
            .fill(Color("green"))
            .frame({ width: 100, height: 100 })
            .offset({ x: offset, y: 0 }),
        Button("Slide Out", () => {
            withAnimation(EaseOut({ duration: 0.5 }), () => {
                setOffset(300)
            })
        })
    ])
}

Linear

A linear timing curve with constant speed throughout.
Linear(options?: { duration?: number }): AnimationComponent;
duration
number
Animation duration in seconds.
const body = () => {
    const [position, setPosition] = useState(0)

    return VStack([
        Circle()
            .fill(Color("orange"))
            .frame({ width: 40, height: 40 })
            .offset({ x: position, y: 0 }),
        Button("Move", () => {
            withAnimation(Linear({ duration: 2.0 }), () => {
                setPosition(position === 0 ? 300 : 0)
            })
        })
    ])
}

Bouncy

A spring animation with extra bounce for playful, attention-grabbing effects.
Bouncy(options?: {
    duration?: number;
    extraBounce?: number;
}): AnimationComponent;
duration
number
Approximate animation duration in seconds.
extraBounce
number
Extra bounce amount. 0 = no extra bounce, 1 = very bouncy.
const body = () => {
    const [scale, setScale] = useState(1.0)

    return VStack([
        Text("Tap me!").font("title").scaleEffect(scale),
        Button("Bounce", () => {
            withAnimation(Bouncy({ extraBounce: 0.4 }), () => {
                setScale(scale === 1.0 ? 1.3 : 1.0)
            })
        })
    ])
}

Snappy

A spring animation with higher damping for a quick, responsive feel.
Snappy(options?: {
    response?: number;
    dampingFraction?: number;
    blendDuration?: number;
}): AnimationComponent;
response
number
Duration of the spring’s settle time in seconds.
dampingFraction
number
Damping ratio. 0 = no damping, 1 = critical damping.
blendDuration
number
Duration in seconds for blending between animations.
const body = () => {
    const [isExpanded, setIsExpanded] = useState(false)

    return VStack([
        Rectangle()
            .fill(Color("cyan"))
            .frame({ width: 200, height: isExpanded ? 200 : 100 }),
        Button(isExpanded ? "Collapse" : "Expand", () => {
            withAnimation(Snappy(), () => {
                setIsExpanded(!isExpanded)
            })
        })
    ])
}

Chainable Modifiers

All animation constructors return an AnimationComponent that supports these chainable methods:

delay

Delays the start of the animation.
.delay(seconds: number): AnimationComponent
const animation = Spring().delay(0.5)

speed

Multiplies the animation speed.
.speed(multiplier: number): AnimationComponent
const slow = EaseInOut({ duration: 1.0 }).speed(0.5)
const fast = Linear({ duration: 2.0 }).speed(2.0)

repeatCount

Repeats the animation a fixed number of times.
.repeatCount(count: number): AnimationComponent
const bouncing = Bouncy().repeatCount(3)

repeatForever

Repeats the animation indefinitely.
.repeatForever(autoreverses: boolean | { autoreverses: boolean }): AnimationComponent
autoreverses
boolean | { autoreverses: boolean }
If true, the animation reverses direction each cycle. If false, it restarts from the beginning.
// Continuous rotation
const spinning = Linear({ duration: 2.0 }).repeatForever(false)

// Pulsing effect (forward and back)
const pulsing = EaseInOut({ duration: 1.0 }).repeatForever(true)

Combining modifiers

const body = () => {
    const [offset, setOffset] = useState(0)

    return VStack([
        Circle()
            .fill(Color("pink"))
            .frame({ width: 60, height: 60 })
            .offset({ x: offset, y: 0 }),
        Button("Complex Animation", () => {
            const animation = Bouncy({ extraBounce: 0.3 })
                .delay(0.3)
                .speed(0.5)
                .repeatCount(3)

            withAnimation(animation, () => {
                setOffset(100)
            })
        })
    ])
}

Notes

  • Default parameter values are platform-specific and optimized for each environment.
  • Spring animations (Spring, InterpolatingSpring, Bouncy, Snappy) may overshoot their target based on damping settings.
  • Timing curve animations (EaseIn, EaseInOut, EaseOut, Linear) run for a fixed duration and do not overshoot.
  • Use repeatForever sparingly to avoid performance issues.

See Also