+2

Tạo hiệu ứng cuộn trang mượt mà khi lăn chuột với thư viện Framer Motion trong React

Demo sản phẩm

Link: https://thuanhmdev.github.io/react-framer-motion-smooth-when-wheel/

1. Giới thiệu

Trong thế giới phát triển web hiện đại, trải nghiệm người dùng là yếu tố quan trọng hàng đầu. Một trong những chi tiết nhỏ nhưng có thể tạo nên sự khác biệt lớn chính là hiệu ứng cuộn trang mượt mà. Thay vì cuộn trang theo cách thông thường, chúng ta có thể tạo ra một trải nghiệm cuộn mượt mà, tạo cảm giác cao cấp và chuyên nghiệp hơn cho website.

Trong bài viết này, chúng ta sẽ tìm hiểu cách tạo hiệu ứng cuộn trang mượt mà bằng cách sử dụng thư viện Framer Motion trong React. Framer Motion là một thư viện animation mạnh mẽ cho React, cung cấp các API đơn giản để tạo ra các hiệu ứng phức tạp.

2. Cài đặt

npm install framer-motion

3. Code mẫu và giải thích Code

Hãy xem xét code của component SmoothScroll:

import { motion, useScroll, useSpring, useTransform } from "framer-motion";
import React, { useLayoutEffect, useRef, useState } from "react";

export default function SmoothScroll({
  children,
}: {
  children: React.ReactNode;
}) {
  const { scrollYProgress } = useScroll();
  const smoothProgress = useSpring(scrollYProgress, {
    mass: 0.1, 
    stiffness: 100, 
    damping: 30, 
  });

  const [contentHeight, setContentHeight] = useState(0);
  const contentRef = useRef<HTMLDivElement>(null);

  const y = useTransform(
    smoothProgress,
    [0, 1],
    contentHeight ? [0, -(contentHeight - window.innerHeight)] : [0, 0]
  );

  useLayoutEffect(() => {
    const handleResize = () => {
      if (contentRef.current) {
        setContentHeight(contentRef.current.scrollHeight);
      }
    };

    handleResize();
    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

  return (
    <>
      <div style={{ height: contentHeight }} />
      <motion.div
        className="scrollBody"
        style={{
          y,
          position: "fixed",
          top: 0,
          left: 0,
          right: 0,
          overflowY: "hidden",
        }}
        ref={contentRef}
      >
        {children}
      </motion.div>
    </>
  );
}

Hãy phân tích từng phần của code:

3.1. Import các hook cần thiết

import { motion, useScroll, useSpring, useTransform } from "framer-motion";
import React, { useLayoutEffect, useRef, useState } from "react";

3.2 Sử dụng các hook của Framer Motion

const { scrollYProgress } = useScroll();
const smoothProgress = useSpring(scrollYProgress, {
  mass: 0.1,
  stiffness: 100,
  damping: 30,
});

useScroll() theo dõi tiến trình cuộn của trang.
useSpring() tạo ra một animation mượt mà dựa trên physics, với các tham số có thể điều chỉnh:

  • mass: Điều chỉnh "quán tính" của animation. Giá trị càng lớn, animation càng chậm khi bắt đầu và kết thúc.
    
  • stiffness: Điều chỉnh "độ đàn hồi". Giá trị càng cao, animation càng nhanh và "bật" mạnh hơn.
    
  • damping: Điều chỉnh "độ mượt". Kiểm soát mức độ animation sẽ dừng lại mượt mà.
    

3.3 Quản lý state và ref

const [contentHeight, setContentHeight] = useState(0);
const contentRef = useRef<HTMLDivElement>(null);

contentHeight lưu trữ chiều cao của nội dung.
contentRef tham chiếu đến phần tử DOM chứa nội dung.

3.4 Tính toán giá trị transform

const y = useTransform(
  smoothProgress,
  [0, 1],
  contentHeight ? [0, -(contentHeight - window.innerHeight)] : [0, 0]
);

useTransform được sử dụng để chuyển đổi giá trị cuộn thành giá trị dịch chuyển Y. Nó kiểm tra contentHeight để tránh các giá trị không hợp lệ khi component mới được mount.

3.5. Xử lý sự kiện resize

 useLayoutEffect(() => {
    const handleResize = () => {
      if (contentRef.current) {
        setContentHeight(contentRef.current.scrollHeight);
      }
    };

    handleResize();
    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
    };
  }, []);

Đoạn code này đảm bảo rằng contentHeight được cập nhật khi kích thước cửa sổ thay đổi.

3.6 Render component

return (
  <>
    <div style={{ height: contentHeight }} />
    <motion.div
      className="scrollBody"
      style={{
        y,
        position: "fixed",
        top: 0,
        left: 0,
        right: 0,
        overflowY: "hidden",
      }}
      ref={contentRef}
    >
      {children}
    </motion.div>
  </>
);

Một div trống với chiều cao bằng contentHeight được tạo ra để giữ cho scrollbar của trình duyệt hoạt động bình thường.</br> motion.div chứa nội dung thực sự, với style position: fixed và transform y để tạo hiệu ứng cuộn mượt mà.</br>

3.7 Sử dụng SmoothScroll

Để sử dụng component này trong ứng dụng của bạn:

function App() {
  return (
    <SmoothScroll>
      {/* Nội dung của trang web của bạn */}
      <h1>Welcome to My Smooth Scrolling Website</h1>
      <p>This content will scroll smoothly.</p>
      {/* ... */}
    </SmoothScroll>
  );
}

Kết luận

Component SmoothScroll này sử dụng sức mạnh của Framer Motion để tạo ra hiệu ứng cuộn mượt mà, nâng cao trải nghiệm người dùng trên trang web của bạn. Bằng cách điều chỉnh các tham số của useSpring, bạn có thể tùy chỉnh cảm giác của animation để phù hợp với thiết kế của mình.

link source: https://github.com/thuanhmdev/react-framer-motion-smooth-when-wheel


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí