Mục lục
Trong thế giới Frontend hiện đại, một website "chạy được" thôi chưa đủ. Khách hàng và người dùng ngày càng khó tính, họ đòi hỏi một website phải "sống động", có hồn và tạo ra cảm giác cao cấp (Premium UX). Khác biệt giữa một website rẻ tiền và một website tiền tỷ nhiều khi nằm ở những chuyển động cực kỳ nhỏ bé - được gọi là Microinteractions.
Và để làm chủ được nghệ thuật Animation trong React, không có thư viện nào mạnh mẽ, dễ dùng và "đỉnh" hơn Framer Motion. Hôm nay chúng ta sẽ mổ xẻ thư viện này từ cơ bản đến nâng cao nhé.
Microinteractions Là Gì? Tại Sao Quan Trọng?
Microinteractions (Tương tác vi mô) là những phản hồi vật lý hoặc hình ảnh rất nhỏ khi người dùng thực hiện một thao tác trên UI. Ví dụ:
- Khi bạn rà chuột qua nút "Submit", nút đó hơi phình to ra và sáng lên.
- Khi bạn bấm Like, trái tim nảy bật lên như lò xo.
- Khi đóng một Modal, nó không biến mất lập tức mà trượt nhẹ xuống và mờ dần.
Mắt người rất nhạy cảm với chuyển động. Những chuyển động mượt mà (có tính vật lý) mang lại cho người dùng cảm giác "ứng dụng này đang phản hồi lại tôi", tạo ra sự tin tưởng và cảm giác thao tác trơn tru, cao cấp.
Cài Đặt Framer Motion
Cài đặt thư viện vào dự án React hoặc Next.js của bạn cực kỳ đơn giản:
npm install framer-motion
# hoặc
yarn add framer-motion
# hoặc
pnpm add framer-motion
Lưu ý quan trọng cho Next.js App Router: Framer Motion thao tác trực tiếp vào DOM và
Event Listeners, nên mọi component sử dụng Framer Motion bắt buộc phải là Client Component (phải có
"use client"; ở đầu file).
Nguyên Lý Cơ Bản (The Core)
Để tạo animation, bạn không dùng thẻ HTML thông thường như <div> hay
<button>, mà bạn phải sử dụng đối tượng motion từ thư viện:
<motion.div>, <motion.button>.
Có 3 thuộc tính chính (Props) mà bạn cần thuộc lòng:
initial: Trạng thái bắt đầu (trước khi animation chạy).animate: Trạng thái kết thúc mong muốn (Framer Motion sẽ tự động nội suy đường đi từ initial đến animate).transition: Quản lý cách thức di chuyển (nhanh/chậm, độ nảy, delay...).
import { motion } from "framer-motion";
export function SimpleBox() {
return (
<motion.div
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="w-32 h-32 bg-indigo-500 rounded-xl"
/>
);
}
Đoạn code trên sẽ tạo ra một chiếc hộp xuất hiện từ dưới lên (y: 50 -> 0) và mờ dần rõ lên (opacity: 0 -> 1) trong vòng 0.5 giây.
Bí Mật Của Lò Xo (Spring Physics)
Đừng bao giờ dùng hiệu ứng linear hay ease thông thường cho các thao tác của
người dùng. Bí mật của các App như iOS hay Telegram nằm ở Vật lý lò xo (Spring Physics).
Framer Motion mặc định sử dụng type: "spring" cho các di chuyển không gian. Hãy điều chỉnh 2
thông số: stiffness (Độ cứng lò xo - tốc độ) và damping (Độ hãm - độ nảy).
<motion.div
initial={{ x: -200 }}
animate={{ x: 0 }}
transition={{
type: "spring",
stiffness: 260, // Lò xo càng cứng, bung càng nhanh
damping: 20 // Độ hãm càng nhỏ, nảy càng nhiều
}}
/>
Hiệu ứng Spring tạo ra sự "chân thực" vì nó bắt chước định luật vật lý của thế giới thực. Component khi dừng lại sẽ không dừng khựng lại cứng nhắc mà sẽ hơi nảy dội lại một chút.
Hover, Tap & Gestures
Framer Motion cung cấp các thuộc tính gắn trực tiếp để xử lý tương tác của người dùng cực nhàn rỗi (không
cần viết CSS :hover hay :active):
<motion.button
whileHover={{ scale: 1.05, backgroundColor: "#4f46e5" }}
whileTap={{ scale: 0.95 }}
className="px-6 py-2 bg-indigo-600 text-white rounded-lg"
>
Click Me!
</motion.button>
Thậm chí nó có cả drag để bạn làm tính năng kéo thả (Kéo tinder card, sắp xếp list, ...)
<motion.div
drag
dragConstraints={{ left: 0, right: 0, top: 0, bottom: 0 }} // Kéo thả nhưng tự dội về vị trí cũ (Snap)
/>
Phép Thuật Layout Animations
Đây là tính năng đáng tiền nhất của Framer Motion. Khi bạn thêm thuộc tính layout vào một
element, Framer Motion sẽ theo dõi sự thay đổi vị trí/kích thước của element đó trên DOM và tự động thực
hiện animation chuyển tiếp cực mượt.
Ví dụ: Khi bạn có một Grid danh sách bài viết, bạn filter (lọc) bài viết. Một số phần tử bị xóa đi, các
phần tử còn lại sẽ lập tức bị dồn lên trên. Nếu không có Animation, nó giật cục rất xấu. Chỉ cần thêm
layout:
<motion.div layout className="card">
<h3>Bài viết</h3>
</motion.div>
Tự dưng các bài viết sẽ "trượt" nhẹ nhàng vào chỗ trống mới thay vì nhảy cái rụp. Phép thuật!
AnimatePresence (Mount/Unmount)
Trong React, khi một component bị Unmount (Bị xóa khỏi DOM bằng
{isOpen && <Modal />}), nó biến mất lập tức. Làm sao để bắt nó chạy Animation mờ dần
rồi mới bị xóa đi?
Đó là lúc cần AnimatePresence. Bọc các phần tử cần Unmount bằng component này, và thêm thuộc
tính exit vào motion.div:
import { motion, AnimatePresence } from "framer-motion";
function App() {
const [isOpen, setIsOpen] = useState(true);
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }} // Sẽ chạy cái này trước khi Unmount
>
Tôi là Modal!
</motion.div>
)}
</AnimatePresence>
);
}
Scroll Animations (Hiệu ứng cuộn)
Muốn các phần tử tự động hiện ra khi người dùng cuộn (scroll) trang web xuống tới nó? Vô cùng đơn giản
với whileInView:
<motion.div
initial={{ opacity: 0, y: 50 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} // Chỉ chạy 1 lần, chạy khi cuộn cách 100px
>
Nội dung xuất hiện khi cuộn tới...
</motion.div>
Tối Ưu Hiệu Năng
Framer Motion rất nhanh vì nó thao tác trực tiếp bằng Web Animations API. Tuy nhiên, để đảm bảo 60fps trên điện thoại yếu, hãy ghi nhớ 1 nguyên tắc vàng:
Chỉ được phép Animate transform (translate, scale, rotate) và opacity. Những
thuộc tính này được xử lý bởi GPU phần cứng.
Tránh xa việc Animate width, height, top, left,
box-shadow vì nó bắt trình duyệt phải tính toán lại (Reflow/Repaint) Layout, gây giật lag
nặng.
Hy vọng qua bài viết siêu dài và đầy đủ này, bạn đã tự tin làm chủ được Framer Motion và hô biến mọi UI/UX trở thành những tác phẩm nghệ thuật mượt mà. Hẹn gặp lại!