Chúng tôi đang làm việc với canvas HTML5, hiển thị nhiều hình ảnh cùng một lúc.Tại sao chrome đấu tranh để hiển thị nhiều hình ảnh trên canvas khi các trình duyệt khác không hiển thị?
Điều này hoạt động khá tốt nhưng gần đây chúng tôi gặp sự cố với Chrome.
Khi vẽ hình ảnh trên canvas, bạn dường như đạt đến một điểm nhất định nơi hiệu suất giảm rất nhanh.
Nó không phải là một hiệu ứng chậm, có vẻ như bạn đi từ 60fps đến 2-4fps.
Dưới đây là một số mã sinh sản:
// Helpers
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Math/random
function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }
// http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function (callback) { window.setTimeout(callback, 1000/60); }; })();
// https://github.com/mrdoob/stats.js
var Stats = function() { var e = Date.now(), t = e; var n = 0, r = Infinity, i = 0; var s = 0, o = Infinity, u = 0; var a = 0, f = 0; var l = document.createElement("div"); l.id = "stats"; l.addEventListener("mousedown", function (e) { e.preventDefault(); y(++f % 2) }, false); l.style.cssText = "width:80px;opacity:0.9;cursor:pointer"; var c = document.createElement("div"); c.id = "fps"; c.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#002"; l.appendChild(c); var h = document.createElement("div"); h.id = "fpsText"; h.style.cssText = "color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; h.innerHTML = "FPS"; c.appendChild(h); var p = document.createElement("div"); p.id = "fpsGraph"; p.style.cssText = "position:relative;width:74px;height:30px;background-color:#0ff"; c.appendChild(p); while (p.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#113"; p.appendChild(d) } var v = document.createElement("div"); v.id = "ms"; v.style.cssText = "padding:0 0 3px 3px;text-align:left;background-color:#020;display:none"; l.appendChild(v); var m = document.createElement("div"); m.id = "msText"; m.style.cssText = "color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; m.innerHTML = "MS"; v.appendChild(m); var g = document.createElement("div"); g.id = "msGraph"; g.style.cssText = "position:relative;width:74px;height:30px;background-color:#0f0"; v.appendChild(g); while (g.children.length < 74) { var d = document.createElement("span"); d.style.cssText = "width:1px;height:30px;float:left;background-color:#131"; g.appendChild(d) } var y = function (e) { f = e; switch (f) { case 0: c.style.display = "block"; v.style.display = "none"; break; case 1: c.style.display = "none"; v.style.display = "block"; break } }; var b = function (e, t) { var n = e.appendChild(e.firstChild); n.style.height = t + "px" }; return { REVISION: 11, domElement: l, setMode: y, begin: function() { e = Date.now() }, end: function() { var f = Date.now(); n = f - e; r = Math.min(r, n); i = Math.max(i, n); m.textContent = n + " MS (" + r + "-" + i + ")"; b(g, Math.min(30, 30 - n/200 * 30)); a++; if (f > t + 1e3) { s = Math.round(a * 1e3/(f - t)); o = Math.min(o, s); u = Math.max(u, s); h.textContent = s + " FPS (" + o + "-" + u + ")"; b(p, Math.min(30, 30 - s/100 * 30)); t = f; a = 0 } return f }, update: function() { e = this.end() } } }
// Firefox events suck
function getOffsetXY(eventArgs) { return { X: eventArgs.offsetX == undefined ? eventArgs.layerX : eventArgs.offsetX, Y: eventArgs.offsetY == undefined ? eventArgs.layerY : eventArgs.offsetY }; }
function getWheelDelta(eventArgs) { if (!eventArgs) eventArgs = event; var w = eventArgs.wheelDelta; var d = eventArgs.detail; if (d) { if (w) { return w/d/40 * d > 0 ? 1 : -1; } else { return -d/3; } } else { return w/120; } }
// Reproduction Code
var stats = new Stats();
document.body.appendChild(stats.domElement);
var masterCanvas = document.getElementById('canvas');
var masterContext = masterCanvas.getContext('2d');
var viewOffsetX = 0;
var viewOffsetY = 0;
var viewScaleFactor = 1;
var viewMinScaleFactor = 0.1;
var viewMaxScaleFactor = 10;
var mouseWheelSensitivity = 10; //Fudge Factor
var isMouseDown = false;
var lastMouseCoords = null;
var imageDimensionPixelCount = 25;
var paddingPixelCount = 2;
var canvasDimensionImageCount = 50;
var totalImageCount = Math.pow(canvasDimensionImageCount, 2);
var images = null;
function init() {
images = createLocalImages(totalImageCount, imageDimensionPixelCount);
initInteraction();
renderLoop();
}
function initInteraction() {
var handleMouseDown = function (eventArgs) {
isMouseDown = true;
var offsetXY = getOffsetXY(eventArgs);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
};
var handleMouseUp = function (eventArgs) {
isMouseDown = false;
lastMouseCoords = null;
}
var handleMouseMove = function (eventArgs) {
if (isMouseDown) {
var offsetXY = getOffsetXY(eventArgs);
var panX = offsetXY.X - lastMouseCoords[0];
var panY = offsetXY.Y - lastMouseCoords[1];
pan(panX, panY);
lastMouseCoords = [
offsetXY.X,
offsetXY.Y
];
}
};
var handleMouseWheel = function (eventArgs) {
var mouseX = eventArgs.pageX - masterCanvas.offsetLeft;
var mouseY = eventArgs.pageY - masterCanvas.offsetTop;
var zoom = 1 + (getWheelDelta(eventArgs)/mouseWheelSensitivity);
zoomAboutPoint(mouseX, mouseY, zoom);
if (eventArgs.preventDefault !== undefined) {
eventArgs.preventDefault();
} else {
return false;
}
}
masterCanvas.addEventListener("mousedown", handleMouseDown, false);
masterCanvas.addEventListener("mouseup", handleMouseUp, false);
masterCanvas.addEventListener("mousemove", handleMouseMove, false);
masterCanvas.addEventListener("mousewheel", handleMouseWheel, false);
masterCanvas.addEventListener("DOMMouseScroll", handleMouseWheel, false);
}
function pan(panX, panY) {
masterContext.translate(panX/viewScaleFactor, panY/viewScaleFactor);
viewOffsetX -= panX/viewScaleFactor;
viewOffsetY -= panY/viewScaleFactor;
}
function zoomAboutPoint(zoomX, zoomY, zoomFactor) {
var newCanvasScale = viewScaleFactor * zoomFactor;
if (newCanvasScale < viewMinScaleFactor) {
zoomFactor = viewMinScaleFactor/viewScaleFactor;
} else if (newCanvasScale > viewMaxScaleFactor) {
zoomFactor = viewMaxScaleFactor/viewScaleFactor;
}
masterContext.translate(viewOffsetX, viewOffsetY);
masterContext.scale(zoomFactor, zoomFactor);
viewOffsetX = ((zoomX/viewScaleFactor) + viewOffsetX) - (zoomX/(viewScaleFactor * zoomFactor));
viewOffsetY = ((zoomY/viewScaleFactor) + viewOffsetY) - (zoomY/(viewScaleFactor * zoomFactor));
viewScaleFactor *= zoomFactor;
masterContext.translate(-viewOffsetX, -viewOffsetY);
}
function renderLoop() {
clearCanvas();
renderCanvas();
stats.update();
requestAnimFrame(renderLoop);
}
function clearCanvas() {
masterContext.clearRect(viewOffsetX, viewOffsetY, masterCanvas.width/viewScaleFactor, masterCanvas.height/viewScaleFactor);
}
function renderCanvas() {
for (var imageY = 0; imageY < canvasDimensionImageCount; imageY++) {
for (var imageX = 0; imageX < canvasDimensionImageCount; imageX++) {
var x = imageX * (imageDimensionPixelCount + paddingPixelCount);
var y = imageY * (imageDimensionPixelCount + paddingPixelCount);
var imageIndex = (imageY * canvasDimensionImageCount) + imageX;
var image = images[imageIndex];
masterContext.drawImage(image, x, y, imageDimensionPixelCount, imageDimensionPixelCount);
}
}
}
function createLocalImages(imageCount, imageDimension) {
var tempCanvas = document.createElement('canvas');
tempCanvas.width = imageDimension;
tempCanvas.height = imageDimension;
var tempContext = tempCanvas.getContext('2d');
var images = new Array();
for (var imageIndex = 0; imageIndex < imageCount; imageIndex++) {
tempContext.clearRect(0, 0, imageDimension, imageDimension);
tempContext.fillStyle = "rgb(" + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ", " + getRandomInt(0, 255) + ")";
tempContext.fillRect(0, 0, imageDimension, imageDimension);
var image = new Image();
image.src = tempCanvas.toDataURL('image/png');
images.push(image);
}
return images;
}
// Get this party started
init();
Và một liên kết jsfiddle cho niềm vui tương tác của bạn: http://jsfiddle.net/BtyL6/14/
này được vẽ 50px x 50px hình ảnh trong một 50 x 50 (2500) lưới trên vải . Tôi cũng đã nhanh chóng thử với hình ảnh 25px x 25px và 50 x 50 (2500).
Chúng tôi có các ví dụ địa phương khác xử lý hình ảnh lớn hơn và số lượng hình ảnh lớn hơn và trình duyệt khác bắt đầu đấu tranh với những giá trị cao hơn này.
Để kiểm tra nhanh, tôi đã kích hoạt mã trong hình ảnh js fiddle đến 100px x 100px và 100 x 100 (10000) và vẫn chạy ở tốc độ 16fps khi thu nhỏ hoàn toàn. (Lưu ý: Tôi phải hạ thấp viewMinScaleFactor xuống 0,01 để vừa với tất cả khi thu nhỏ.)
Mặt khác Chrome có vẻ bị giới hạn và FPS giảm từ 60 xuống còn 2-4.
Dưới đây là một số thông tin về những gì chúng tôi đã cố gắng và kết quả:
Chúng tôi đã cố gắng sử dụng setInterval hơn requestAnimationFrame.
Nếu bạn tải 10 hình ảnh và vẽ chúng 250 lần mỗi hình thay vì 2500 hình ảnh được vẽ một lần thì vấn đề sẽ biến mất. Điều này dường như chỉ ra rằng chrome đang tấn công một số loại giới hạn/kích hoạt như lượng dữ liệu lưu trữ về hiển thị.
Chúng tôi đã chọn lọc (không hiển thị hình ảnh ngoài phạm vi hình ảnh) trong các ví dụ phức tạp hơn và điều này giúp giải pháp không phải là giải pháp vì chúng tôi có thể hiển thị tất cả hình ảnh cùng một lúc.
Chúng tôi chỉ hiển thị hình ảnh nếu có thay đổi trong mã cục bộ của chúng tôi, điều này giúp (khi không có gì thay đổi, rõ ràng) nhưng không phải là giải pháp đầy đủ vì canvas nên tương tác.
Trong mã ví dụ, chúng tôi đang tạo hình ảnh bằng canvas, nhưng mã cũng có thể chạy khi nhấn dịch vụ web để cung cấp hình ảnh và hành vi tương tự (chậm) sẽ được xem.
Chúng tôi thấy rất khó để tìm kiếm sự cố này, hầu hết kết quả là từ một vài năm trước và đã lỗi thời.
Nếu có thêm thông tin nào hữu ích thì vui lòng hỏi!
EDIT: Đã thay đổi js fiddle URL để phản ánh cùng mã như trong câu hỏi. Bản thân mã không thực sự thay đổi, chỉ là định dạng. Nhưng tôi muốn nhất quán.
EDIT: jsfiddle cập nhật và và mã với css để ngăn việc chọn và gọi requestAnim sau khi render vòng lặp được thực hiện.
Tôi đã thử nghiệm mã trong Chrome 27.0.1453.94 và 29.0.1516.3 canary. Trong ** Chrome canary ** Tôi không có vấn đề về hiệu suất. Ngay cả trong một máy tính cấp thấp, tôi nhận được 15 khung hình/giây với tất cả các hình ảnh trên màn hình. Ví dụ, Internet Explorer 10 với mức thu phóng tối thiểu hiệu suất vẫn ở trên 5 khung hình/giây. Đó rõ ràng là một vấn đề thực tế và dường như các nhà phát triển Chrome đã sửa nó cho các bản phát hành chính thức tiếp theo. –
Chỉ cần chỉ ra rằng mã trong Fiddle có nhiều vấn đề. Mã này vẫn còn sống! Đôi khi nó làm việc một cách duyên dáng và chỉ cần bấm chạy một lần nữa trang bị đóng băng, ngay cả trong Canary như tôi đã đăng trước đó. Ken đã làm một công việc tốt đẹp trong câu trả lời của anh, btw. –
Xin chào gfcarv, đồng nghiệp của tôi đã được thử nghiệm ở Canary và cũng tìm thấy nó được cải thiện. Hy vọng rằng nó chỉ là một hồi quy và nó sẽ được sửa chữa sớm. Như đã đề cập ở trên, mã này chỉ đơn giản là sinh sản của vấn đề. Mã "thực" có nhiều tính năng hơn để cải thiện hiệu suất trong các điều kiện nhất định. Tuy nhiên, ngay cả những thứ như culling và chỉ vẽ lại khi cần thiết không giúp giảm mức sử dụng tài nguyên tối đa và không ngăn Chrome "kích hoạt" chế độ chậm này. Thật không may mã của Ken giảm bớt vấn đề bằng cách không còn thực hiện các hành động cần thiết, cụ thể là vẽ hình ảnh vào canvas. – AndyJ