带动画3D英雄区域,支持滚动驱动视频
来自 Wikiprompt,自由的提示词百科全书
带动画3D英雄区域,支持滚动驱动视频 一个全面的提示,用于使用React、Tailwind和Framer Motion构建一个电影级、视频背景的落地页,包含滚动擦除、模糊效果和流畅动画。
提示词内容收藏
🌐
import { useEffect, useRef } from 'react';
import { motion, useScroll, useTransform } from 'motion/react';
import Lenis from 'lenis';
// LiquidVideoCanvas component
function LiquidVideoCanvas({ onReady }) {
const videoRef = useRef(null);
const containerRef = useRef(null);
const isSeeking = useRef(false);
const nextSeekTime = useRef(null);
const currentScroll = useRef(0);
const targetTime = useRef(0);
const isAnimatingIn = useRef(false);
// Entrance animation
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const handleReady = () => {
if (video.readyState >= 3 && !isAnimatingIn.current) {
isAnimatingIn.current = true;
video.style.transition = 'transform 1.4s cubic-bezier(0.16, 1, 0.3, 1), opacity 1.4s cubic-bezier(0.16, 1, 0.3, 1)';
video.style.transform = 'scale(1)';
video.style.opacity = '1';
onReady?.();
}
};
// Safety timeout
const timeout = setTimeout(handleReady, 3500);
video.addEventListener('loadeddata', handleReady);
video.addEventListener('canplaythrough', handleReady);
return () => {
clearTimeout(timeout);
video.removeEventListener('loadeddata', handleReady);
video.removeEventListener('canplaythrough', handleReady);
};
}, [onReady]);
// Scroll-driven seeking
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const handleScroll = () => {
const scrollY = window.scrollY;
const maxScroll = document.documentElement.scrollHeight - window.innerHeight;
const progress = maxScroll > 0 ? scrollY / maxScroll : 0;
targetTime.current = progress * video.duration;
};
const handleSeeked = () => {
isSeeking.current = false;
if (nextSeekTime.current !== null) {
const time = nextSeekTime.current;
nextSeekTime.current = null;
if (!video.seeking) {
isSeeking.current = true;
video.currentTime = time;
}
}
};
const handleSeeking = () => {
isSeeking.current = true;
};
const animate = () => {
if (!video.seeking && !isSeeking.current) {
const lerped = currentScroll.current + (targetTime.current - currentScroll.current) * 0.12;
currentScroll.current = lerped;
if (Math.abs(lerped - targetTime.current) > 0.01) {
isSeeking.current = true;
video.currentTime = lerped;
}
}
requestAnimationFrame(animate);
};
window.addEventListener('scroll', handleScroll, { passive: true });
video.addEventListener('seeked', handleSeeked);
video.addEventListener('seeking', handleSeeking);
const raf = requestAnimationFrame(animate);
return () => {
window.removeEventListener('scroll', handleScroll);
video.removeEventListener('seeked', handleSeeked);
video.removeEventListener('seeking', handleSeeking);
cancelAnimationFrame(raf);
};
}, []);
return (
<div ref={containerRef} className="fixed inset-0 z-[1] bg-black">
<video
ref={videoRef}
className="w-full h-full object-cover"
style={{ transform: 'scale(1.12)', opacity: 0 }}
loop
muted
playsInline
preload="auto"
src="https://t.co/cvkGVS5C0E"
/>
</div>
);
}
// Progressive blur component
function ProgressiveBlur() {
const blurRef = useRef(null);
const { scrollYProgress } = useScroll();
useEffect(() => {
const unsubscribe = scrollYProgress.on('change', (value) => {
if (!blurRef.current) return;
let blur = 0;
if (value < 0.5) {
blur = (value / 0.5) * 5;
} else {
blur = 5 + ((value - 0.5) / 0.5) * 50;
}
blurRef.current.style.backdropFilter = `blur(${blur}px)`;
blurRef.current.style.webkitBackdropFilter = `blur(${blur}px)`;
});
return () => unsubscribe();
}, [scrollYProgress]);
return <div ref={blurRef} className="fixed bottom-0 left-0 right-0 h-32 z-30 pointer-events-none" />;
}
// Text scramble effect component
function ScrambleText({ text, className }) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()';
const intervalRef = useRef(null);
useEffect(() => {
let iteration = 0;
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
const target = text;
const el = document.querySelector(`[data-scramble="${text}"]`);
if (!el) return;
el.innerText = target
.split('')
.map((char, index) => {
if (char === ' ') return ' ';
if (index < iteration) return target[index];
return chars[Math.floor(Math.random() * chars.length)];
})
.join('');
if (iteration >= target.length) {
clearInterval(intervalRef.current);
}
iteration += 1 / 3;
}, 30);
return () => clearInterval(intervalRef.current);
}, [text]);
return (
<span data-scramble={text} className={className}>
{text}
</span>
);
}
// Main App component
export default function App() {
const mainRef = useRef(null);
const { scrollYProgress } = useScroll();
const opacity = useTransform(scrollYProgress, [0, 0.1], [1, 0]);
const y = useTransform(scrollYProgress, [0, 0.1], [0, -50]);
// Lenis smooth scroll
useEffect(() => {
const lenis = new Lenis({
duration: 1.2,
easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
smoothWheel: true,
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
const rafId = requestAnimationFrame(raf);
return () => {
cancelAnimationFrame(rafId);
lenis.destroy();
};
}, []);
return (
<div className="relative overflow-x-hidden bg-black">
<LiquidVideoCanvas />
{/* Header */}
<header className="fixed top-0 left-0 right-0 z-50 flex items-center justify-between px-8 py-6">
<div className="text-xl font-bold tracking-widest">SYNAPSE<span className="text-blue-500">X</span></div>
<nav className="hidden md:flex items-center gap-8 text-sm">
<a href="#" className="hover:text-blue-400 transition-colors">Technology</a>
<a href="#" className="hover:text-blue-400 transition-colors">Solutions</a>
<a href="#" className="hover:text-blue-400 transition-colors">Research</a>
<a href="#" className="hover:text-blue-400 transition-colors">Contact</a>
</nav>
<button className="px-4 py-2 border border-white/20 rounded-full text-sm hover:bg-white/10 transition-colors">
Get Access
</button>
</header>
{/* Main content */}
<main ref={mainRef} className="relative z-10 min-h-screen">
<motion.section
style={{ opacity, y }}
className="flex flex-col items-center justify-center min-h-screen text-center px-4"
>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.5 }}
className="mb-8"
>
<i className="bi bi-apple text-4xl text-white/80" />
</motion.div>
<motion.h1
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.7 }}
className="text-5xl md:text-7xl font-bold mb-6 tracking-tight"
>
<ScrambleText text="SYNAPSE" />
<span className="text-blue-500">X</span>
</motion.h1>
<motion.p
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.9 }}
className="text-lg md:text-xl text-white/70 max-w-2xl mb-12"
>
<ScrambleText text="Next-generation neural interfaces" />
<br />
<ScrambleText text="for human-machine symbiosis" />
</motion.p>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 1.1 }}
className="flex flex-col md:flex-row gap-4"
>
<button className="px-8 py-4 bg-blue-600 hover:bg-blue-500 rounded-full font-bold transition-colors">
Explore Technology
</button>
<button className="px-8 py-4 border border-white/20 hover:bg-white/10 rounded-full font-bold transition-colors">
Watch Demo
</button>
</motion.div>
</motion.section>
{/* Scroll indicator */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1.5 }}
className="absolute bottom-8 left-1/2 transform -translate-x-1/2"
>
<div className="w-6 h-10 border-2 border-white/30 rounded-full flex justify-center pt-2">
<motion.div
animate={{ y: [0, 12, 0] }}
transition={{ repeat: Infinity, duration: 1.5 }}
className="w-1 h-2 bg-white/50 rounded-full"
/>
</div>
</motion.div>
</main>
<ProgressiveBlur />
{/* Additional content sections */}
<section className="relative z-10 min-h-screen flex items-center justify-center">
<div className="max-w-4xl mx-auto px-8 text-center">
<h2 className="text-4xl md:text-5xl font-bold mb-8">
<ScrambleText text="Beyond Human Limits" />
</h2>
<p className="text-white/70 text-lg leading-relaxed">
<ScrambleText text="Our neural interface technology enables unprecedented" />
<br />
<ScrambleText text="cognitive enhancement and seamless digital integration" />
</p>
</div>
</section>
<section className="relative z-10 min-h-screen flex items-center justify-center">
<div className="max-w-4xl mx-auto px-8 text-center">
<h2 className="text-4xl md:text-5xl font-bold mb-8">
<ScrambleText text="The Future is Here" />
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mt-12">
{['Neural Link', 'Quantum Core', 'Bio Sync'].map((item) => (
<div key={item} className="p-6 border border-white/10 rounded-2xl hover:border-blue-500/50 transition-colors">
<h3 className="text-xl font-bold mb-4">{item}</h3>
<p className="text-white/60 text-sm">
Advanced technology for the next generation of human-computer interaction
</p>
</div>
))}
</div>
</div>
</section>
</div>
);
}
```
```css
/* index.css */
@import url('https://fonts.googleapis.com/css2?family=Space+Mono:ital,wght@0,400;0,700;1,400&display=swap');
@import url('https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css');
@import "tailwindcss";
@theme {
--font-sans: "Space Mono", monospace;
--font-serif: "Space Mono", monospace;
--font-inter: "Space Mono", monospace;
--font-mono: "Space Mono", monospace;
}
html, body {
font-family: var(--font-sans);
background: black;
color: white;
margin: 0;
padding: 0;
overflow-x: hidden;
overflow-y: auto;
}
/* Lenis compatibility */
html.lenis, html.lenis body {
height: auto;
}
.lenis.lenis-smooth {
scroll-behavior: auto !important;
}
.lenis.lenis-smooth [data-lenis-prevent] {
overscroll-behavior: contain;
}
.lenis.lenis-stopped {
overflow: hidden;
}
.lenis.lenis-smooth iframe {
pointer-events: none;
}
用法
此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论