🎉 chore(init): project initialization
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
motion,
|
||||
useAnimationFrame,
|
||||
useMotionValue,
|
||||
useScroll,
|
||||
useSpring,
|
||||
useTransform,
|
||||
useVelocity,
|
||||
} from 'framer-motion';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
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 function VelocityScroll({ text, default_velocity = 5, className }: VelocityScrollProps) {
|
||||
function ParallaxText({ children, baseVelocity = 100, className }: ParallaxProps) {
|
||||
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 = React.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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user