chore: 更新构建ID和相关文件引用
This commit is contained in:
@@ -0,0 +1,438 @@
|
||||
'use client';
|
||||
|
||||
import { motion, useScroll, useTransform, useSpring, type MotionValue, type Variants, type HTMLMotionProps } from 'framer-motion';
|
||||
import { useRef, type ReactNode } from 'react';
|
||||
|
||||
export const scrollRevealVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 50,
|
||||
scale: 0.95,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.8,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const inkRevealVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
scale: 0.8,
|
||||
filter: 'blur(10px)',
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
filter: 'blur(0px)',
|
||||
transition: {
|
||||
duration: 1,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const slideInLeftVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
x: -50,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const slideInRightVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
x: 50,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
x: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
interface ScrollRevealProps extends HTMLMotionProps<'div'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
delay?: number;
|
||||
variants?: Variants;
|
||||
once?: boolean;
|
||||
threshold?: number;
|
||||
}
|
||||
|
||||
export function ScrollReveal({
|
||||
children,
|
||||
className = '',
|
||||
delay = 0,
|
||||
variants = scrollRevealVariants,
|
||||
once = true,
|
||||
threshold = 0.1,
|
||||
...props
|
||||
}: ScrollRevealProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'start center'],
|
||||
});
|
||||
|
||||
const opacity = useTransform(scrollYProgress, [0, threshold], [0, 1]);
|
||||
const y = useTransform(scrollYProgress, [0, threshold], [50, 0]);
|
||||
const scale = useTransform(scrollYProgress, [0, threshold], [0.95, 1]);
|
||||
|
||||
const smoothOpacity = useSpring(opacity, { stiffness: 100, damping: 30 });
|
||||
const smoothY = useSpring(y, { stiffness: 100, damping: 30 });
|
||||
const smoothScale = useSpring(scale, { stiffness: 100, damping: 30 });
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
ref={ref}
|
||||
style={{
|
||||
opacity: smoothOpacity,
|
||||
y: smoothY,
|
||||
scale: smoothScale,
|
||||
}}
|
||||
initial={{ opacity: 0, y: 50, scale: 0.95 }}
|
||||
whileInView={{ opacity: 1, y: 0, scale: 1 }}
|
||||
viewport={{ once, amount: threshold }}
|
||||
transition={{
|
||||
duration: 0.8,
|
||||
delay,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
}}
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ParallaxSectionProps extends HTMLMotionProps<'section'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
export function ParallaxSection({
|
||||
children,
|
||||
className = '',
|
||||
speed = 0.5,
|
||||
...props
|
||||
}: ParallaxSectionProps) {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'end start'],
|
||||
});
|
||||
|
||||
const y = useTransform(scrollYProgress, [0, 1], ['0%', `${speed * 100}%`]);
|
||||
const smoothY = useSpring(y, { stiffness: 100, damping: 30 });
|
||||
|
||||
return (
|
||||
<motion.section ref={ref} style={{ y: smoothY }} className={className} {...props}>
|
||||
{children}
|
||||
</motion.section>
|
||||
);
|
||||
}
|
||||
|
||||
interface ParallaxImageProps {
|
||||
src: string;
|
||||
alt: string;
|
||||
className?: string;
|
||||
speed?: number;
|
||||
}
|
||||
|
||||
export function ParallaxImage({ src, alt, className = '', speed = 0.3 }: ParallaxImageProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'end start'],
|
||||
});
|
||||
|
||||
const y = useTransform(scrollYProgress, [0, 1], [`${-speed * 50}%`, `${speed * 50}%`]);
|
||||
const scale = useTransform(scrollYProgress, [0, 0.5, 1], [1.1, 1, 1.1]);
|
||||
|
||||
return (
|
||||
<div ref={ref} className={`overflow-hidden ${className}`}>
|
||||
<motion.img
|
||||
src={src}
|
||||
alt={alt}
|
||||
style={{ y, scale }}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ScaleOnScrollProps extends HTMLMotionProps<'div'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
minScale?: number;
|
||||
maxScale?: number;
|
||||
}
|
||||
|
||||
export function ScaleOnScroll({
|
||||
children,
|
||||
className = '',
|
||||
minScale = 0.8,
|
||||
maxScale = 1,
|
||||
...props
|
||||
}: ScaleOnScrollProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'center center'],
|
||||
});
|
||||
|
||||
const scale = useTransform(scrollYProgress, [0, 1], [minScale, maxScale]);
|
||||
const smoothScale = useSpring(scale, { stiffness: 100, damping: 30 });
|
||||
|
||||
return (
|
||||
<motion.div ref={ref} style={{ scale: smoothScale }} className={className} {...props}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface FadeOnScrollProps extends HTMLMotionProps<'div'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
direction?: 'up' | 'down' | 'left' | 'right';
|
||||
distance?: number;
|
||||
}
|
||||
|
||||
export function FadeOnScroll({
|
||||
children,
|
||||
className = '',
|
||||
direction = 'up',
|
||||
distance = 50,
|
||||
...props
|
||||
}: FadeOnScrollProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'center center'],
|
||||
});
|
||||
|
||||
const getTransform = () => {
|
||||
switch (direction) {
|
||||
case 'up':
|
||||
return useTransform(scrollYProgress, [0, 1], [distance, 0]);
|
||||
case 'down':
|
||||
return useTransform(scrollYProgress, [0, 1], [-distance, 0]);
|
||||
case 'left':
|
||||
return useTransform(scrollYProgress, [0, 1], [distance, 0]);
|
||||
case 'right':
|
||||
return useTransform(scrollYProgress, [0, 1], [-distance, 0]);
|
||||
default:
|
||||
return useTransform(scrollYProgress, [0, 1], [distance, 0]);
|
||||
}
|
||||
};
|
||||
|
||||
const transform = getTransform();
|
||||
const opacity = useTransform(scrollYProgress, [0, 0.5], [0, 1]);
|
||||
|
||||
const smoothTransform = useSpring(transform, { stiffness: 100, damping: 30 });
|
||||
const smoothOpacity = useSpring(opacity, { stiffness: 100, damping: 30 });
|
||||
|
||||
const style =
|
||||
direction === 'left' || direction === 'right' ? { x: smoothTransform, opacity: smoothOpacity } : { y: smoothTransform, opacity: smoothOpacity };
|
||||
|
||||
return (
|
||||
<motion.div ref={ref} style={style} className={className} {...props}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface StaggerRevealProps extends HTMLMotionProps<'div'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
staggerDelay?: number;
|
||||
containerDelay?: number;
|
||||
}
|
||||
|
||||
export function StaggerReveal({
|
||||
children,
|
||||
className = '',
|
||||
staggerDelay = 0.1,
|
||||
containerDelay = 0,
|
||||
...props
|
||||
}: StaggerRevealProps) {
|
||||
const containerVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: staggerDelay,
|
||||
delayChildren: containerDelay,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
variants={containerVariants}
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface StaggerItemProps extends HTMLMotionProps<'div'> {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function StaggerItem({ children, className = '', ...props }: StaggerItemProps) {
|
||||
const itemVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 30,
|
||||
scale: 0.95,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
scale: 1,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div variants={itemVariants} className={className} {...props}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface TextRevealProps extends HTMLMotionProps<'div'> {
|
||||
text: string;
|
||||
className?: string;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export function TextReveal({ text, className = '', delay = 0, ...props }: TextRevealProps) {
|
||||
const words = text.split(' ');
|
||||
|
||||
const containerVariants: Variants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.05,
|
||||
delayChildren: delay,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wordVariants: Variants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 20,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.4,
|
||||
ease: [0.16, 1, 0.3, 1],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={containerVariants}
|
||||
className={className}
|
||||
{...props}
|
||||
>
|
||||
{words.map((word, index) => (
|
||||
<motion.span
|
||||
key={index}
|
||||
variants={wordVariants}
|
||||
className="inline-block mr-2"
|
||||
>
|
||||
{word}
|
||||
</motion.span>
|
||||
))}
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ProgressIndicatorProps {
|
||||
className?: string;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
export function ProgressIndicator({ className = '', color = '#C41E3A' }: ProgressIndicatorProps) {
|
||||
const { scrollYProgress } = useScroll();
|
||||
const scaleX = useSpring(scrollYProgress, { stiffness: 100, damping: 30 });
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
className={`fixed top-0 left-0 right-0 h-1 origin-left z-50 ${className}`}
|
||||
style={{ scaleX, backgroundColor: color }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
interface ScrollTriggeredCounterProps {
|
||||
end: number;
|
||||
duration?: number;
|
||||
prefix?: string;
|
||||
suffix?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function ScrollTriggeredCounter({
|
||||
end,
|
||||
duration = 2,
|
||||
prefix = '',
|
||||
suffix = '',
|
||||
className = '',
|
||||
}: ScrollTriggeredCounterProps) {
|
||||
const ref = useRef<HTMLSpanElement>(null);
|
||||
const { scrollYProgress } = useScroll({
|
||||
target: ref,
|
||||
offset: ['start end', 'center center'],
|
||||
});
|
||||
|
||||
const displayNumber = useTransform(scrollYProgress, [0, 1], [0, end]);
|
||||
const rounded = useTransform(displayNumber, (latest) => Math.round(latest));
|
||||
|
||||
return (
|
||||
<motion.span ref={ref} className={className}>
|
||||
{prefix}
|
||||
<motion.span>{rounded}</motion.span>
|
||||
{suffix}
|
||||
</motion.span>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user