Scroll Velocity
Scroll velocity animations create a dynamic and engaging effect as elements move on the screen. This technique can be used to draw attention to specific content or enhance the overall user experience.
Installation
npm install framer-motion
pnpm install framer-motion
yarn add framer-motion
bun add framer-motion
Copy and paste the following code into your project.
components/ui/scrollbasedvelocity.tsx
"use client";
import React, { useEffect, useRef, useState } from "react";
import {
motion,
useAnimationFrame,
useMotionValue,
useScroll,
useSpring,
useTransform,
useVelocity,
} from "framer-motion";
import { cn } from "@/lib/utils";
interface VelocityScrollProps {
text: string;
default_velocity?: number;
className?: string;
}
interface ParallaxProps {
children: string;
baseVelocity: number;
className?: string;
}
export const wrap = (min: number, max: number, v: number) => {
const rangeSize = max - min;
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
};
export const VelocityScroll: React.FC<VelocityScrollProps> = ({
text,
default_velocity = 5,
className,
}) => {
const ParallaxText: React.FC<ParallaxProps> = ({
children,
baseVelocity = 100,
className,
}) => {
const baseX = useMotionValue(0);
const { scrollY } = useScroll();
const scrollVelocity = useVelocity(scrollY);
const smoothVelocity = useSpring(scrollVelocity, {
damping: 50,
stiffness: 400,
});
const velocityFactor = useTransform(smoothVelocity, [0, 1000], [0, 5], {
clamp: false,
});
const [repetitions, setRepetitions] = useState(1);
const containerRef = useRef<HTMLDivElement>(null);
const textRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
const calculateRepetitions = () => {
if (containerRef.current && textRef.current) {
const containerWidth = containerRef.current.offsetWidth;
const textWidth = textRef.current.offsetWidth;
const newRepetitions = Math.ceil(containerWidth / textWidth) + 2;
setRepetitions(newRepetitions);
}
};
calculateRepetitions();
window.addEventListener("resize", calculateRepetitions);
return () => window.removeEventListener("resize", calculateRepetitions);
}, [children]);
const x = useTransform(baseX, (v) => `${wrap(-100 / repetitions, 0, v)}%`);
const directionFactor = useRef<number>(1);
useAnimationFrame((t, delta) => {
let moveBy = directionFactor.current * baseVelocity * (delta / 1000);
if (velocityFactor.get() < 0) {
directionFactor.current = -1;
} else if (velocityFactor.get() > 0) {
directionFactor.current = 1;
}
moveBy += directionFactor.current * moveBy * velocityFactor.get();
baseX.set(baseX.get() + moveBy);
});
return (
<div
className="w-full overflow-hidden whitespace-nowrap"
ref={containerRef}
>
<motion.div className={cn("inline-block", className)} style={{ x }}>
{Array.from({ length: repetitions }).map((_, i) => (
<span key={i} ref={i === 0 ? textRef : null}>
{children}{" "}
</span>
))}
</motion.div>
</div>
);
};
return (
<section className="relative w-full">
<ParallaxText baseVelocity={default_velocity} className={className}>
{text}
</ParallaxText>
<ParallaxText baseVelocity={-default_velocity} className={className}>
{text}
</ParallaxText>
</section>
);
};
Props
Prop | Type | Default |
---|---|---|
text? | string | Hello World! |
default_velocity? | number | 5 |
className? | string | - |
Random Unveil
Random unveil animations create a dynamic and engaging effect as elements appear on the screen. This technique can be used to draw attention to specific content or enhance the overall user experience.
Text RevealNew
Text reveal animations create a dynamic and engaging effect as text appears on the screen. This technique can be used to draw attention to specific content or enhance the overall user experience.