Вопрос по canvas, html5 – Перерисовка холста HTML5 невероятно медленная

5

Я только начал играть с холстом HTML5, и я надеялся сделать пару игр с ним. Однако, как только я начал отображать координаты мыши, он почти остановился:

http://jsfiddle.net/mnpenner/zHpgV/

Все, что я сделал, это отрисовал 38 строк и немного текста, он должен был справиться с этим, нет?

Я делаю что-то неправильно? Я хотел бы иметь возможность рендерить по крайней мере 30 FPS, но для чего-то подобного я бы ожидал, что он сможет рисовать тысячи раз.

Или я просто использую не тот инструмент для работы? WebGL для этой задачи? Почему один будет намного медленнее другого?

String.prototype.format = function() {
    var args = arguments;
    return this.replace(/\{(\d+)\}/g, function(m, n) {
        return args[n];
    });
};
var $canvas = $('#canvas');
var c = $canvas[0].getContext('2d');
var scale = 20;
var xMult = $canvas.width() / scale;
var yMult = $canvas.height() / scale;
var mouseX = 0;
var mouseY = 0;
c.scale(xMult, yMult);
c.lineWidth = 1 / scale;
c.font = '1pt Calibri';

function render() {
    c.fillStyle = '#dcb25c';
    c.fillRect(0, 0, scale, scale);
    c.fillStyle = '#544423';
    c.lineCap = 'square';
    for (var i = 0; i <= 19; ++i) {
        var j = 0.5 + i;
        c.moveTo(j, 0.5);
        c.lineTo(j, 19.5);
        c.stroke();
        c.moveTo(0.5, j);
        c.lineTo(19.5, j);
        c.stroke();
    }
    c.fillStyle = '#ffffff';
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5);
}
render();
$canvas.mousemove(function(e) {
    mouseX = e.clientX;
    mouseY = e.clientY;
    render();
});
<canvas id="canvas" width="570" height="570"></canvas>

Ваш Ответ

3   ответа
7

Вот код, сделанный намного лучше.

http://jsfiddle.net/zHpgV/3/

Вот разбивка вещей, которые вы должны принять во внимание, что я изменил:

  • Continuous adding to a path instead of stopping and creating a new path with beginPath. This is by far the biggest performance killer here. You're ending up with a path with thousands and thousands of line segements that never gets cleared.
  • Continuously making the same path over and over when it only needs to be made once, on initialization. That is, the only thing you need to call inside of render is stroke. You do not need to call lineTo/moveTo ever again, and certainly not continuously. See note 1.
  • Stroking twice for one path
  • Stroking inside a for loop
  • Redrawing a background instead of setting CSS background
  • Setting the line cap over and over

Note 1: Если вы планируете иметь более одного пути в своем приложении, то вам, вероятно, следует кэшировать пути, подобные этому, поскольку они никогда не меняются. У меня есть учебник о том, как это сделатьВот.

Конечно,if you are doing all of this to just make a background, it should be saved as a png and you should be using a CSS background-image.

Вот так:http://jsfiddle.net/zHpgV/4/

Тогда внезапно ваша процедура рендеринга становится довольно маленькой:

function render() {
    c.clearRect(0, 0, scale, scale);
    c.fillText('{0}, {1}'.format(mouseX, mouseY), 0.5, 1.5);
}
Я не знал, что пути сработали! Я думаю, что иметьpath Объект был бы более интуитивным. Имеет смысл, почему это было так медленно, хотя, спасибо! mpen
Теперь в спецификации HTML5 Canvas есть объект path, и вы сможете создать Path и вызватьdrawPath в будущем. Но ни один браузер еще не внедрил его, и могут пройти месяцы, прежде чем вы сможете его использовать. Купи когда-нибудь!
9

Как я сказал в комментариях, я был удивлен медлительностью этого кода, поскольку я рисую намного более сложные вещи с очень быстрой анимацией, даже не заботясь о двойной буферизации.

Поэтому я посмотрел немного больше и нашел ошибку, как и ожидалось.

Основная проблема - накопление пути прорисовки.

Добавитьc.beginPath(); каждый раз, когда вы рисуете один путь.

Вот этобыстрый рендеринг того же, чтобы доказать это сейчас летит.

Холст рисунокis быстрый и может быть использован для анимации.

7

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

<div id="canv">
 <canvas id="bgLayer" width="500" height="500" style="z-index: 0"></canvas>
 <canvas id="fgLayer"  width="500" height="500" style="z-index: 1"></canvas>
</div>

Вотпример Я играл со слоистым холстом. Стол нарисован на нижнем холсте, шары нарисованы на верхнем холсте. Это просто игровая площадка, так что есть много чего исправить и оптимизировать, например, нарисовать каждый шарик только один раз на другом скрытом холсте и использоватьgetImageData / putImageData улучшить производительность.

Также рекомендуется использоватьrequestAnimationFrame обновить холст. Вместо этого ваш пример опирается на каждое движение мыши, это гораздо чаще, чем необходимо (когда мышь движется, конечно).

Есть хорошийстатья на улучшении производительности холста. Кроме того, есть отличныйТАК сообщение на эту тему.

Я застрял с canvas. Моя анимация не работает с canvas. Можете ли вы помочь мне с этим. Это мой вопрос:stackoverflow.com/questions/37208156/…
Мне потребовалось некоторое время, чтобы понять, что вы буквально подразумевали слой элементов canvas. Я думал, что "слои" были концепции в контексте холста. Это хорошая идея. Спасибо за советы! mpen
Это хороший комментарий, но есть еще одна проблема, которую я не могу найти в этом случае. Я рисую намного более сложные вещи с очень быстрой анимацией, даже не заботясь о двойной буферизации.
Извините, я изменил это, чтобы не запутать кого-то еще.
Бинго! Смотри ответ.

Похожие вопросы