182
0

Animation Scroll với GSAP và Locomotive

182
Thời gian đọc: 8 phút

Xin chào các bạn, hiện tại chắc các bạn cũng đã thấy nhiều website sẽ có các hiệu ứng phụ thuộc vào vị trí khi các bạn scroll nhỉ? Nôm na cho dễ hiểu là, animation của các section, component sẽ thực hiện khi các bạn scroll tới nó… Thì ở bài này mình sẽ hướng dẫn các bạn làm việc đó với sự trở giúp của hai thư viện: GSAP và Locomotive. Giờ mình cùng tiềm hiểu từng cái nhé.

1/ GSAP.

1.1/ Giới thiệu

GSAP là một thư viện giúp cho các bạn có thể cấu hình animation theo ý mình. Mình ví dụ khi các bạn bấm vào 1 nút button nhưng các bạn muốn có 3 animation trồng chéo nhau như đang chạy đc animation đầu xong chạy tiếp animation 2 và 3 sau đó. Thì nếu các bạn viết bằng javascript + css không thì nó sẽ rất phức tạp và cầu kì. Thì GSAP sẽ giúp chúng ta giải quyết vấn đề này. Dưới dây là 1 số mẫu các bạn có thể coi qua để có thể hình dung hơn.

animation-gsap-locomotive
Nguồn: https://36days-21.reflektor.digital/

Như các bạn thấy 1 loạt animation được xuất hiện, thì nếu chúng ta viết thuần không thì nhìn vô là thấy không biết mình viết xong là cái web đi về đâu luôn…

Bên cạnh đó GSAP còn có các plugin hỗ trợ khác trong quá trình dev nữa.

1.2/ Một số plugin hỗ trợ:

  • Flip
    ScrollTrigger (Ở bài này chúng ta sẽ sử dụng tới ScrollTrigger)
  • MotionPathPlugin
  • PixiPlugin
  • MorphSVGPlugin

Ngoài ra còn có các plugin hỗ trợ khác nữa, các bạn có thể tham khảo thêm tại: https://greensock.com/gsap-plugins/

2/ Locomotive

2.1/ Giới thiệu

Locomotive là một thư viện giúp cho website của các bạn có thể scroll smooth hơn. Và khi smooth hơn thì animation của các bạn cũng sẽ hoạt động một cách trơn tru hơn. Nhưng nếu chỉ có vậy thì không có gì đặc biệt cả. Bởi Locomotive còn có một số tính năng khá tiện lợi và hữu ích mà mình thấy. Mục kế tiếp mình sẽ trình bài về tính năng của nó.

2.2/ Tính năng của locomotive

  • Speed Control: cho phép các bạn có thể config tốc độ scroll của website trong một lần scroll
  • Scroll Direction: cho phép các bạn có thể config / detect scroll theo vertical hoặc horizontal
  • Fixed elements: tức cho bạn scroll fix element hoặc component trong khoảng độ bao nhiêu đó thay vì các bạn scroll phát qua component khác như bình thường.

Các bạn có thể coi demo của thư viện ngay tại đây: https://locomotivemtl.github.io/locomotive-scroll/

3/ Kết hợp Locomotive và GSAP để làm animation

3.1/ Cài đặt Locomotive và GSAP.

Trước tiên các bạn cần cài đặt Locomotive và GSAP trước, các bạn có thể tải theo link mình để bên dưới:
Locomotive: https://github.com/locomotivemtl/locomotive-scroll
GSAP: https://greensock.com/

3.2/ Tạo animation

Đầu tiên chúng ta sẽ tạo file HTML trước:

<div class="smooth-scroll">
  <div class="description panel blue">
    <div><h1>Locomotive Scroll + ScrollTrigger</h1>
      <div class="scroll-down">Scroll down<div class="arrow"></div></div>
    </div>
  </div>

  <section class="panel red">
    <p><span class="line line-1"></span>Line sẽ chạy dựa trên process của khi scroll tới section, khi các bạn khai báo<code>scrub: true</code></p>
  </section>

  <section class="panel orange">
    <p><span class="line line-2"></span>Pin section cho đến khi end: 100%.</p>
  </section>

  <section class="panel relative">
    <div class="bg-pannel">
      <div class="row">
        <div class="col-3 purple"></div>
        <div class="col-3 orange"></div>
        <div class="col-3 gray"></div>
        <div class="col-3 blue"></div>
      </div>
    </div>
    <p><span class="line line-3"></span>Pha màu thêm 1 chút animation chứ các bạn nhỉ xD </p>
  </section>

  <section class="panel gray">
    DONE!
  </section>
</div>

Sau đó là thêm 1 chút css để style lại:

.panel {
  height: 100vh;
}
.panel.relative{
  position:relative;
  overflow:hidden;
}
.row{
  display:flex;
  width:100%;
  height:100%;
}
.col-3{
  flex:0 0 calc(100% / 4);
  max-width:0 0 calc(100% / 4);
  width:100%;
  transition:transform .25s ease;
}
.bg-pannel{
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:-1;
}
.line {
  width: 100%;
  max-width: 800px;
  height: 8px;
  margin: 0 0 10px 0;
  position: relative;
  display: inline-block;
  background-color: rgba(255,255,255,1);
}

.blue {
    background-color: #2c7ad2;
    background-image: radial-gradient(circle at 47% 14%, rgba(205, 205, 205,0.04) 0%, rgba(205, 205, 205,0.04) 43%,transparent 43%, transparent 100%),radial-gradient(circle at 35% 12%, rgba(215, 215, 215,0.04) 0%, rgba(215, 215, 215,0.04) 4%,transparent 4%, transparent 100%),radial-gradient(circle at 1% 35%, rgba(24, 24, 24,0.04) 0%, rgba(24, 24, 24,0.04) 37%,transparent 37%, transparent 100%),radial-gradient(circle at 21% 1%, rgba(0, 0, 0,0.04) 0%, rgba(0, 0, 0,0.04) 26%,transparent 26%, transparent 100%),radial-gradient(circle at 23% 82%, rgba(249, 249, 249,0.04) 0%, rgba(249, 249, 249,0.04) 60%,transparent 60%, transparent 100%),radial-gradient(circle at 11% 54%, rgba(251, 251, 251,0.04) 0%, rgba(251, 251, 251,0.04) 23%,transparent 23%, transparent 100%),radial-gradient(circle at 69% 68%, rgba(234, 234, 234,0.04) 0%, rgba(234, 234, 234,0.04) 10%,transparent 10%, transparent 100%),linear-gradient(90deg, #2c7ad2,#1568c6);
}
.gray {
    background-color: #777;
    background-image: radial-gradient(circle at 47% 14%, rgba(205, 205, 205,0.04) 0%, rgba(205, 205, 205,0.04) 43%,transparent 43%, transparent 100%),radial-gradient(circle at 35% 12%, rgba(215, 215, 215,0.04) 0%, rgba(215, 215, 215,0.04) 4%,transparent 4%, transparent 100%),radial-gradient(circle at 1% 35%, rgba(24, 24, 24,0.04) 0%, rgba(24, 24, 24,0.04) 37%,transparent 37%, transparent 100%),radial-gradient(circle at 21% 1%, rgba(0, 0, 0,0.04) 0%, rgba(0, 0, 0,0.04) 26%,transparent 26%, transparent 100%),radial-gradient(circle at 23% 82%, rgba(249, 249, 249,0.04) 0%, rgba(249, 249, 249,0.04) 60%,transparent 60%, transparent 100%),radial-gradient(circle at 11% 54%, rgba(251, 251, 251,0.04) 0%, rgba(251, 251, 251,0.04) 23%,transparent 23%, transparent 100%),radial-gradient(circle at 69% 68%, rgba(234, 234, 234,0.04) 0%, rgba(234, 234, 234,0.04) 10%,transparent 10%, transparent 100%),linear-gradient(90deg, #777,#606060);
}
.orange {
    background-color: #e77614;
    background-image: radial-gradient(circle at 46% 40%, rgba(228, 228, 228,0.06) 0%, rgba(228, 228, 228,0.06) 13%,transparent 13%, transparent 100%),radial-gradient(circle at 11% 41%, rgba(198, 198, 198,0.06) 0%, rgba(198, 198, 198,0.06) 19%,transparent 19%, transparent 100%),radial-gradient(circle at 52% 23%, rgba(14, 14, 14,0.06) 0%, rgba(14, 14, 14,0.06) 69%,transparent 69%, transparent 100%),radial-gradient(circle at 13% 85%, rgba(148, 148, 148,0.06) 0%, rgba(148, 148, 148,0.06) 44%,transparent 44%, transparent 100%),radial-gradient(circle at 57% 74%, rgba(232, 232, 232,0.06) 0%, rgba(232, 232, 232,0.06) 21%,transparent 21%, transparent 100%),radial-gradient(circle at 59% 54%, rgba(39, 39, 39,0.06) 0%, rgba(39, 39, 39,0.06) 49%,transparent 49%, transparent 100%),radial-gradient(circle at 98% 38%, rgba(157, 157, 157,0.06) 0%, rgba(157, 157, 157,0.06) 24%,transparent 24%, transparent 100%),radial-gradient(circle at 8% 6%, rgba(60, 60, 60,0.06) 0%, rgba(60, 60, 60,0.06) 12%,transparent 12%, transparent 100%),linear-gradient(90deg, #ff7600, #ff7600);
}
.purple {
    background-color: #8d3dae;
    background-image: radial-gradient(circle at 47% 14%, rgba(205, 205, 205,0.04) 0%, rgba(205, 205, 205,0.04) 43%,transparent 43%, transparent 100%),radial-gradient(circle at 35% 12%, rgba(215, 215, 215,0.04) 0%, rgba(215, 215, 215,0.04) 4%,transparent 4%, transparent 100%),radial-gradient(circle at 1% 35%, rgba(24, 24, 24,0.04) 0%, rgba(24, 24, 24,0.04) 37%,transparent 37%, transparent 100%),radial-gradient(circle at 21% 1%, rgba(0, 0, 0,0.04) 0%, rgba(0, 0, 0,0.04) 26%,transparent 26%, transparent 100%),radial-gradient(circle at 23% 82%, rgba(249, 249, 249,0.04) 0%, rgba(249, 249, 249,0.04) 60%,transparent 60%, transparent 100%),radial-gradient(circle at 11% 54%, rgba(251, 251, 251,0.04) 0%, rgba(251, 251, 251,0.04) 23%,transparent 23%, transparent 100%),radial-gradient(circle at 69% 68%, rgba(234, 234, 234,0.04) 0%, rgba(234, 234, 234,0.04) 10%,transparent 10%, transparent 100%),linear-gradient(90deg, #8d3dae,#8d3dae);
}

Ok vậy là phần đầu tiên của animation khi chúng ta xây dựng html / css trước, giờ sẽ là đến việc chúng ta tạo animation nhé. Trước hết thì mình sẽ tạo file js

Đầu tiên, thì mình sẽ khai báo register cho plugin ScrollTrigger và DOM tới class scroll-smooth để tạo scroll smooth bằng Locomotive.

gsap.registerPlugin(ScrollTrigger);

const locoScroll = new LocomotiveScroll({
  el: document.querySelector(".smooth-scroll"),
  smooth: true
});

// Dòng này có tác dụng là để mỗi lần các bạn scroll nó sẽ sync với position của section / element trong browser
locoScroll.on("scroll", ScrollTrigger.update);

// Kế tiếp mình set cho scrollTrigger dùng class smooth-scroll để trigger animation. Vì hiện tại chúng ta đang dùng class này để sử dụng locomotive
ScrollTrigger.scrollerProxy(".smooth-scroll", {
  scrollTop(value) {
    return arguments.length ? locoScroll.scrollTo(value, 0, 0) : locoScroll.scroll.instance.scroll.y;
  },
  getBoundingClientRect() {
    return {top: 0, left: 0, width: window.innerWidth, height: window.innerHeight};
  },
});

Về cách import ScrollTrigger các bạn có thể follow theo link sau: https://greensock.com/docs/v3/Installation?checked=core,scrollTrigger#CDN

Tại đây các bạn scroll xuống dưới và sẽ thấy một giao diện như thế này rồi chọn phần cách bạn import gsap, sau đó dưới browser code sẽ render ra 1 đoạn code import cho các bạn:

Kế tiếp mình sẽ thực hiện animation khi ta scroll tới section đầu tiên với param scrub: true,

gsap.from(".line-1", { // class của component sẽ thực hiện animation. Class này phải nằm trong component trigger bên dưới nhé. dưới 
  scrollTrigger: {
    trigger: ".line-1", // class của component các bạn muốn trigger tới
    scroller: ".smooth-scroll", // proxy scroll
    scrub: true,
    start: "top top",
    end: "+=100%",
    onUpdate(self) { // đây là method update của trigger, các bạn có thể dùng nó để tùy biến animation thêm nếu cần thiết, truyền self vào để các bạn nắm rõ hơn nó sẽ trả về gì.
      console.log(self)
    }
  },
  scaleX: 0, // set animation từ bao nhiêu đó cho đến 100% (process)
  transformOrigin: "left center", 
  ease: "none"
});

Kế nữa là chúng ta sẽ tạo animation với pin:true

gsap.from(".line-2", {
  scrollTrigger: {
    trigger: ".panel.orange",
    scroller: ".smooth-scroll",
    scrub: true,
    pin: true,
    start: "top top",
    end: "+=100%",
    onUpdate() {
      console.log("Update")
    }
  },
  scaleX: 0, 
  transformOrigin: "left center", 
  ease: "none"
});

Và thêm một cái nữa là mình sẽ kết hợp 2 animation với nhau bằng cách sử dụng timeline của gsap:

var tl = gsap.timeline({
    scrollTrigger: {
      trigger: ".panel.relative",
      scroller: ".smooth-scroll",
      scrub: true,
      pin: true,
      start: "top top",
      end: "+=100%",
      onUpdate() {
        console.log("Update")
      }
    }
  });

tl.from(".purple", {translateY: "+=100%",ease: "none"}, 0).to(".purple", {translateY: "+=0%",ease: "none"}, 0);

tl.from(".panel.relative .gray", {translateY: "+=100%",ease: "none"}, 0).to(".panel.relative .gray", {translateY: "+=0%",ease: "none"}, 0);

tl.from(".panel.relative .orange", {translateY: "-=100%",ease: "none"}, 0).to(".panel.relative .orange", {translateY: "+=0%",ease: "none"}, 0);

tl.from(".panel.relative .blue", {translateY: "-=100%",ease: "none"}, 0).to(".panel.relative .blue", {translateY: "+=0%",ease: "none"}, 0);

tl.from(".panel.relative p", {scale: 0.3, rotation:45, autoAlpha: 0, ease: "power2"})
  .from(".line-3", {scaleX: 0, transformOrigin: "left center", ease: "none"}, 1);

ScrollTrigger.addEventListener("refresh", () => locoScroll.update()); // Mỗi lần window browser thay đổi, StrollTrigger refresh lại và update locomotive

ScrollTrigger.refresh();

Ở đây các bạn có thể thấy khác với những cái khác, thay vì gsap.to thì mình dùng gsap.timeline. Tức là dòng thời gian của animation. Phần config animation thì mình vẫn sẽ làm như cũ. Nhưng các bạn để ý các phần tiếp nhé. Ở các dòng kế, mình khai báo từ fromto tức animation sẽ bắt đầu từ và kết thúc tại.

Cú pháp: from(“element”, {option css animation}, duration) (Tương tự như vậy với to)

Từ đó các bạn có thể hiểu rằng là, ở các phần mình khai báo duration là 0 tức nó sẽ thực hiện đầu tiên, sau đó sẽ là các animation tiếp theo, theo thứ tự duration tăng dần. Và dưới dây là kết quả:

4/ Tổng kết

Như vậy tụi mình đã giới thiệu cho các bạn cách tạo animation và control chúng khi bạn scroll browser của mình. Đồng thời cũng giới thiệu cho các bạn biết về 2 thư viện GSAP để tạo animation và Locomotive để kết hợp với scrollTrigger của gsap. Hi vọng bài viết sẽ bổ ích và giúp ích cho các bạn có thể mở rộng ý tưởng về animation cho website của các bạn. Hẹn gặp các bạn trong bài tới. Và dưới đây chính là toàn bộ kết quả của chúng ta.