Мерцание холста HTML5 в FireFox 4

Я работаю над доказательством концепции на холсте HTML5. Я смог заставить это работать как шарм в Chrome и IE9, но в Firefox 4 я получаю постоянное мерцание, когда он перерисовывает холст. Я пробовал несколько методов, упомянутых на этом сайте, таких как двойная буферизация, но я все еще получаю большое количество мерцаний. Любое понимание этого будет оценено!

<!DOCTYPE HTML>
<html>
<head>
<style type="text/css">  
body
{  
    margin:0px;  
    padding:0px;  
    text-align:center;  
} 
canvas
{  
    outline:0;  
    border:1px solid #000;  
    margin-top: 10px;
    margin-left: auto;  
    margin-right: auto;  
}  
</style>
<script type="text/javascript">
var canvasWidth = 640;
var thisXPos = 0;
var canvasHeight = 820;
var thisYPos = 0;
var canvas = null;
var context = null;
var gLoop = null;
var rain = [];
var rainImg = "images/raindrop.gif";
var bgImg = null;
var i;

for (i = 0; i < howManyLetters; i++)
{
    rain.push([Math.floor(Math.random() * canvasWidth), Math.floor(Math.random() * canvasHeight),rainImg]);
}

var DrawRain = function()
{
    for (i = 0; i < 10; i++)
    {
        thisXPos = rain[i][0];
        thisYPos = rain[i][1];
        imgSrc = rain[i][2];
        letterImg = new Image();
        letterImg.setAtX = thisXPos;
        letterImg.setAtY = thisYPos;
        letterImg.onload = function()
        {
            context.drawImage(this, this.setAtX, this.setAtY);
        }
        letterImg.src = imgSrc;
    }
};

var MoveRain = function(e)
{
    for (i = 0; i < 10; i++)
    {
        if ((rain[i][1] - 5) > canvasHeight)
        {
            randomnumber = Math.floor(Math.random()*26);

            rain[i][0] = Math.random() * canvasWidth;
            rain[i][1] = 0;
            rain[i][2] = rainImg;
        }
        else
        {
            rain[i][1] += e;
        }
    }
};

var clear = function()
{
    context.beginPath();
    context.rect(0, 0, canvasWidth, canvasHeight);
    context.closePath();

    bgImg = new Image();
    bgImg.src = "images/bg.png";
    bgImg.onload = function()
    {
        context.drawImage(bgImg,0,0);
    }
} 

var GameLoop = function()
{
    context.save();
    clear();
    MoveRain(1);
    DrawRain();
    context.restore();

    gLoop = setTimeout(GameLoop, 10);
}

function loadGame()
{
    canvas = document.getElementById("gameCanvas");
    context = canvas.getContext("2d");
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    GameLoop();
}

</script>
</head>
<body onload="loadGame();">
    <canvas id="gameCanvas"></canvas>
</body>
</html>

person Dexter    schedule 15.04.2011    source источник


Ответы (2)


Я перегнал ваш пример до этого:

http://jsfiddle.net/sPm3b/6/

И это работает очень быстро в Firefox и Chrome.

Итак, мы знаем, что проблема заключается в изображениях.

Вам нужно оптимизировать то, как они создаются и загружаются. Прямо сейчас каждый clear() создает новое изображение и ждет его загрузки! Это изображение должно быть создано только один раз, в вашем loadGame(), а затем повторно использовано снова и снова.

Точно такая же сделка с letterImg в DrawRain(). Перенесите его создание на loadGame()

Это, вероятно, решит проблему.

РЕДАКТИРОВАТЬ:

как это:

Вверху добавляем:

var letterImg = new Image();
var bgImg = new Image();

затем

function loadGame()
{
    canvas = document.getElementById("gameCanvas");
    context = canvas.getContext("2d");
    canvas.width = canvasWidth;
    canvas.height = canvasHeight;

    bgImg.src = "images/bg.png";
    letterImg.src = "images/raindrop.gif";

    // optional: wait for them to load here

    GameLoop();
}

Тогда drawRain, например, будет выглядеть так:

var DrawRain = function()
{
    for (i = 0; i < 10; i++)
    {
        thisXPos = rain[i][0];
        thisYPos = rain[i][1];
        context.drawImage(letterImg, thisXPosX, thisYPos); // letterImg was already constructed, no need to make it again
    }
};
person Simon Sarris    schedule 15.04.2011
comment
Однако я удаляю исходное изображение и перерисовываю его на 1 пиксель ниже предыдущего местоположения, чтобы показать падение. Может быть, я неправильно понимаю, как работает ваш пример. Если я создаю объекты изображения внутри loadGame(), как я могу перерисовать их местоположение ниже на холсте? - person Dexter; 15.04.2011
comment
Я отредактировал, чтобы сделать это немного более ясным. Вы можете очищать холст и перерисовывать уже загруженное изображение снова и снова, если хотите, с этим нет проблем. - person Simon Sarris; 15.04.2011
comment
Рад помочь. В будущем всегда помните о таких вещах. Спросите себя: загружаю ли я что-то больше, чем нужно? Также спросите себя: я рисую вещи или делаю вычисления для объектов, которые находятся за пределами экрана? Как я могу пропустить (выполнить работу) их? С Canvas можно провести множество оптимизаций. Получайте удовольствие от кодирования! - person Simon Sarris; 15.04.2011

В дополнение к ответу Саймона Сарриса. Я использовал технику «двойного холста», чтобы избежать мерцания экрана из-за тяжелого холста.

Как это работает, всегда есть 2 версии холста, одна в DOM, одна снаружи, и всегда рисовать на той, которая не в DOM. Я использую его с очередью перерисовки.

вот часть рабочего кода

(...)
clear: function() {
    //rotating on 2 canvas, one for draw (outside DOM) one for show
    var self = this;
    if (null == self.canvasbackup) {
        var tmpcanvas = self.canvas.clone(true);
        self.canvasbackup = self.canvas;
        self.canvas=tmpcanvas;
    } else {
        var tmpcanvas = self.canvasbackup;
        self.canvasbackup = self.canvas;
        self.canvas=tmpcanvas;
    }
    self.ctx = self.canvas[0].getContext('2d');
    self.ctx.clearRect( 0, 0, self.options.width, self.options.height );
    jQuery.each(self.elements,function(idx,elt){
        // custom function: my elements need to know which canvas they depends on
        elt.reconnectCanvas(self.canvas,self.ctx);
    });
},
inDOM: function() {
    var self = this;
    if(null==self.canvasbackup) {
        //1st time need to get all things in DOM
        self.canvas.appendTo(self.div);
        self.div.appendTo(self.container);
    } else {
        // remove current shown canvas
        self.connectHuman();
        self.canvasbackup.remove();
        // loosing some events here...
        self.canvas.appendTo(self.div);
        // div is already in DOM, we are in redraw
    }
},
redraw: function() {
    var self = this;
    self.clear();
    jQuery.each(self.elements,function(idx,elt){
        elt.draw();
        elt.enddraw();
    });
    self.inDOM();
}
(...)
person regilero    schedule 15.04.2011