Back
This is a fun and easy animation to add some interactivity to your site. We'll start by animating a paragraph as a whole to finish with a word-by-word animation - which in my opinion is nicer and doesn't overcomplicate things.
Both these methods are going to be relying on the useScroll hook from Framer Motion to animate the opacity on scroll.
import { motion, useScroll } from 'framer-motion'import React from 'react'type AnimatedParagraphProps = {value: string}export function AnimatedParagraph({ value }: AnimatedParagraphProps) {const ref = React.useRef<HTMLParagraphElement>(null)const { scrollYProgress } = useScroll({target: ref,offset: ['start 0.85', 'start 0.3'],})return (<motion.p ref={ref} style={{ opacity: scrollYProgress }} className={'flex w-fit flex-wrap'}>{value}</motion.p>)}
This is quite similar to the previous method except we are splitting the string into words and then animating each of them.
import React from 'react'import { MotionValue, useScroll, useTransform, motion } from 'framer-motion'type AnimatedWordProps = {value: string}export function AnimatedWord({ value }: AnimatedWordProps) {const ref = React.useRef<HTMLParagraphElement>(null)const { scrollYProgress } = useScroll({target: ref,offset: ['start 0.85', 'start 0.3'],})const words = value.toString().split(' ')return (<p ref={ref} className={'mx-auto flex w-fit max-w-5xl flex-wrap text-7xl font-semibold text-white'}>{words.map((word, i) => {const start = i / words.lengthconst end = start + 1 / words.lengthreturn (<Word key={i} range={[start, end]} scrollYProgress={scrollYProgress}>{word}</Word>)})}</p>)}type WordProps = {children: stringrange: [number, number]scrollYProgress: MotionValue<number>}function Word({ children, range, scrollYProgress }: WordProps) {const opacity = useTransform(scrollYProgress, range, [0, 1])return (<span className={'relative mr-2 text-pretty leading-none'}><span className={'absolute opacity-20'}>{children}</span><motion.span style={{ opacity }}>{children}</motion.span></span>)}