13

Xin chào, tôi có một ứng dụng web có thể chạy trên cả Trình duyệt trên Điện thoại thông minh và Máy tính để bàn. Trong khi tôi đang mong đợi để có được một số hành vi tò mò trên các thiết bị nhỏ như Iphone, tôi đã khá tự tin rằng nó sẽ chạy tốt trên một Android Galaxy Tab là thiết bị Android mà tôi có thể chạy thử nghiệm với tại thời điểm này.Sự cố về hiệu suất với Canvas HTML5 trong một số trình duyệt di động.

Bây giờ tôi đã cài đặt một loạt các trình duyệt trên Galaxy Tab để kiểm tra điều này với:

  • Trình duyệt Android Native
  • Chrome dành cho Android
  • Firefox dành cho Android

Trên Máy tính để bàn tôi đã sử dụng

  • Firefox
  • Google Chrome

và cuối cùng tôi có một Iphone để kiểm tra.

Trang web sử dụng canvas HTML5 cho pixel và sprite dựa trên bản vẽ không có chuyển đổi, bộ lọc hoặc hiệu ứng ưa thích, chủ yếu là đường dẫn đơn giản và đa giác. Tôi nghe các sự kiện liên lạc và sử dụng requestAnimationFrame để vẽ lại đúng cách.

Nhìn chung, ứng dụng chạy tốt trên Trình duyệt trên máy tính để bàn, ứng dụng này cũng chạy rất tốt trên iOS Safari (iPhone) và Firefox-trên-Android. Tuy nhiên, Androids Native Browser đang gây rắc rối cho tôi. Tôi đã thiết lập nó để màn hình đỏ bừng khi javascript không đáp ứng, và nó hầu như luôn luôn nhấp nháy khi chạm vào màn hình.

Vì vậy, tôi tự hỏi liệu có bất kỳ sự cố nào đã biết với Android Native App và HTML5 hay không. Do không tồn tại tên của các trình duyệt bản địa của nó khá khó để google thông tin về việc này. Bất kỳ ý tưởng nào cho tôi nơi tôi có thể nhận thêm thông tin? Bất kỳ ý tưởng nào có thể gây ra sự chậm trễ của trình duyệt Android gốc?

Có một vài ý tưởng về vấn đề này:

  • iOS không hỗ trợ requestAnimationFrame, do đó tôi đã thay thế nó bằng một sự thay thế dựa trên thời gian chờ. Nếu tôi sử dụng thay thế đó trên trình duyệt gốc của Android, sự cố vẫn tiếp diễn.

  • Tôi sử dụng AJAX (google clojure xhrio) khá thường xuyên để truy xuất dữ liệu từ máy chủ. Có thể nào là các cuộc gọi lại truy xuất dữ liệu đang chặn đường dẫn sự kiện của tôi?

  • Thông báo trên bảng điều khiển nhật ký (console.log) có được biết là làm chậm các ứng dụng không? Họ có thể kích hoạt trình duyệt chạy lại qua cây DOM hay bất kỳ thứ gì có liên quan không?

+2

Tôi không thể trả lời câu hỏi thực tế của bạn, nhưng việc sử dụng bảng điều khiển có thể tiêu tốn lượng bộ nhớ đáng kể tùy thuộc vào cách bạn đang sử dụng. Đặc biệt là nếu bạn đang đăng nhập các đối tượng lớn hoặc đăng nhập rất thường xuyên. – idbehold

Trả lời

42

Tôi đã thực hiện rất nhiều thử nghiệm với canvas trong nhiều trình duyệt.Một số vấn đề hiệu suất mà tôi nhận thấy:

Thứ nhất, về phỏng đoán của bạn:

  • Khi requestAnimationFrame được hỗ trợ bởi một trình duyệt, các công cụ vẽ và các ứng dụng riêng của mình là phản ứng nhanh hơn. Sử dụng setTimeout hoặc setInterval làm dự phòng luôn có thể nhưng bạn cần phải cẩn thận về thời gian. Điều này robust polyfill có thể giúp một chút, nhưng không có gì so với nativeAnimationFrame bản địa.

  • Nếu console.log được gọi là mọi khung hình (hoặc gần như), có hiệu suất giảm xuống. Vì Trình duyệt Android gốc không có một đối tượng điều khiển, mỗi khi nó được gọi sẽ tạo ra một lỗi cũng góp phần làm chậm ứng dụng của bạn. Bạn có thể làm điều này:

    if(typeof console === "undefined"){ console = {}; }

  • Đối với cường độ cao các ứng dụng thời gian thực web sockets là nhanh hơn sau đó yêu cầu http. Rất tiếc, tính năng này không được hỗ trợ bởi trình duyệt Android gốc cũ. Nếu không thể sử dụng ổ cắm web, bạn nên giảm thiểu các yêu cầu http.

Lưu ý: Chrome hỗ trợ android nhất của HTML5 tính năng trích dẫn ở đây, bao gồm requestAnimationFramewebsockets. thông tin

thêm:

  • Vẽ văn bản sử dụng bối cảnh 2d fillText nó quá đắt tiền, nhưng trong một số trình duyệt này thậm chí còn tồi tệ hơn. Tạo trước văn bản của bạn trong canvas khác hoặc sử dụng phông chữ bitmap. (Trong trình duyệt Android gốc, sau khi thay thế filltext bản vẽ cho các công cụ trước khi kết xuất, hiệu suất đã tăng từ 10-15 FPS lên 30-45 FPS trong một số trò chơi tôi đã thực hiện).

  • Tránh chia tỷ lệ và xoay ngữ cảnh vì chúng cũng làm giảm hiệu suất. Nếu bạn cần chia tỷ lệ hoặc xoay một sprite chỉ một lần, hãy sử dụng kết xuất trước.

  • Bạn cần giảm thiểu bản vẽ thời gian thực. Hiển thị trước nội dung của bạn bất cứ khi nào bạn có thể. Chỉ vẽ lại nội dung đã thay đổi và cần được cập nhật.

  • Cố gắng viết memory efficient và mã thân thiện với bộ sưu tập rác.

Có rất nhiều việc cần làm. Tôi chỉ trích dẫn một vài.

MIPO: thực hiện một số kiểm tra căng thẳng cho các tính năng bạn không chắc chắn nếu chúng là kẻ giết người thực hiện và nắm bắt kết quả điểm chuẩn.

Trong các ứng dụng dành cho thiết bị di động, các ứng dụng thời gian thực đặc biệt, tất cả các tối ưu hóa đều được chào đón nếu không chỉ là tối ưu hóa quá mức hoặc tăng thêm bộ nhớ.

Để biết thêm thông tin theo các liên kết dưới đây:

Cũng tìm kiếm hiệu quả trong Posts & Tutorials.

EDIT
jsfiddle code snippet Điều này cho thấy một số nội dung được đề cập trong câu trả lời này và cung cấp một fps truy cập thô để benchmark. Chỉnh sửa fiddle này của chính mình và kiểm tra xem nó ra.

+4

câu trả lời thực sự hữu ích và sâu sắc. Rất nhiều đánh giá cao;) – wirrbel

+0

Khi sử dụng các els canvas bổ sung cho kết xuất trước, hãy đảm bảo rằng nội dung của chúng không bị cắt bớt, ví dụ: khi xoay văn bản. trong trường hợp đó, bạn có thể dịch nội dung trên khung vẽ trước và sau đó di chuyển nó trở lại khi sử dụng canvas bằng drawImage: 'preRenderCtx.translate (100, 100); preRenderCtx.rotate (xoay vòng); preRenderCtx.fillText (char, 0, 0); context.drawImage (preRenderCanvas, x-100, y-100); ' –

0

Tùy thuộc vào những gì bạn đang vẽ, chiến lược cải thiện hiệu suất phổ biến nhất với vải Html5 là sử dụng các lớp (nghĩa là nhiều canvas) và chỉ cập nhật các lớp cần vẽ lại, thay vì vẽ lại toàn bộ khung . Bạn có thể cuộn một cái gì đó như thế này, hoặc bạn có thể sử dụng một cái gì đó giống như đó là một khung vải Html5 nhẹ cho phép những thứ ngoại vi như phát hiện hit, phân lớp, bộ nhớ đệm, hỗ trợ tỷ lệ pixel, tải xuống, v.v. Bạn sẽ làm như sau:

var wrapper = new Concrete.Wrapper({ 
    width: 500, 
    height: 300, 
    container: el 
}); 

var layer1 = new Concrete.Layer(); 
var layer2 = new Concrete.Layer(); 

wrapper.add(layer1).add(layer2); 

// something happens which requires you to redraw layer2, but not layer1... 
layer2.sceneCanvas.context.fillStyle = 'red'; 
layer2.sceneCanvas.context.fillRect(0, 0, 200, 100); 
Các vấn đề liên quan