June 17, 2024
We see a lot of modern websites e.g. Apple, Frame.io where the video playback is synced with the scroll position. This creates a illusion of complex animations and effects. Let's see how we can achieve this effect using ffmpeg, framer-motion & nextjs.
Before we start, let's see a demo of the effect we are going to achieve. You may need to refresh the page (this is a foreshadowing of an optimization we will discuss later in this article)
There are only 3 steps to achieve this effect:
We need to encode the video in such format that each frame can be seeked given a timestamp. We can use ffmpeg to encode the video. Here is the command to encode the video:
ffmpeg -i input_video.mp4 -movflags faststart -vcodec libx264 -crf 23 -g 1 -pix_fmt yuv420p output_video.mp4
We can use any video player to render the video. For this example, we will use the video
tag in html.
<video loop muted playsInline ref={videoRef}>
<source
src="video.mp4"
type="video/mp4"
/>
</video>
We can use framer-motion to sync the time of video with the scroll position. Here is the code to achieve this effect:
useMotionValueEvent(scrollY, "change", (latest) => {
const video = videoRef.current;
const timestamp = ...; // calculate the time based on the scroll position
video.currentTime = timestamp;
});
I've skipped the implementation of useMotionValueEvent
and calculation of timestamp
for brevity. There are many ways to achieve this effect. Even you can achieve this effect without using any library. The key is to calculate the time based on the scroll position and set the currentTime
of video accordingly.
You can find the complete code in the github repo.
Here's a few tips to optimize the performance & user experience: