Hướng dẫn requestAnimationFrame ()

Tìm hiểu API để thực hiện hoạt ảnh và lên lịch sự kiện theo cách có thể dự đoán được

requestAnimationFrame()là một API trình duyệt tương đối gần đây. Nó cung cấp một cách dễ đoán hơn để tham gia vào chu trình kết xuất của trình duyệt.

Nó hiện được hỗ trợ bởi tất cả các trình duyệt hiện đại (và IE 10+)

Browser support

Nó không phải là một API dành riêng cho hoạt ảnh, nhưng đó là nơi nó được sử dụng nhiều nhất.

JavaScript có một vòng lặp sự kiện. Nó liên tục chạy để thực thi JavaScript.

Trước đây, các hoạt ảnh được thực hiện bằng cách sử dụngsetTimeout()hoặc làsetInterval(). Bạn thực hiện một chút hoạt ảnh và bạn gọisetTimeout()để lặp lại mã này sau vài mili giây kể từ bây giờ:

const performAnimation = () => {
  //...
  setTimeout(performAnimation, 1000 / 60)
}
setTimeout(performAnimation, 1000 / 60)

hoặc là

const performAnimation = () => {
  //...
}
setInterval(performAnimation, 1000 / 60)

Bạn có thể dừng hoạt ảnh bằng cách lấy tham chiếu thời gian chờ hoặc khoảng thời gian và xóa nó:

let timer

const performAnimation = () => { //… timer = setTimeout(performAnimation, 1000 / 60) }

timer = setTimeout(performAnimation, 1000 / 60)

//… clearTimeout(timer)

Các1000 / 60khoảng thời gian giữaperformAnimation()các cuộc gọi được xác định bởi tốc độ làm mới màn hình, trong hầu hết các trường hợp là 60 Hz (60 lần sửa lại mỗi giây), vì việc sơn lại sẽ vô ích nếu màn hình không thể hiển thị do các hạn chế của nó. Nó dẫn đến ~ 16,6ms thời gian mà chúng tôi có thể sử dụng để hiển thị mọi khung hình.

Vấn đề với cách tiếp cận này là mặc dù chúng tôi chỉ định độ chính xác này một cách chính xác, trình duyệt có thể đang bận thực hiện các hoạt động khác và các lệnh gọi setTimeout của chúng tôi có thể không đến kịp để sơn lại và nó sẽ bị trì hoãn sang chu kỳ tiếp theo.

Điều này thật tệ vì chúng ta bị mất một khung hình và trong lần tiếp theo hoạt ảnh được thực hiện 2 lần, khiến mắt chú ý đến hoạt ảnh lộn xộn.

Kiểm tra ví dụ này trên Glitch of anhoạt ảnh được tạo bằng cách sử dụng setTimeout ().

requestAnimationFramelà cách tiêu chuẩn để thực hiện hoạt ảnh và nó hoạt động theo một cách rất khác sự kiện mặc dù mã trông rất giống với mã setTimeout / setInterval:

let request

const performAnimation = () => { request = requestAnimationFrame(performAnimation) //animate something }

requestAnimationFrame(performAnimation)

//… cancelAnimationFrame(request) //stop the animation

Ví dụ này trên Glitch of anhoạt ảnh được tạo bằng requestAnimationFrame ()cho thấy làm thế nào.

Tối ưu hóa

requestAnimationFrame()vì phần giới thiệu của nó rất thân thiện với CPU, khiến các hoạt ảnh dừng lại nếu cửa sổ hoặc tab hiện tại không hiển thị.

Tại thời điểm requestAnimationFrame () được giới thiệu, setTimeout / setInterval đã chạy ngay cả khi tab bị ẩn, nhưng hiện tại vì cách tiếp cận này đã được chứng minh là thành công cũng như tiết kiệm pin, các trình duyệt cũng triển khai điều chỉnh cho các sự kiện đó, cho phép tối đa 1 lần thực thi mỗi giây .

Sử dụngrequestAnimationFrametrình duyệt có thể tối ưu hóa hơn nữa việc tiêu thụ tài nguyên và làm cho hình ảnh động mượt mà hơn.

Ví dụ về dòng thời gian

Đây là dòng thời gian hoàn hảo nếu bạn sử dụng setTimeout hoặc setInterval:

Perfect timeline

bạn có một tập hợp các sự kiện sơn (xanh lá cây) và hiển thị (tím) và mã của bạn nằm trong hộp màu vàng - nhân tiện, đây cũng là những màu được sử dụng trong Trình duyệt DevTools để đại diện cho dòng thời gian:

The devtools timeline

Hình minh họa cho thấy trường hợp hoàn hảo. Bạn có thể vẽ và kết xuất cứ sau 60 mili giây, và hoạt ảnh của bạn diễn ra ở giữa, hoàn toàn bình thường.

Nếu bạn sử dụng cuộc gọi tần suất cao hơn cho chức năng hoạt ảnh của mình:

Too frequent timeline

Lưu ý rằng trong mỗi khung hình, chúng ta gọi 4 bước hoạt ảnh, trước khi bất kỳ kết xuất nào xảy ra, và điều này sẽ làm cho hoạt ảnh cảm thấy rất lộn xộn.

Điều gì sẽ xảy ra nếu setTimeout không thể chạy đúng giờ do mã khác chặn vòng lặp sự kiện? Chúng tôi kết thúc với một khung hình bị bỏ lỡ:

Missed frame

Điều gì sẽ xảy ra nếu một bước hoạt ảnh diễn ra nhiều hơn một chút so với dự đoán của bạn?

Delay in the timeline

các sự kiện kết xuất và sơn cũng sẽ bị trì hoãn.

Đây là cáchrequestAnimationFrame()hoạt động trực quan:

Delay in the timeline

tất cả các mã hoạt ảnh chạytrướccác sự kiện kết xuất và vẽ tranh. Điều này làm cho mã dễ dự đoán hơn và có rất nhiều thời gian để thực hiện hoạt ảnh mà không phải lo lắng về việc vượt qua thời gian 16ms mà chúng ta có.

Kiểm travideo tuyệt vời này của Jake Archibaldvề chủ đề.

Tải xuống miễn phí của tôiSổ tay dành cho Người mới bắt đầu JavaScript


Các hướng dẫn khác về trình duyệt: