Explore CSS Transitions, the mathematics of timing functions (Cubic-bezier curves), and how to optimize animation performance using compositor-only rendering properties.

This lesson is currently only available in Vietnamese. Please switch the language toggle in the menu to Vietnamese to read the full guide and take the interactive quiz.

Trong thiết kế giao diện hiện đại (UI/UX), sự chuyển tiếp mượt mà giữa các trạng thái (như khi rê chuột vào nút bấm, mở rộng menu, ẩn hiện modal) chính là yếu tố phân biệt một sản phẩm chuyên nghiệp với một giao diện chắp vá thô cứng.

CSS cung cấp thuộc tính transition để giải quyết bài toán này một cách cực kỳ đơn giản. Tuy nhiên, đằng sau sự đơn giản đó là một loạt các khái niệm toán học về đường cong gia tốc (Easing Curves) và các cơ chế tối ưu hóa render phần cứng (GPU Compositing) mà mọi kỹ sư frontend cần phải làm chủ để đạt hiệu năng mượt mà 60fps.

1. Thuộc tính CSS Transition & Cách hoạt động

Cơ chế hoạt động của Transition dựa trên việc lắng nghe sự thay đổi của các thuộc tính CSS (thường được kích hoạt bởi pseudo-class như :hover, :focus, hoặc qua việc JavaScript gán class mới) và tự động tính toán bù suy hao (interpolate) để thay đổi giá trị đó tăng/giảm dần theo thời gian thay vì nhảy số lập tức.

Một khai báo đầy đủ bao gồm 4 thuộc tính cấu thành:

  • transition-property: Chỉ định tên thuộc tính muốn tạo hiệu ứng chuyển tiếp (ví dụ: background-color, transform).
  • transition-duration: Thời gian diễn ra hiệu ứng chuyển tiếp, nhận đơn vị giây (s) hoặc mili-giây (ms).
  • transition-timing-function: Quy định đường cong gia tốc (tốc độ chuyển động nhanh hay chậm theo thời gian).
  • transition-delay: Khoảng thời gian chờ đợi trước khi bắt đầu thực hiện hiệu ứng chuyển tiếp.
🕳️ Cạm bẫy hiệu năng: Lạm dụng transition: all
Rất nhiều lập trình viên thường viết tắt transition: all 0.3s. Điều này cực kỳ có hại cho hiệu năng. Trình duyệt sẽ buộc phải theo dõi sự thay đổi của tất cả mọi thuộc tính CSS của phần tử đó. Nếu có các thuộc tính kích hoạt Reflow (như margin, padding), trang web sẽ lập tức bị giật lag nghiêm trọng, đặc biệt trên thiết bị di động cấu hình yếu. Hãy luôn chỉ định chính xác thuộc tính cần chuyển tiếp (như transition: transform 0.3s, opacity 0.3s).

2. Bản chất toán học của Easing Curves: Cubic Bezier

Đường cong gia tốc (Easing Curve) định nghĩa cách giá trị CSS thay đổi dọc theo dòng thời gian của hiệu ứng. Nó được mô tả toán học bằng một đường cong Bézier bậc 3 (Cubic Bézier).

Đường cong Cubic Bézier được định nghĩa bởi 4 điểm kiểm soát: $P_0, P_1, P_2, P_3$. Trong đó, $P_0 (0,0)$ là thời điểm bắt đầu và $P_3 (1,1)$ là thời điểm kết thúc. Chúng cố định. Hai điểm còn lại, $P_1 (x_1, y_1)$ và $P_2 (x_2, y_2)$, là các điểm neo điều hướng lực kéo giúp bạn tự do tùy biến đường cong.

Hàm toán học mô tả đường cong Bézier bậc 3 theo tham số $t \in [0, 1]$:

\[B(t) = (1-t)^3 P_0 + 3(1-t)^2 t P_1 + 3(1-t) t^2 P_2 + t^3 P_3\]

Trong CSS, chúng ta khai báo đường cong này qua cú pháp: cubic-bezier(x1, y1, x2, y2).

ℹ️ Các hàm gia tốc tích hợp sẵn (Presets)
CSS cung cấp một số từ khóa rút gọn cho các đường cong tiêu chuẩn:
  • linear: Vận tốc không đổi suốt quá trình. Tương đương cubic-bezier(0, 0, 1, 1).
  • ease: Bắt đầu chậm, tăng tốc nhanh ở giữa và giảm tốc chậm ở cuối. Tương đương cubic-bezier(0.25, 0.1, 0.25, 1).
  • ease-in: Bắt đầu chậm và tăng tốc dần đến cuối. Tương đương cubic-bezier(0.42, 0, 1, 1).
  • ease-out: Bắt đầu nhanh và giảm tốc chậm dần về cuối. Tương đương cubic-bezier(0, 0, 0.58, 1).
  • ease-in-out: Bắt đầu chậm, tăng tốc ở giữa và giảm tốc dần ở cuối. Tương đương cubic-bezier(0.42, 0, 0.58, 1).

3. Hiệu năng dựng hình: CPU Reflow vs GPU Compositing

Khi viết transition, lựa chọn thuộc tính nào để thay đổi sẽ quyết định trực tiếp hiệu năng mượt mà của giao diện. Trình duyệt dựng hình (rendering pipeline) trải qua 3 giai đoạn chính ảnh hưởng bởi CSS:

  1. Layout (Reflow): Tính toán lại hình học, kích thước và vị trí của các khối. Tác vụ này cực kỳ tốn CPU vì thay đổi một phần tử có thể kéo theo tính toán lại toàn bộ trang (Layout Tree Rebuild).
  2. Paint (Repaint): Tô màu pixel, vẽ chữ, viền, đổ bóng của các hộp. Tốn tài nguyên vì phải vẽ lại giao diện.
  3. Composite (Dàn dựng): Gửi các lớp ảnh (Layers) đã vẽ lên GPU để sắp lớp và hiển thị. Cực kỳ nhanh vì GPU được thiết kế cho việc dịch chuyển, biến đổi hình học phẳng siêu tốc.
🔬 Đào sâu: Tại sao transform mượt mà hơn left/top?
Khi bạn thay đổi left hoặc top, trình duyệt bắt buộc phải chạy lại cả 3 giai đoạn Layout -> Paint -> Composite ở mỗi khung hình (frame). Điều này làm CPU quá tải và gây ra hiện tượng giật lag.
Ngược lại, khi sử dụng transform: translateX(), trình duyệt sẽ đưa phần tử đó lên một lớp đồ họa riêng (Graphics Layer). Khi dịch chuyển, trình duyệt bỏ qua hoàn toàn bước Layout và Paint, GPU chỉ việc thao tác di chuyển lớp ảnh đó ở giai đoạn Composite Only. Tốc độ render đạt tối đa 60fps hoặc 120fps.

Sân chơi tương tác: Easing & Performance Lab

Dưới đây là công cụ so sánh trực quan hiệu năng dựng hình. Bạn có thể thay đổi thời gian, đường cong gia tốc để quan sát đường cong Bézier được vẽ trên Canvas, đồng thời nhấn "Chạy thử hiệu ứng" để xem sự khác biệt giữa hai kỹ thuật dịch chuyển:

🎨 Sân chơi tương tác: Easing & Performance Lab

Thiết lập cấu hình

Đường cong Bézier
Cách 1: Di chuyển bằng left (Layout-heavy) CPU Reflow -> Paint
Left
Cách 2: Di chuyển bằng transform (Compositor) Composite Only (GPU)
GPU
transition.html
<div class="container">
  <!-- Cách 1: Kém hiệu năng do gây Reflow liên tục -->
  <div class="box box-left">Left</div>

  <!-- Cách 2: Tối ưu, mượt mà nhờ chạy trên GPU -->
  <div class="box box-transform">GPU</div>
</div>
transition.css

4. Kỹ thuật nâng cao: Bouncing Effect & Hardware Acceleration

Bằng cách đẩy điểm tọa độ dọc của $P_1$ hoặc $P_2$ ra ngoài khoảng giới hạn $[0, 1]$ (ví dụ: gán $y_1$ hoặc $y_2$ lớn hơn 1 hoặc nhỏ hơn 0), bạn có thể tạo nên các hiệu ứng nảy (Bouncing/Elastic) cực kỳ sống động mà không cần tới JavaScript hay Keyframe phức tạp.

bounce.css
.button-pop {
  transition: transform 0.4s cubic-bezier(0.68, -0.6, 0.32, 1.6);
}
.button-pop:hover {
  transform: scale(1.15); /* Phần tử sẽ hơi co rúm lại trước khi phình to nảy ra ngoài */
}

Để ép buộc trình duyệt gán một phần tử vào một Graphics Layer riêng ngay từ đầu nhằm tăng tốc phần cứng, bạn có thể sử dụng thuộc tính will-change:

acceleration.css
.gpu-accelerated-layer {
  will-change: transform, opacity; /* Báo trước cho GPU chuẩn bị tài nguyên */
}

Trắc nghiệm ôn tập

Câu 1: Thuộc tính nào sau đây khi thay đổi trạng thái sẽ có hiệu năng cao nhất (chỉ kích hoạt Composite Only)?

Trắc nghiệm ôn tập

Câu 2: Trong hàm cubic-bezier(x1, y1, x2, y2), giá trị nào sau đây bị giới hạn bắt buộc từ 0 đến 1?

Related Articles

Bài viết liên quan trong series

Lesson 5: Keyframes & Animation — Multi-state motion control Bài 5: Keyframes & Hoạt Ảnh Phức Tạp — Kiểm Soát Chuyển Động Đa Trạng Thái Lesson 3: CSS Grid — Designing Complex Layouts Bài 3: CSS Grid 2 Chiều — Thiết Kế Bố Cục Phức Tạp Back to CSS Course Overview Quay lại Lộ trình Series CSS