Tạo animation với HTML5 canvas

  • HTML5
  • 02 tháng 10 2014
  • bởi Văn Khương
  • 0 Comments

Tạo animation với html5 canvasrequestAnimationFrame là hàm sẽ được sử dung thay cho những hàm trước đây ta thường dùng là setTimeout hay setInterval, trước tiên ta sẽ sơ lược qua về các cách thức tạo animation trong javaScript. setInterval sẽ thực hiện lặp đi lặp lại mãi mãi với mỗi thời gian nhất định cho tới khi ta gọi clearInterval để dừng nó. SetTimeout sẽ tạo delay trước khi thực hiện một chức năng nào đó kết hợp dùng đệ quy chúng ta cũng có lại cái lặp đi lặp lại mãi mãi với mỗi thời gian nhất định.

Sự mượt mà của của ứng dụng animation của bạn dựa vào tỉ lệ khung hình trên mỗi giây, thông thường các màn hình của chúng ta có tỷ lệ làm tươi là 60Hz nên chúng ta có tỷ lệ khung hình nhanh nhất là 60fps, điều này có nghĩa trong javaScipt độ mượt tối đa có thể thực hiện được khi gọi setInterval với một khoảng thời gian là khoảng 17ms (1000ms / 60(fps) = 16.7ms).

Có vài lý do mà hiện người ta không còn hài lòng với setTimeout và setInterval, chẳng hạn như nó làm CPU của bạn vất cả hơn và khi trang đang được ẩn trong tab thì CPU vẫn phải hoạt động cho ứng dụng của bạn, với trình duyệt Chrome thì nó đã được chỉnh lại setTimeout và setInterval tới 1fps trong các tab ẩn, còn các trình duyệt khác thì không biết thế nào. Để có những animation tốt hơn Mozilla đã đề xuất sử dụng requestAnimationFrame và hiện nó đã trở thành API của trình duyệt và đang có mặt trên hầu hết các trình duyệt hiện đại, IE được hổ trợ từ phiên bản 10, xem thêm ai hổ trợ ở caniuse. Với requestAnimationFrame chúng ta không còn phải khai báo thời gian cho mỗi khung hình nữa mà đã được tối ưu hóa dựa trên tỷ lệ làm tươi của màn hình, nó tận dụng được sức mạnh của GPU giúp animation trở nên mượt mà hơn hẳn.

Vì mỗi trình duyệt sẽ có một tiếp đầu ngữ riêng nên cần phải kiểm tra trước khi dùng:

var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] ||
    window[vendors[x]+'CancelRequestAnimationFrame'];
}

requestAnimationFrame sẽ trả về một ID và cứ tăng dần khi requestAnimationFrame được gọi lại, để hủy requestAnimationFrame chúng ta cần tới cancelAnimationFrame(ID). Hãy xem xét 1 ví dụ sau để biết cách xây dựng một hàm tạo animate cho ứng dụng. Trước tiên ta có một hình chữ nhật trong canvas với các thông số được lưu trữ trong một biến/đối tượng, requestAnimationFrame sẽ vẽ lại nó liên tục.

// Các thông số của hình chữ nhật
var square = {
    'x': 50,
    'y': 50,
    'width': 100,
    'height': 100
};

var render = function() {
    // xóa toàn bộ vùng vanvas
    context.clearRect(0, 0, canvas.width, canvas.height);
    // vẽ hình chữ nhật
    context.fillRect(square.x, square.y, square.width, square.height);
    // vẽ lại liên tục
    requestAnimationFrame(render);
};
render();

Tiếp theo sẽ là một hàm với một vài tham số cần thiết để hình chữ nhật thay đổi theo một thời gian nào đó

var animate = function(prop, val, duration) {
  // Thời gian bắt đầu
  var start = new Date().getTime();
  // Thời điểm kết thúc
  var end = start + duration;
  // Trạng thái ban đầu của hình chữ nhật
  var current = square[prop];
  // Khoảng thay đổi của hình chữ nhật
  var distance = val - current;

  var step = function() {
    // Thời gian hiện tại
    var timestamp = new Date().getTime();
    var progress = Math.min((duration - (end - timestamp)) / duration, 1);
    // Cập nhật trạng thái cho hình chữ nhật
    square[prop] = current + (distance * progress);

    // Tới 1 thì dừng lặp step để kết thúc animation
    if (progress < 1){
      requestAnimationFrame(step);
    }
  };
  return step();
};
animate('x', 0, 1000);

Trong hàm step() ở trên có lẽ cần phải nói thêm về cách tính và ý nghĩa của biến progress. Trong hàm Math.min giá trị lớn nhất có thể trả về là 1, ở tham số đầu tiên của Math.min vì biến timestamp sẽ có giá trị tăng dần theo thời gian, nên kết quả của (end - timestamp) là khoảng thời gian còn lại và (duration - (end - timestamp) là khoảng thời gian đã trôi qua, kết quả ta được biến progress sẽ nhận giá trị từ 0 tới 1 lợi dụng điều này chúng ta cập nhật được trạng thái của hình chữ nhật. Xem ví dụ nguồn tại đây

Tham khảo: creativejs.comcodular.com

  • Chia sẻ
comments powered by Disqus