Sử dụng GSAP ScrollTrigger với pin để bạn hiểu cách hoạt động
Nguồn tài liệu ở đây: https://github.com/dddodddo/basic
Hướng dẫn sử dụng:
Họ đã đưa lên

Giờ muốn chạy web/FlipScroll

Trên trình duyệt https://dddodddo.github.io/basic/web/FlipScroll

Chú ý:

✅ 1. Mục tiêu
🔹 Pin một section (hoặc element) khi scroll tới nó 🔹 Trong thời gian pin, nội dung scroll tiếp nhưng section vẫn giữ cố định
Ví dụ 1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP ScrollTrigger Pin Example</title>
<style>
body {
margin: 0;
font-family: sans-serif;
background: #f0f0f0;
}
section {
height: 100vh;
display: grid;
justify-items: end;
align-items: baseline;
align-content: baseline;
justify-content: center;
}
.pin-section {
background: #007bff;
color: white;
}
.normal-section {
background: #ccc;
color: #333;
}
</style>
</head>
<body>
<section class="normal-section">Section 1</section>
<section class="pin-section">
<h1>🚀 Ứng dụng thực tế</h1>
<div>✅ Pin hero section khi scroll xuống</div>
<div>✅ Pin sidebar menu khi scroll qua content dài</div>
<div>✅ Pin ảnh background khi scroll nội dung text</div>
</section>
<section class="normal-section">Section 3</section>
<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>
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({
trigger: ".pin-section", // phần tử được pin
start: "top top", // khi top của section chạm top viewport thì bắt đầu pin
end: "+=500", // pin trong 500px scroll
pin: true, // bật pinning cho section
markers: true, // để debug vị trí start/end
scrub: true, // Gắn animation theo scroll
anticipatePin: 1 // Mượt hơn khi pin trong các layout phức tạp
});
</script>
</body>
</html>
Ví dụ 2:
Khi bắt đầu đến cuối nó mới hoạt động

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP ScrollTrigger Pin + Scrub + Timeline</title>
<style>
body {
margin: 0;
font-family: sans-serif;
background: #f0f0f0;
}
section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 40px;
font-weight: bold;
}
.pin-section {
background: #007bff;
color: white;
flex-direction: column;
}
.pin-section h1 {
margin: 0;
font-size: 50px;
}
.pin-section p {
font-size: 20px;
opacity: 0;
transform: translateY(50px);
}
.normal-section {
background: #ccc;
color: #333;
}
</style>
</head>
<body>
<section class="normal-section">Section 1</section>
<section class="pin-section">
<h1>Hero Title</h1>
<p>This is the hero description appearing on scroll.</p>
</section>
<section class="normal-section">Section 3</section>
<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>
gsap.registerPlugin(ScrollTrigger);
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".pin-section",
start: "top top",
end: "+=500", // pin trong 500px scroll
pin: true,
scrub: true, // Gắn animation theo scrol
markers: true
}
});
// Animate h1 scale down and fade out
tl.to(".pin-section h1", {
scale: 0.8,
opacity: 0.5,
duration: 1
}, 0);
// Animate p fade in and move up
tl.to(".pin-section p", {
opacity: 1,
y: 0,
duration: 1
}, 0.5); // bắt đầu sau 0.5s trong timeline
</script>
</body>
</html>
Ví dụ 3:
— pinSpacing: false

— pinSpacing: true

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ScrollTrigger PinSpacing: false Example</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f0f0f0;
}
.section {
height: 100vh;
/* Chiếm toàn bộ chiều cao viewport */
display: flex;
justify-content: center;
align-items: center;
font-size: 2em;
color: white;
text-align: center;
}
.section-1 {
background-color: #a7d9f2;
}
/* Xanh dương nhạt */
.section-2 {
background-color: #8cde8c;
}
/* Xanh lá nhạt */
.section-3 {
background-color: #ff9999;
}
/* Đỏ nhạt */
.section-4 {
background-color: #ffff99;
}
/* Vàng nhạt */
.section-5 {
background-color: #cc99ff;
}
/* Tím nhạt */
.pin-box {
width: 200px;
height: 150px;
background-color: #007bff;
/* Xanh dương đậm */
color: white;
display: flex;
justify-content: center;
align-items: center;
font-size: 1.5em;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
/* Để dễ thấy sự khác biệt khi pinSpacing: false */
margin: 20px;
position: relative;
/* Quan trọng để pin hoạt động với pinSpacing: false */
z-index: 10;
/* Đảm bảo nó nằm trên các phần tử khác khi ghim */
}
.content-after-pin {
background-color: #f7e1b5;
/* Cam nhạt */
padding: 50px;
text-align: center;
color: #333;
font-size: 1.2em;
/* Tự thêm padding/margin để tránh bị chồng lên pin-box khi pinSpacing: false */
margin-top: 170px;
/* Ví dụ: nếu pin-box cao 150px + 20px margin-top + 20px margin-bottom */
}
/* Thêm một số nội dung để trang dài hơn */
.long-text {
padding: 50px;
font-size: 1.1em;
line-height: 1.6;
background-color: #fff;
margin-top: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="section section-1">
<h1>Phần 1: Cuộn xuống để thấy hiệu ứng ghim</h1>
</div>
<div class="section section-2">
<p>Phần 2</p>
</div>
<div class="pin-box">
HỘP NÀY ĐƯỢC GHIM
</div>
<div class="content-after-pin">
<p>Đây là nội dung ngay sau "HỘP NÀY ĐƯỢC GHIM".</p>
<p>Khi `pinSpacing: false`, nội dung này sẽ cuộn lên và có thể **chồng lên** hộp được ghim, vì ScrollTrigger không
tự động thêm khoảng trống bù.</p>
<p>Bạn sẽ cần tự điều chỉnh CSS (ví dụ: `margin-top` cho phần tử này) nếu muốn tránh sự chồng chéo.</p>
</div>
<div class="section section-3">
<p>Phần 3</p>
</div>
<div class="section section-4">
<p>Phần 4</p>
</div>
<div class="section section-5">
<p>Phần 5</p>
</div>
<div class="long-text">
<h2>Giới thiệu về nội dung dài</h2>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
<p>Maecenas in massa efficitur, dapibus nisi nec, scelerisque ipsum. Mauris vitae nisl et nibh hendrerit vestibulum.
Sed non risus at orci eleifend faucibus. Proin ut elit non turpis malesuada bibendum. Fusce vel enim ac nisi
aliquet congue. Phasellus sit amet lorem non libero egestas laoreet. Praesent aliquet metus at tellus feugiat, non
hendrerit lacus finibus.</p>
<p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec dictum, libero eu
vulputate dictum, elit odio efficitur arcu, a consequat magna enim ac velit. Aliquam erat volutpat. Sed vitae
felis at neque suscipit facilisis. Nam sit amet velit quis metus volutpat suscipit. Sed quis dolor id elit blandit
semper eu vel urna.</p>
<p>Nunc quis justo vitae nulla efficitur tincidunt. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. Morbi id ligula non metus feugiat dictum. Vestibulum vitae purus id odio
ullamcorper vehicula. Curabitur elementum orci a sem elementum, vel vulputate velit tristique.</p>
<p>Aenean ut lorem nec velit mattis feugiat. Integer vel justo in quam euismod laoreet. Quisque scelerisque risus eu
justo volutpat, at maximus neque consectetur. Praesent commodo magna id arcu bibendum, ac vulputate leo malesuada.
Curabitur vel turpis ac lectus hendrerit tincidunt.</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
<script>
// Đăng ký plugin ScrollTrigger
gsap.registerPlugin(ScrollTrigger);
gsap.to(".pin-box", {
scrollTrigger: {
trigger: ".pin-box",
pin: true,
start: "top center", // Bắt đầu ghim khi đỉnh của pin-box chạm vào giữa viewport
end: "+=500", // Ghim trong khoảng 500px cuộn
pinSpacing: true, // <-- ĐÂY LÀ PHẦN QUAN TRỌNG
markers: true, // Hiển thị markers để dễ debug
}
});
</script>
</body>
</html>
Ví dụ 4: Nghiên cứu bài toán dap dụng pin (chưa hiểu rõ ý nghĩa cần xem lại)

index.html
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Hero & Column Pin</title>
<link rel='stylesheet' href='https://codepen.io/GreenSock/pen/xxmzBrw.css'><link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div class="header"></div>
<div class="wrapper">
<div class="hero gradient-purple"></div>
<div class="row left-row">
<div class="column gradient-green">
<h1>Lorem ipsum dolor sit amet.</h1>
</div>
</div>
<div class="row right-row">
<div class="column gradient-blue">
<h1>Lorem ipsum dolor sit amet.</h1>
</div>
</div>
</div>
<div class="spacer gradient-red"></div>
<!-- partial -->
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script src="./script.js"></script>
</body>
</html>
script.js
console.clear();
gsap.registerPlugin(ScrollTrigger);
// pin the image
const heroSt = ScrollTrigger.create({
trigger: ".hero",
start: "bottom 250",
end: "top bottom",
endTrigger: ".spacer",
pin: true,
pinSpacing: false,
// markers: true,
id: "hero"
});
// Pin left column
ScrollTrigger.create({
trigger: ".left-row",
start: () => heroSt.start,
end: () => heroSt.end,
pin: true,
pinSpacing: false,
// markers: { indent: 150 },
id: "lft"
});
// Pin right column
ScrollTrigger.create({
trigger: ".right-row",
start: "top 250",
end: heroSt.end,
pin: true,
pinSpacing: false,
// markers: { indent: 300 },
id: "rgt"
});
style.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.header {
position: fixed;
width: 100%;
height: 200px;
background-color: #a0a0a0;
z-index: 10;
}
.wrapper {
padding-top: 200px;
}
.hero {
width: 100%;
height: 40vh;
}
.row {
display: flex;
justify-content: flex-start;
width: 100%;
height: 100vh;
}
.right-row {
justify-content: flex-end;
}
.column {
width: 50%;
height: 100%;
}
.spacer {
width: 100%;
height: 100vh;
}
Ví dụ 5: Cũng không hiểu rõ ví dụ này

index.html
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Loop Absolute Cards</title>
<link rel='stylesheet' href='https://codepen.io/GreenSock/pen/xxmzBrw.css'><link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<main>
<div class="panel gradient-green"></div>
<div class="wrapper">
<div class="card">
<p>Card 1</p>
</div>
<div class="card">
<p>Card 2</p>
</div>
<div class="card">
<p>Card 3</p>
</div>
</div>
<div class="panel gradient-blue"></div>
</main>
<!-- partial -->
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script src="./script.js"></script>
</body>
</html>
script.js
console.clear();
gsap.registerPlugin(ScrollTrigger);
const cards = gsap.utils.toArray(".card");
gsap.set(cards, { xPercent: -50, yPercent: 100, autoAlpha: 1 });
const tl = gsap.timeline({
defaults: {
duration: 1,
ease: "power1.inOut"
},
scrollTrigger: {
trigger: ".wrapper",
start: "top top",
end: "+=150%",
pin: "main",
scrub: true,
markers: true
}
});
cards.forEach((card, i) => {
tl.to(
card,
{
y: -window.innerHeight * 0.4,
yPercent: 0
},
"<"
).to(card, {
autoAlpha: 0,
duration: 0.5
});
});
style.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.panel,
.wrapper {
position: relative;
width: 100%;
height: 80vh;
min-height: unset;
}
.card {
position: absolute;
left: 50%;
bottom: 0;
visibility: hidden;
font-size: 30px;
}
.card p {
margin: 0;
}
Ví dụ 6:
index.html
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - A Pen by michael</title>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div class='content content--center'>
<h3 class='title'>Techne</h3>
<h2 class='text' data-expand-2>
<span class='content--center-text'>Technical skill</span>
<span class='content--center-text'>is mastery of complexity</span>
<span class='expanding-text expanding-text--center'>
<span class='aright'>while </span>
<span class='expanding-text-img'>
<span
class='expanding-text-img-inner'
style='background-image: url(https://placehold.co/600x400.png)'></span>
</span>
<span class='anim'>creativity</span>
</span>
<span class='content--center-text'>is mastery of simplicity.</span>
</h2>
<p class='block'>
<a
rel='noopener'
target='_blank'
href='https://knowyourmeme.com/memes/inside-you-there-are-two-wolves'
>Inside me are two wolves</a
>. One fascinated with the complexities of modern software, and the other attracted to
minimalism and simplicty. As an engineer, i harness the strengths of both wolves to
balance complexity and simplicity in all my work. In other words, i use complex tools
to create simple solutions.
</p>
</div>
<!-- partial -->
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://assets.codepen.io/16327/Flip.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script src="./script.js"></script>
</body>
</html>
script.js
const wrapElement = document.querySelector('[data-expand-2]');
const image = wrapElement.querySelector('.expanding-text-img')
const expandTexts = wrapElement.querySelectorAll('.anim')
const textBlock = wrapElement.nextElementSibling
wrapElement.classList.add('text--open')
const flipstate = Flip.getState(image)
wrapElement.classList.remove('text--open')
Flip.to(flipstate, {
ease: 'sine',
simple: true,
scrollTrigger: {
trigger: wrapElement,
start: 'center bottom',
end: 'center top',
scrub: true
}
}).to(expandTexts, {
ease: 'sine',
skewX: -20,
scrollTrigger: {
trigger: wrapElement,
start: 'center bottom',
end: 'center top',
scrub: true
}
})
style.css
.content {
padding-top: 50vh;
display: grid;
gap: 2rem;
grid-template-columns: 100%;
grid-template-areas: "text" "block";
grid-template-rows: auto 70vh auto;
}
.content--center-text {
display: block;
}
.title {
grid-area: title;
font-weight: inherit;
margin-bottom: 5vh;
}
.text {
grid-area: text;
margin: 0;
font-size: clamp(2rem, 4.75vw, 5rem);
text-transform: none;
line-height: 1.1;
font-weight: normal;
}
.expanding-text {
vertical-align: top;
display: inline-grid;
grid-template-columns: min-content;
}
.expanding-text--center,
.expanding-text--full {
display: block;
}
.text--open .expanding-text {
height: 40vh;
}
.expanding-text-img {
display: block;
position: relative;
aspect-ratio: 16/9;
width: 0%;
height: auto;
overflow: hidden;
border-radius: 4rem;
}
.expanding-text-img-inner {
display: block;
width: 100%;
height: 100%;
background-size: cover;
background-position: center;
}
.text--open .expanding-text-img {
width: 100%;
}
.anim {
display: inline-block;
white-space: nowrap;
}
.block {
grid-area: block;
margin: 50px 0px;
max-width: 400px;
line-height: 1.5;
font-size: 18px;
color: black;
}
.block a {
color: black;
text-decoration: underline;
}
@media screen and (min-width: 40em) {
.content--center {
text-align: center;
place-items: center;
grid-template-areas: "title" "text" "block";
grid-template-rows: auto 40vw auto;
}
.content--center-text:is(:last-child) {
align-self: start;
}
.content--center-text:is(:last-child)::after {
content: "- Christopher Zeeman";
font-size: 20px;
}
.text--open .expanding-text-img {
max-width: 50vw;
}
.expanding-text--center {
display: inline-grid;
gap: 0.15em;
justify-content: center;
grid-template-columns: 1fr min-content 1fr;
}
.aright {
text-align: right;
}
.anim {
height: 100px;
}
}

Ví dụ 6: Cũng không hiểu rõ ví dụ này
index.html
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - A Pen by Rodrigo Hernando</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel='stylesheet' href='https://codepen.io/GreenSock/pen/xxmzBrw.css'>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<div class="hero panel center">
<h1>HERO</h1>
</div>
<div class="first panel gradient-blue">
<div class="content">
<h3>Lorem, ipsum dolor.</h3>
<h3>Earum, ad quidem!</h3>
<h3>Iusto, minus quis?</h3>
<h3>Aut, quae quidem!</h3>
</div>
</div>
<div class="panel gradient-green"></div>
<!-- partial -->
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script src="./script.js"></script>
</body>
</html>
script.js
console.clear();
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({
trigger: ".hero",
start: "top top",
end: "max",
pin: true,
pinSpacing: false
});
const titles = gsap.utils.toArray(".first h3");
titles.forEach(title => {
gsap.to(title, {
y: -50,
opacity: 0,
scrollTrigger: {
trigger: title,
start: "top 25%",
end: "+=200",
scrub: true,
markers: true
}
});
});
style.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.hero,
.panel {
position: relative;
}
.content {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.content h3 {
width: 100%;
text-align: center;
color: var(--dark);
margin: 75px 0;
}
Ví dụ 7: Cũng không hiểu rõ ví dụ này
<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>CodePen - Right Column Callbacks</title>
<link rel='stylesheet' href='https://codepen.io/GreenSock/pen/xxmzBrw.css'><link rel="stylesheet" href="./style.css">
</head>
<body>
<!-- partial:index.partial.html -->
<section class="section gradient-green"></section>
<section class="column-wrapper">
<div class="column left gradient-purple">
<div class="column-content">
<h1>H1 Element 1</h1>
</div>
<div class="column-content">
<h1>H1 Element 2</h1>
</div>
<div class="column-content">
<h1>H1 Element 3</h1>
</div>
<div class="column-content">
<h1>H1 Element 4</h1>
</div>
<div class="column-content">
<h1>H1 Element 5</h1>
</div>
<div class="column-content">
<h1>H1 Element 6</h1>
</div>
</div>
<div class="column right gradient-orange">
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1535730480175-8f43dbb6f894?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1472791108553-c9405341e398?q=80&w=2137&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1455156218388-5e61b526818b?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1611252871536-305c663777ab?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1503614472-8c93d56e92ce?q=80&w=2011&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
<div class="column-content image">
<img src="https://images.unsplash.com/photo-1715604723676-7e7fb8607478?q=80&w=2072&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="">
</div>
</div>
</section>
<section class="section gradient-blue"></section>
<!-- partial -->
<script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
<script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script src="./script.js"></script>
</body>
</html>
console.clear();
gsap.registerPlugin(ScrollTrigger);
const images = gsap.utils.toArray(".column-content.image");
const titleContainers = gsap.utils.toArray(".column.left .column-content");
ScrollTrigger.create({
trigger: ".column-wrapper",
start: "top top",
end: "+=" + (images.length - 1) * 100 + "%",
pin: ".column.right",
markers: true
});
titleContainers.forEach((title, i) => {
ScrollTrigger.create({
trigger: title,
start: "top 60%",
end: "top 60%",
markers: {
indent: 150 * (i + 1)
},
id: i + 1,
onEnter: () => {
if (i) {
gsap.to(images, {
yPercent: -100 * i,
ease: "power1.inOut",
overwrite: true
});
}
},
onEnterBack: () => {
if (i) {
gsap.to(images, {
yPercent: -100 * (i - 1),
ease: "power1.inOut",
overwrite: true
});
}
}
});
});
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.section {
width: 100%;
height: 100vh;
}
.column-wrapper {
width: 100%;
display: flex;
}
.column {
flex: 1 0 50%;
min-height: 100vh;
}
.column.right {
height: 100vh;
overflow: hidden;
}
.column-content {
width: 100%;
height: 100vh;
font-size: 40px;
display: flex;
justify-content: center;
align-items: center;
color: var(--dark);
}
.column-content.image img {
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
Ví dụ 8: Cũng không hiểu rõ ví dụ này
PreviousSử dụng GSAP Timeline để animate show/hide search box khi click button (ok)NextĐặt chiều cao bằng nhau, Set equal height, same height (ok)
Last updated