HTML5: Canvas работает слишком медленно на компьютерах более низкого уровня - PullRequest
3 голосов
/ 21 февраля 2011

Моя проблема заключается в том, что мой javascript / canvas работает очень медленно на компьютерах более низкого уровня (хотя они могут выполнять даже более сложные canvas сценарии плавно). Я пытаюсь сделать простую анимацию в зависимости от выбора пользователя.

Когда рисование на холсте напрямую оказалось слишком медленным, я рисую на скрытом холсте и сохраняю все кадры (getImageData) в data, а затем вызываю animate(1); для рисования на моем реальном холсте.

function animate(i){
    if(i < 12){
        ctx2.putImageData(data[i], 0, 0);
        setTimeout(function(){animate(i+1)},1);
    }
}

Но даже это слишком медленно. Что мне делать?

Ответы [ 6 ]

7 голосов
/ 21 февраля 2011
  1. Не используйте putImageData, если можете помочь. Производительность на FF3.6 ужасна :
    Результаты теста, показывающие низкую производительность getImageData http://phrogz.net/tmp/canvas_copy_benchmark.png
    Используйте команды рисования на закадровых холстах и ​​скрытых спрайтах в субрегионах, используя вместо этого drawImage.

  2. Как уже упоминалось @MartinJespersen, переписать цикл рисования рамки:

    var animate = function(){
      // ...
      setTimeout(animate,30); //Max out around 30fps
    };
    animate();
    
  3. Если вы используете библиотеку, которая вызывает clearRect каждый кадр, но вам это не нужно, прекратите использовать эту библиотеку. Очистите и перерисовайте только те части, которые вам нужны.

  4. Используйте меньший размер холста. Если вы сочтете это достаточным, вы можете даже увеличить его с помощью CSS.

  5. Примите, что медленные компьютеры медленные, и вы стоите на плечах множества уровней абстракции. Если вы хотите получить производительность для компьютеров низкого уровня, пишите на C ++ и OpenGL. В противном случае установите минимальные системные требования.

3 голосов
/ 21 февраля 2011

Заданное вами время ожидания составляет 1 миллисекунду. Ни один браузер не может обновить холст так быстро. Измените его на 1000 - это будет 1 секунда, то есть:

setTimeout(function(){animate(i+1)}, 1000)

UPD. Еще одна попытка состоит в том, чтобы подготовить столько холстов, сколько кадров в вашей анимации, установите для всех из них значение display:none, а затем последовательно включите для них display:block. Я сомневаюсь, что это будет быстрее, чем putImageData, но все же стоит попробовать.

2 голосов
/ 21 февраля 2011
  1. Как уже упоминалось, тайм-ауты с интервалом в 1 миллисекунду обречены на провал, поэтому первый шаг должен остановить это.

  2. Вы вызываете setTimeout рекурсивнокоторый не идеален для создания анимации.Вместо этого инициируйте все setTimeout, которые вам нужны для всей анимации одновременно, с увеличением задержек в цикле и позвольте им идти своим курсом, или, что еще лучше, используйте setInterval, который является лучшим способом создания анимации, и какАнимации экземпляра jQuery работают.

  3. Похоже, вы пытаетесь перерисовать весь canvas на каждом шаге вашей анимации - это не оптимально, попробуйте манипулировать только пикселями, которые меняются.Ссылка, которую вы дали на «более сложные сценарии холста», на самом деле намного проще, чем то, что вы пытаетесь сделать, поскольку это все математика на векторной основе - именно для этого оптимизирован элемент canvas - ее никогда не заставляли делатьполное повторное рендеринг каждые x миллисекунд, и, вероятно, никогда не будет.

  4. Если вам действительно нужно изменить полное изображение для каждого кадра в вашей анимации - не используйтеcanvas, но обычные теги изображений с предварительно загруженными изображениями будут работать ровно в ie6 на одноядерном атоме.

0 голосов
/ 18 мая 2013

Я сделал setTimeout таким образом, надеюсь, он поможет кому-нибудь в улучшении приложения:

var do = true;
var last = false;
window.onmousemove = function(evt){
    E.x = evt.pageX - cvs.offsetLeft;
    E.y = evt.pageY - cvs.offsetTop;
    if(do){
        draw();
        do = false;
        //in 23 ms drawing enabled again
        var t = setTimeout(function(){do = true;},23);
    }else{
        //the last operation must be done to catch the cursor point
        clearTimeout(last );
        last = setTimeout(function(){draw();},23);
    }
};
0 голосов
/ 26 января 2012

ОК, обо всем по порядку. Что еще происходит во время этой анимации? Любой другой javascript, любые другие таймеры, любые другие обработчики? Ответ, кстати, не может быть ничем . Ваш браузер перерисовывает окно - биты, которые вы меняете, по крайней мере. Если другой javascript работает, помните, что это не совсем так. Javascript является однопоточным по дизайну. Вы можете только очередь для выполнения , поэтому, если какой-то другой javascript перегружает поток, вы не увидите его.

Во-вторых, узнайте, как работают таймеры. http://ejohn.org/blog/how-javascript-timers-work/ - мой личный любимый пост на эту тему. В частности, setTimeout просит браузер запустить что-либо после по крайней мере указанного времени, но только когда у браузера есть отверстие для этого.

В-третьих, знайте, что вы делаете с function(){animate(i+1);}. Эта анонимная функция может только существовать в пределах своей родительской функции. Другими словами, когда вы ставите в очередь такую ​​функцию, родительская область все еще существует в стеке вызовов, как указывал @MartinJespersen. И поскольку эта функция ставит в очередь другую, другую и другую ... каждая будет становиться все медленнее.

Я все изложил в маленькой скрипке:

http://jsfiddle.net/KzGRT/

(впервые я использовал jsfiddle, так что будьте добры). Это простая 10-кадровая анимация с (номинально) 100 мс, использующая setTimeout для каждого. (Я сделал это таким образом вместо setInterval, потому что, теоретически, тот, который выполняется дольше, должен начать отставать от других. В теории - опять же, потому что javascript является однопоточным, если один замедлится, это также задержит других).

В верхнем методе все десять изображений нарисованы на перекрывающихся холстах, и только по одному показу за раз. Анимация просто скрывает предыдущий кадр и показывает следующий. Второй выполняет putImageData в холст с функцией верхнего уровня. Третий использует анонимную функцию, как вы пытались. Следите за красной вспышкой на нулевом кадре, и вы увидите, кто выполняет самые быстрые - для меня это занимает некоторое время, но в конечном итоге они начинают дрейфовать (в Chrome, на приличной машине. Это должно быть более очевидно в FF) на что-то более низкое-спекуляция).

Попробуйте на тестовом компьютере низкого уровня и посмотрите, что получится.

0 голосов
/ 26 января 2012

У меня есть приложение, которое работает как карты Google - оно позволяет нажимать и перемещаться по большому изображению. Я сильно перерисовываю свой холст, выбирая и масштабируя каждое большое изображение.

В любом случае, мне довелось попробовать подход с двумя холстами - рисовать в (больший) буфер при необходимости, а затем делать canvas_display.drawImage (canvas_buffer) для вывода области на экран. Мало того, что я не видел увеличения производительности, но это стало значительно медленнее с iPhone. Просто назначение данных ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...