Hướng dẫn kỹ thuật FLIP animation với React

Trước đây mình đã có một bài về FLIP animation, các bạn có thể đọc lại ở đây để biết kỹ thuật này là gì và tại sao nó lại giúp tối ưu tốc độ.

Có thể tóm tắt kỹ thuật FLIP bằng hình minh họa bên dưới

Những gì trong ô màu tím, cần diễn ra trước sự kiện paint của trình duyệt.

Để đạt mục đích transform trước khi trình duyệt thực hiện paint, chúng ta sẽ sử dụng useLayoutEffect, những gì diễn ra ở đây sẽ xảy ra sau khi DOM cập nhập, trước khi paint

Với lần render đầu tiên, chúng ta cần cache lại vị trí cuối cùng của animation

useEffect(() => {
    // ...
    const rects = useRef(new Map()).current;
    
    const squares = document.querySelectorAll('.square');
    
    for (const square of squares) {
        rects.set(square.id, square.getBoundingClientRect());
    }
},[])

useLayoutEffect(() => {
    const squares = document.querySelectorAll('.square');
    
    for (const square of squares) {
        const cachedRect = rects.get(square.id);
        
        if (cachedRect) {
            const nextRect = square.getBoundingClientRect();
            
            // invert
            const translateX = cachedRect.x - nextRect.x;
            
            // cache position và size
            rects.set(square.id, nextRect);
            
            // play animation
            square.animate([
                { transform: `translateX(${translateX}px)` },
            	{ transform: `translateX(0px)` }
            ], 1000)
        }
    }
}, ids);

Tóm tắt những gì đã thực hiện bằng hình minh họa sau

Diagram. Cache the size and position, retrieve previous size and position from cache, get new size and position, calculate the difference, and play the animation.

Một vài lưu ý

  • Đảm bảo các tính toán không vượt quá 100ms, điều này nhằm đảm bảo user không cảm nhận có một độ trễ trên giao diện, có thể kiểm tra bằng DevTools img
  • Để tránh re-render không cần thiết, chúng ta ko được dùng useState, thay vào đó chúng ta phải dùng useRef một object cố định chúng ta có thể thay đổi giá trị mà không gây ra re-render
  • Không thực hiện đọc vị trí -> chạy animate ngay và luôn trên element đó, luôn thực hiện theo dạng đọc hàng loạt, sau đó animate một loạt
  • Nếu không muốn tự viết, có thể tham khảo 2 thư viện dùng với React: react-flip-toolkitreact-easy-flip.

Everything You Need to Know About FLIP Animations in React