Apple Vision Pro Scroll Animation

Apple Vision Pro Scroll Animation

Nandi Muzsik

How can I improve Framer Uni?

Let me know if there’s a missing feature or something that could be improved.

Share feedback

Nandi Muzsik

How can I improve Framer Uni?

Let me know if there’s a missing feature or something that could be improved.

Share feedback

Animation

Apple Vision Pro Scroll Animation

This is the scroll animation from the Apple Vision Pro site, recreated in Framer. Feel free to remix the project and check out how it's built.

image of Nandi Muzsik

Created by

Apple Vision Pro Scroll Animation
Apple Vision Pro Scroll Animation
Apple Vision Pro Scroll Animation
Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

Related Lesson

Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

Related Lesson

Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

Related Lesson

Framer Tutorial: Creating the Apple Vision Pro Scroll Animation

About the resource

The scroll animation effect leverages a mix of native Framer effects and some code overrides.

Elements of Vision Pro are moved using scroll transforms, triggered by scroll sections.

The video plays on scroll due to a scroll scrub code override applied to it.

I also employed sticky positioning, ensuring that the Vision Pro headset stays at the top of the viewport during the animation. An additional code override on the sticky container assures that the top value remains at 50vh, meaning the section halts in the center of the viewport.

You can copy both overrides from below.

Code override for the scroll scrub video effect

Go ahead and copy the code, then create a new override in your project to start from scratch.

You can edit the "startY" and "distance" variables to alter when the video starts playing and the duration it takes to play the entire video.

About the resource

The scroll animation effect leverages a mix of native Framer effects and some code overrides.

Elements of Vision Pro are moved using scroll transforms, triggered by scroll sections.

The video plays on scroll due to a scroll scrub code override applied to it.

I also employed sticky positioning, ensuring that the Vision Pro headset stays at the top of the viewport during the animation. An additional code override on the sticky container assures that the top value remains at 50vh, meaning the section halts in the center of the viewport.

You can copy both overrides from below.

Code override for the scroll scrub video effect

Go ahead and copy the code, then create a new override in your project to start from scratch.

You can edit the "startY" and "distance" variables to alter when the video starts playing and the duration it takes to play the entire video.

About the resource

The scroll animation effect leverages a mix of native Framer effects and some code overrides.

Elements of Vision Pro are moved using scroll transforms, triggered by scroll sections.

The video plays on scroll due to a scroll scrub code override applied to it.

I also employed sticky positioning, ensuring that the Vision Pro headset stays at the top of the viewport during the animation. An additional code override on the sticky container assures that the top value remains at 50vh, meaning the section halts in the center of the viewport.

You can copy both overrides from below.

Code override for the scroll scrub video effect

Go ahead and copy the code, then create a new override in your project to start from scratch.

You can edit the "startY" and "distance" variables to alter when the video starts playing and the duration it takes to play the entire video.

import type { ComponentType } from "react"
import { useState, useEffect } from "react"
import type { MotionValue, Transition } from "framer-motion"
import { useViewportScroll, useTransform } from "framer-motion"
import { gsap } from "gsap"

export function withScrolledProgress(Component): ComponentType {
    const startY = 1000
    const distance = 1500
    const endY = startY + distance
    return (props) => {
        const { scrollY } = useViewportScroll()
        const progress = useTransform(scrollY, [startY, endY], [0, 1])
        useEffect(() => {
            const video = document.getElementById("video") as HTMLVideoElement
            gsap.to(video, {
                scrollTrigger: {
                    trigger: ".scroll-container",
                    start: "top top",
                    end: "bottom bottom",
                    scrub: 1,
                    markers: true,
                },
                keyframes: [
                    { progress: 0 },
                    { progress: 0.1 },
                    { progress: 0.2 },
                    { progress: 0.3 },
                    { progress: 0.4 },
                    { progress: 0.5 },
                    { progress: 0.6 },
                    { progress: 0.7 },
                    { progress: 0.8 },
                    { progress: 0.9 },
                    { progress: 1 },
                ],
                ease: "linear",
                duration: 10,
            })

            return () => {
                gsap.killTweensOf(video)
            }
        }, [scrollY, distance])

        return <Component {...props} progress={progress} />
    }
}
import type { ComponentType } from "react"
import { useState, useEffect } from "react"
import type { MotionValue, Transition } from "framer-motion"
import { useViewportScroll, useTransform } from "framer-motion"
import { gsap } from "gsap"

export function withScrolledProgress(Component): ComponentType {
    const startY = 1000
    const distance = 1500
    const endY = startY + distance
    return (props) => {
        const { scrollY } = useViewportScroll()
        const progress = useTransform(scrollY, [startY, endY], [0, 1])
        useEffect(() => {
            const video = document.getElementById("video") as HTMLVideoElement
            gsap.to(video, {
                scrollTrigger: {
                    trigger: ".scroll-container",
                    start: "top top",
                    end: "bottom bottom",
                    scrub: 1,
                    markers: true,
                },
                keyframes: [
                    { progress: 0 },
                    { progress: 0.1 },
                    { progress: 0.2 },
                    { progress: 0.3 },
                    { progress: 0.4 },
                    { progress: 0.5 },
                    { progress: 0.6 },
                    { progress: 0.7 },
                    { progress: 0.8 },
                    { progress: 0.9 },
                    { progress: 1 },
                ],
                ease: "linear",
                duration: 10,
            })

            return () => {
                gsap.killTweensOf(video)
            }
        }, [scrollY, distance])

        return <Component {...props} progress={progress} />
    }
}
import type { ComponentType } from "react"
import { useState, useEffect } from "react"
import type { MotionValue, Transition } from "framer-motion"
import { useViewportScroll, useTransform } from "framer-motion"
import { gsap } from "gsap"

export function withScrolledProgress(Component): ComponentType {
    const startY = 1000
    const distance = 1500
    const endY = startY + distance
    return (props) => {
        const { scrollY } = useViewportScroll()
        const progress = useTransform(scrollY, [startY, endY], [0, 1])
        useEffect(() => {
            const video = document.getElementById("video") as HTMLVideoElement
            gsap.to(video, {
                scrollTrigger: {
                    trigger: ".scroll-container",
                    start: "top top",
                    end: "bottom bottom",
                    scrub: 1,
                    markers: true,
                },
                keyframes: [
                    { progress: 0 },
                    { progress: 0.1 },
                    { progress: 0.2 },
                    { progress: 0.3 },
                    { progress: 0.4 },
                    { progress: 0.5 },
                    { progress: 0.6 },
                    { progress: 0.7 },
                    { progress: 0.8 },
                    { progress: 0.9 },
                    { progress: 1 },
                ],
                ease: "linear",
                duration: 10,
            })

            return () => {
                gsap.killTweensOf(video)
            }
        }, [scrollY, distance])

        return <Component {...props} progress={progress} />
    }
}

Code override for sticky container

Go ahead and copy the code, then create a new override in your project to start from scratch.

This code basically makes the frame stop with a 50vh (50% of the given viewport) distance from the top of the viewport.

Code override for sticky container

Go ahead and copy the code, then create a new override in your project to start from scratch.

This code basically makes the frame stop with a 50vh (50% of the given viewport) distance from the top of the viewport.

Code override for sticky container

Go ahead and copy the code, then create a new override in your project to start from scratch.

This code basically makes the frame stop with a 50vh (50% of the given viewport) distance from the top of the viewport.

import type { ComponentType } from "react"

export function withStickyTop(Component): ComponentType {
    return (props) => {
        return (
            <Component {...props} style={{ position: "sticky", top: "50vh" }} />
        )
    }
}
import type { ComponentType } from "react"

export function withStickyTop(Component): ComponentType {
    return (props) => {
        return (
            <Component {...props} style={{ position: "sticky", top: "50vh" }} />
        )
    }
}
import type { ComponentType } from "react"

export function withStickyTop(Component): ComponentType {
    return (props) => {
        return (
            <Component {...props} style={{ position: "sticky", top: "50vh" }} />
        )
    }
}

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

Framer Navigator

Learn the fundamentals of Framer for free.

Build your ideas with ease by learning the basics of website building with Framer.

Nandi portrait's background
Nandi's portrait

More resources

More resources

  • Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

    Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

    Curved grid layout showcasing a variety of product images

    3D Curved Grid Scroll Animation in Framer

    Animation

  • Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation

    Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation

    Framer website homepage with navigation menu and pixelated project previews showcasing web design tool capabilities

    Animated Navigation Bar in Framer

    Animation