HTML5: Tạo chuyển động với WindowAnimationTiming API

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Thay vì đặt timeout để gọi các phương thức vẽ lại hình ảnh, cách tốt nhất mà bạn nên sử dụng để tạo các hiệu ứng chuyển động trong canvas là dùng API WindowAnimationTiming, thông qua phương thức chính là requestAnimationFrame().

setTimeout và setInterval

Cách truyền thống mà bạn dùng để tạo các hiệu ứng đồ họa chuyển động với javascript là gọi liên tục công việc update và draw sau những khoảng thời gian xác định thông qua phương thức setInterval() hoặc setTimeout(). Mỗi lần gọi, một frame (khung hình) mới sẽ được tạo ra và vẽ đè lên màn hình cũ.

Khó khăn của phương pháp này là khó xác định được giá trị thời gian thích hợp dựa trên mỗi thiết bị được sử dụng (thông thường khoảng 60 fps – frames per second). Ngoài ra với những hiệu ứng phức tạp thì việc update/draw có thể diễn ra lâu hơn so với thời gian giữa hai lần gọi.

Cách tổng quát để giải quyết vấn đề trên là thực hiện tính toán dựa vào khoảng cách thời gian giữa lần gọi trước đó và hiện tại, sau đó xác định bỏ qua một vài bước draw hoặc thay đổi giá trị timeout cho phù hợp.

WindowAnimationTiming

Một tính năng mới ra đời cho phép bạn đơn giản hóa công việc này là API WindowAnimationTiming.

Đây là một interface bao gồm hai phương thức là requestAnimationFrame() và cancelAnimationFrame(). Việc xác định thời điểm cập nhật frame sẽ được tự động chọn giá trị thích hợp nhất.

interface WindowAnimationTiming {

long requestAnimationFrame(FrameRequestCallback callback);

void cancelAnimationFrame(long handle);

};

Window implements WindowAnimationTiming;

callback FrameRequestCallback = void (DOMTimeStamp time);

requestAnimationFrame: gửi request đến trình duyệt thực hiện một hành động update/draw frame mới thông qua tham số callback. Các request này sẽ được lưu trong đối tượng document với các cặp <handle, callback> và được thực hiện lần lượt. Giá trị handle là một số định danh được tạo ra và trả về sau khi gọi phương thức này.

cancelAnimationFrame: hủy một request được tạo ra requestAnimationFrame với tham số là handle của request.

Ngoài ra còn có thuộc tính window.mozAnimationStartTime (chỉ mới được hỗ trợ trên Firefox) chứa giá trị milisecond là khoảng cách từ mốc thời gian (1/1/1970) đến thời điểm bắt đầu của request cuối cùng được thực hiện. Giá trị này tương đương với giá trị trả về của Date.now() hoặc Date.getTime(), mốc thời gian là bao nhiêu không quan trọng nhưng nó giúp bạn biết được khoảng thời gian giữa hai lần thực hiện request.

Lợi ích và hiệu quả

Microsoft cho ta một ví dụ trực quan về hiệu quả của requestAnimationFrame() so với setTimeout() tại trang requestAnimationFrame API. Qua ví dụ này, ta thấy được số lần gọi callback (update) thực tế của setTimeout() lớn hơn so với dự tính. Ngoài ra các kết quả cho thấy hiệu suất của việc thực thi callback, CPU, mức tiêu tốn năng lượng và ảnh hướng đến các tác vụ nền của requestAnimationFrame() hơn hẳn so với setTimeout():

  setTimeout requestAnimationFrame
Expected callbacks  40543  40544
Actual callbacks  41584  40544
Callback Efficiency 59.70%  100.00%
Callback Efficiency Medium High
Power Consumption Medium Low
Background Interference High Low

 Một hiệu quả khác các animation sẽ tự động dừng lại nếu tab chứa nó không được hiển thị (khi bạn chuyển sang một tab khác của trình duyệt). Điều này giúp hạn chế được tài nguyên sử dụng một cách hợp lý.

Sử dụng

Kiểm tra trình duyệt hỗ trợ:

Tùy theo trình duyệt mà tên của phương thức này sẽ có những prefix khác nhau (vendor):

_reqAnimation = window.requestAnimationFrame ||

window.mozRequestAnimationFrame ||

window.webkitRequestAnimationFrame ||

window.msRequestAnimationFrame ||

window.oRequestAnimationFrame;

if(_reqAnimation)

  update();

else

  alert("Your browser doesn't support requestAnimationFrame.");

Ví dụ hoàn chỉnh:

<HTML>

<head>

<script>

const RADIUS = 20;

var cx = 100;

var cy = 100;

var speedX = 2;

var speedY = 2;

var _canvas;

var _context;

var _reqAnimation;

var _angle = 0;

function update(time) {

  cx += speedX;

  cy += speedY;

  if(cx<0 || cx > _canvas.width)

    speedX = -speedX;

  if(cy<0 || cy > _canvas.height)

    speedY = -speedY;

  // draw

  _context.clearRect(0, 0, _canvas.width, _canvas.height);

  _context.beginPath();

  _context.arc(cx, cy, RADIUS, 0, Math.PI*2, false);

  _context.closePath();

  _context.fill();

  _reqAnimation(update);

}

window.onload = function(){

  _canvas = document.getElementById("canvas");

  _context = _canvas.getContext("2d");

  _context.fillStyle = "red";

  cx = _canvas.width/2;

  cy = _canvas.height/2;

  _reqAnimation = window.requestAnimationFrame ||

  window.mozRequestAnimationFrame ||

  window.webkitRequestAnimationFrame ||

  window.msRequestAnimationFrame ||

  window.oRequestAnimationFrame ;

  if(_reqAnimation)

    update();

  else

    alert("Your browser doesn't support requestAnimationFrame.");

};

</script>

</head>

<body>

<canvas id="canvas" width="400px" height="300px" style="border: 1px solid"></canvas>

</body>

</HTML>

» Tiếp: Vẽ ảnh và thao tác với pixel
« Trước: Web Worker
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!