Sử dụng ScrollSmoother thay thế Locomotive Scroll hay Lenis. (ok)
Dưới đây là example đầy đủ sử dụng:
✅ GSAP ScrollTrigger
✅ GSAP ScrollSmoother
(smooth scrolling)
✅ gsap.utils.toArray
loop nhiều counter
✅ Hàm CounterUpAnimation
của bạn
⚠️ 1. Lưu ý
ScrollSmoother chỉ có trong GSAP Club Membership. CDN demo sẽ hoạt động nhưng production cần license.
Đảm bảo load ScrollSmoother script đúng version.
✅ 2. Example HTML + JS
htmlCopyEdit<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Counter with ScrollSmoother</title>
<style>
body {
background: #111;
color: white;
font-size: 40px;
font-family: sans-serif;
margin: 0;
padding: 0;
}
.smooth-wrapper {
overflow: hidden;
}
.smooth-content {
will-change: transform;
}
.section {
padding: 150px 0;
text-align: center;
}
.counter {
font-size: 60px;
}
</style>
</head>
<body>
<div class="smooth-wrapper">
<div class="smooth-content">
<div class="section">
<div class="counter" data-target="100">0+</div>
</div>
<div class="section">
<div class="counter" data-target="200">0+</div>
</div>
<div class="section">
<div class="counter" data-target="300">0+</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollSmoother.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
// Khởi tạo ScrollSmoother
ScrollSmoother.create({
wrapper: ".smooth-wrapper",
content: ".smooth-content",
smooth: 1.2, // độ mượt
effects: true
});
const CounterUpAnimation = (number, start, end, duration) => {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) {
startTimestamp = timestamp;
}
let progress = Math.min((timestamp - startTimestamp) / duration, 1);
number.innerHTML = Math.floor(progress * (end - start) + start) + '+';
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
};
// Loop qua tất cả counter
gsap.utils.toArray(".counter").forEach(counter => {
let target = parseInt(counter.getAttribute("data-target"));
ScrollTrigger.create({
trigger: counter,
start: "top 80%",
onEnter: () => {
CounterUpAnimation(counter, 0, target, 2000);
},
once: true,
markers: true // remove nếu không cần debug
});
});
</script>
</body>
</html>
🔧 3. Giải thích
✅ ScrollSmoother.create
khởi tạo smooth scroll.
✅ .smooth-wrapper
bọc ngoài .smooth-content
đúng chuẩn ScrollSmoother.
✅ ScrollTrigger.create
vẫn hoạt động bình thường với ScrollSmoother vì plugin đã tích hợp.
✅ once: true
để counter chỉ chạy 1 lần khi scroll tới.
🚀 4. Mở rộng
Nếu muốn:
✅ Dùng GSAP tween thay vì requestAnimationFrame
cho counter
✅ Animate opacity + scale counter cùng lúc
✅ Kết hợp SplitText + ScrollSmoother + Counter animation hero
Hãy cho tôi biết, tôi sẽ viết version advanced cho landing page animation của bạn trong hôm nay.
Lỗi ScrollSmoother is not defined
nghĩa là bạn chưa có file ScrollSmoother hoặc link CDN bạn đang dùng không có plugin này.
📌 Quan trọng
ScrollSmoother
là plugin Club GSAP, KHÔNG có trong bảngsap
public trên CDN nhưgsap.min.js
hayScrollTrigger.min.js
.Nếu bạn chỉ dùng:
htmlCopyEdit<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollSmoother.min.js"></script>
→
ScrollSmoother.min.js
không tồn tại trên CDN public!
✅ Cách dùng đúng
1️⃣ Dùng link CDN chính thức từ GSAP
Vào: https://greensock.com/scrollsmoother/
Hoặc nếu bạn có tài khoản GSAP Club, tải file
.zip
về hoặc dùng link CodePenhttps://assets.codepen.io/16327/ScrollSmoother.min.js
.
Ví dụ:
htmlCopyEdit<script src="https://assets.codepen.io/16327/ScrollSmoother.min.js"></script>
svg1g.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Counter with ScrollSmoother</title>
<style>
body {
background: #111;
color: white;
font-size: 40px;
font-family: sans-serif;
margin: 0;
padding: 0;
}
.smooth-wrapper {
overflow: hidden;
}
.smooth-content {
will-change: transform;
}
.section {
padding: 150px 0;
text-align: center;
}
.counter {
font-size: 60px;
}
</style>
</head>
<body>
<div class="smooth-wrapper">
<div class="smooth-content">
<div class="section">
<div class="counter" data-target="100">0+</div>
</div>
<div class="section">
<div class="counter" data-target="200">0+</div>
</div>
<div class="section">
<div class="counter" data-target="300">0+</div>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"></script>
<script src="https://assets.codepen.io/16327/ScrollSmoother.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger, ScrollSmoother);
// Khởi tạo ScrollSmoother
ScrollSmoother.create({
wrapper: ".smooth-wrapper",
content: ".smooth-content",
smooth: 1.2, // độ mượt
effects: true
});
const CounterUpAnimation = (number, start, end, duration) => {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) {
startTimestamp = timestamp;
}
let progress = Math.min((timestamp - startTimestamp) / duration, 1);
number.innerHTML = Math.floor(progress * (end - start) + start) + '+';
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
};
// Loop qua tất cả counter
gsap.utils.toArray(".counter").forEach(counter => {
let target = parseInt(counter.getAttribute("data-target"));
ScrollTrigger.create({
trigger: counter,
start: "top 80%",
onEnter: () => {
CounterUpAnimation(counter, 0, target, 2000);
},
once: true,
markers: true // remove nếu không cần debug
});
});
</script>
</body>
</html>
Last updated