😍 4. Mở rộng thêm để hỗ trợ animate delay theo thứ tự (kiểu stagger effect) khi scroll đến không
Tuyệt! Hãy mở rộng code của bạn để có hiệu ứng staggered animation – tức là các phần tử xuất hiện dần dần theo thứ tự, giống như slide trong hero hoặc gallery đẹp mắt.
Ý tưởng:
Khi các .box trở nên visible, ta sẽ:
Thêm class show để kích hoạt animation.
Gán delay tăng dần bằng style.animationDelay = ... (hoặc style.transitionDelay nếu dùng transition).
✅ Cập nhật Tailwind config (nếu dùng animate-fade-in-up)
intersectionRatio là tỷ lệ phần tử đang hiển thị so với toàn bộ kích thước của nó (giá trị từ 0 đến 1).
✅ Hiệu ứng đạt được:
Khi cuộn đến .box, chúng lần lượt hiện lên với delay 0ms, 150ms, 300ms…
Khi cuộn ra ngoài, chúng fade-out đồng thời
Mượt, tự nhiên, dễ custom!
🔧 Mẹo mở rộng:
Thay 150ms bằng biến như baseDelay để dễ tùy chỉnh
Dùng data-index để kiểm soát thứ tự nếu cần
Dùng transition-delay nếu bạn muốn dùng @apply transition-* thay vì animation
Làm thêm phiên bản dùng GSAP dùng React để animation nâng cao hơn
Tuyệt vời! Dưới đây là phiên bản nâng cao sử dụng GSAP (GreenSock Animation Platform) trong React để tạo hiệu ứng scroll-triggered stagger animation – rất mượt, chuyên nghiệp hơn so với chỉ dùng CSS.
✅ 1. Cài đặt GSAP và ScrollTrigger
npm install gsap
Sau đó, trong code bạn register pluginScrollTrigger.
✅ 2. React Component với GSAP + Stagger Scroll Animation
/* You can also use global styles if needed */
.section {
will-change: transform, opacity;
}
✅ Kết quả:
4 Section xuất hiện khi cuộn tới.
Mỗi section fade-in + slide-up riêng biệt.
Reverse animation khi cuộn ngược lại (nếu cần).
Rất mượt và chuyên nghiệp với GSAP.
✅ Mở rộng gợi ý:
Ý tưởng
Cách làm
Parallax scroll effect
Dùng ScrollTrigger + scrub: true
Stagger từng phần tử trong section
Dùng gsap.timeline() + stagger
Fixed background scroll reveal
Dùng pin của ScrollTrigger
Animate ảnh, tiêu đề, text riêng
Tách selector & gán timeline riêng
— Nếu bạn muốn mình build một landing page kiểu "scroll storytelling" với GSAP (sticky + animation từng phần + số đếm + ảnh phóng to), mình có thể làm mẫu hoàn chỉnh.
Tuyệt! Hãy cùng xây dựng một landing page dạng scroll storytelling với các kỹ thuật nâng cao từ GSAP + React.
Dễ chịu với thao tác tay trên mobile, mạnh mẽ trên desktop
✅ Pro Tips:
Mục tiêu
Cách làm
✅ Thay đổi duration/ease theo size
ScrollTrigger.matchMedia({})
✅ Ẩn một số animation trên mobile
Đơn giản return null hoặc không tạo timeline
✅ Stagger khác nhau
Thêm stagger: 0.3 trong desktop, và stagger: 0.1 trong mobile
✅ Pin section chỉ ở desktop
Pin với start: 'top top', end: '+=1000', pin: true trong desktop only
✅ Muốn nâng cao hơn?
Kết hợp với CSS container queries (sắp phổ biến)
Thêm matchMedia.add() để destroy() khi thay kích thước màn hình
Kết hợp Framer Motion để có spring + tap/drag/hover interaction
Nếu bạn muốn mình:
👉 Kết hợp nhiều section vào 1 demo hoàn chỉnh (có responsive scroll story)
👉 Tạo video scroll sync, parallax, split text animation cho responsive
👉 Export thành một GitHub repo + Live preview
video scroll sync
Tuyệt! Giờ ta sẽ build một ví dụ 🎬 video scroll sync với GSAP + ScrollTrigger + React, nơi video được điều khiển bằng cuộn chuột (scrub) – hiệu ứng thường thấy trong các landing page cao cấp (Apple, Tesla...).
Tuyệt! Giờ ta sẽ build một ví dụ 🎬 video scroll sync với GSAP + ScrollTrigger + React, nơi video được điều khiển bằng cuộn chuột (scrub) – hiệu ứng thường thấy trong các landing page cao cấp (Apple, Tesla...).
🎯 Mục tiêu:
Video không tự play
Người dùng scroll để điều khiển thời gian (time) của video
Có sticky section giữ video khi cuộn
✅ 1. Cài đặt
npm install gsap
✅ 2. Component: VideoScrollSync.tsx
import { useEffect, useRef } from 'react';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
const VideoScrollSync = () => {
const containerRef = useRef(null);
const videoRef = useRef<HTMLVideoElement>(null);
useEffect(() => {
const video = videoRef.current;
if (!video) return;
// Đảm bảo video đã tải đủ dữ liệu để có duration
video.addEventListener('loadedmetadata', () => {
const duration = video.duration;
// ScrollTrigger scrub = video progress
gsap.to(video, {
currentTime: duration,
ease: 'none',
scrollTrigger: {
trigger: containerRef.current,
start: 'top top',
end: '+=3000', // chiều cao cuộn để điều khiển toàn bộ video
scrub: true,
pin: true,
anticipatePin: 1,
markers: false, // true để debug
},
});
});
}, []);
return (
<section
ref={containerRef}
className="relative h-screen bg-black flex items-center justify-center overflow-hidden"
>
<video
ref={videoRef}
src="/path-to-your-video.mp4"
className="w-full h-auto max-h-[90vh] rounded-xl shadow-2xl"
muted
preload="auto"
/>
</section>
);
};
export default VideoScrollSync;
✅ 3. Ghi chú quan trọng
Điều cần biết
Lý do
video.currentTime
GSAP có thể tween giá trị này để điều khiển playback thủ công
pin: true
Giữ section cố định khi scroll (sticky effect)
end: +=3000
Chiều cao scroll – càng lớn cuộn càng lâu mới hết video
scrub: true
Kéo mượt theo cuộn – không phải bật phát một lần
muted
Trình duyệt chặn autoplay audio nếu không có muted
✅ Đặt file video ở đâu?
Nếu bạn dùng Vite:
Đặt video vào thư mục public/
Gọi như src="/your-video.mp4"
Hoặc import video nếu bạn dùng Webpack / file bundler:
import myVideo from './assets/video.mp4';
<video src={myVideo} ... />