为什么铬在其他浏览器不支持的情况下会在画布上显示大量图像? [英] Why does chrome struggle to display lots of images on a canvas when the other browsers don't?

查看:244
本文介绍了为什么铬在其他浏览器不支持的情况下会在画布上显示大量图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用HTML5画布,一次显示大量图片。



这个功能非常好,但最近我们遇到了Chrome的问题。

将图像绘制到画布上时,您似乎会达到某个性能下降非常快的特定点。



<这不是一个缓慢的效果,它似乎是从60fps到2-4fps。



以下是一些复制代码:

  //助手
// 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 =无穷大,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(米); var g = document.createElement(div); g.id =msGraph; g.style.cssText =position:relative; width:74px; height:30px; background-color:#0f0; v.appendChild(克); 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;打破;情况1:c.style.display =none; v.style.display =block;打破}}; 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));一个++; (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事件吸
函数getOffsetXY(eventArgs){return {X:eventArgs.offsetX ==未定义? 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;如果(d){if(w){return w / d / 40 * d> 0? 1:-1; } else {return -d / 3; }} else {return w / 120; }}

//复制代码
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;

函数init(){
images = createLocalImages(totalImageCount,imageDimensionPixelCount);
initInteraction();
renderLoop();


函数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);


函数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);
}

函数renderLoop(){
clearCanvas();
renderCanvas();
stats.update();
requestAnimFrame(renderLoop);
}

函数clearCanvas(){
masterContext.clearRect(viewOffsetX,viewOffsetY,masterCanvas.width / viewScaleFactor,masterCanvas.height / viewScaleFactor); (var imageX = 0; imageX< 0; imageY< canvasDimensionImageCount; imageY ++){
}

函数renderCanvas(){
; 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);



$ b函数createLocalImages(imageCount,imageDimension){
var tempCanvas = document.createElement('canvas');
tempCanvas.width = imageDimension;
tempCanvas.height = imageDimension;
var tempContext = tempCanvas.getContext('2d');

var images = new Array(); (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);
}

返回图片;
}

//开始派对
init();

还有一个jsfiddle链接可以让您的互动愉悦:
http://jsfiddle.net/BtyL6/14/



这是绘图50px x 50px图像在画布上的50 x 50(2500)网格中。我也很快尝试了25px x 25px和50 x 50(2500)的图像。



我们还有其他一些本地例子可以处理更大的图像和大量的图像,其他浏览器开始与这些更高的价值斗争。



作为一个快速测试,我将js小提琴中的代码抬高到100px x 100px和100 x 100(10000 )图像,并且在完全缩小时仍然以16fps的速度运行。 (注意:我必须将viewMinScaleFactor降低到0.01,以便在缩小时适用。)另一方面,Chrome似乎遇到了某种限制,而且FPS从60下降到2-4。






下面是关于我们尝试过的结果和结果的一些信息: p>

我们已经尝试过使用setinterval而不是requestAnimationFrame。



如果加载10张图像并将它们分别绘制250次超过2500个图像每个绘制一次然后问题消失。这似乎表明,chrome正在触发某种限制/触发器,以便存储关于渲染的数据量。



我们已经剔除了(不渲染图像之外的图像视觉范围)在我们更复杂的例子中,虽然这有助于它不是一个解决方案,因为我们需要能够一次显示所有图像。



我们只有图片如果我们的本地代码发生了变化,那么它会被渲染出来,对此有帮助(显然没有什么变化),但它不是一个完整的解决方案,因为画布应该是交互式的。



在示例代码中,我们使用画布创建图像,但也可以运行代码来提供图像并显示相同的行为(缓慢)。






我们发现甚至很难搜索这个问题,大部分结果都来自几年前,并且过时了。



如果有更多信息会有用,那么请填写ase ask!




编辑:更改了js小提琴URL以反映与问题中相同的代码。代码本身并没有改变,只是格式。但我希望保持一致。






编辑:更新了jsfiddle和css代码以防止选择并调用requestAnim之后渲染循环完成。

解决方案

这是Chrome中的一个合法的错误。



https://code.google.com/p/chromium/问题/详细信息?id = 247912



现在已经修复,应该很快就会出现在Chrome主线上。


We're working with the HTML5 canvas, displaying lots of images at one time.

This is working pretty well but recently we've had a problem with chrome.

When drawing images on to a canvas you seem to reach a certain point where the performance degrades very quickly.

It's not a slow effect, it seems that you go right from 60fps to 2-4fps.

Here's some reproduction code:

// 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();

And a jsfiddle link for your interactive pleasure: http://jsfiddle.net/BtyL6/14/

This is drawing 50px x 50px images in a 50 x 50 (2500) grid on the canvas. I've also quickly tried with 25px x 25px and 50 x 50 (2500) images.

We have other local examples that deal with bigger images and larger numbers of images and the other browser start to struggle with these at higher values.

As a quick test I jacked up the code in the js fiddle to 100px x 100px and 100 x 100 (10000) images and that was still running at 16fps when fully zoomed out. (Note: I had to lower the viewMinScaleFactor to 0.01 to fit it all in when zoomed out.)

Chrome on the other hand seems to hit some kind of limit and the FPS drops from 60 to 2-4.


Here's some info about what we've tried and the results:

We've tried using setinterval rather than requestAnimationFrame.

If you load 10 images and draw them 250 times each rather than 2500 images drawn once each then the problem goes away. This seems to indicate that chrome is hitting some kind of limit/trigger as to how much data it's storing about the rendering.

We have culling (not rendering images outside of the visual range) in our more complex examples and while this helps it's not a solution as we need to be able to show all the images at once.

We have the images only being rendered if there have been changes in our local code, against this helps (when nothing changes, obviously) but it isn't a full solution because the canvas should be interactive.

In the example code we're creating the images using a canvas, but the code can also be run hitting a web service to provide the images and the same behaviour (slowness) will be seen.


We've found it very hard to even search for this issue, most results are from a couple of years ago and woefully out of date.

If any more information would be useful then please ask!


EDIT: Changed js fiddle URL to reflect the same code as in the question. The code itself didn't actually change, just the formatting. But I want to be consistent.


EDIT: Updated jsfiddle and and code with css to prevent selection and call requestAnim after the render loop is done.

解决方案

This was a legitimate bug in chrome.

https://code.google.com/p/chromium/issues/detail?id=247912

It has now been fixed and should be in a chrome mainline release soon.

这篇关于为什么铬在其他浏览器不支持的情况下会在画布上显示大量图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆