Проблемы с производительностью HTML5 Canvas в некоторых мобильных браузерах.

Привет, у меня есть веб-приложение, которое должно работать как на смартфонах, так и на настольных браузерах. Хотя я ожидал получить любопытное поведение на небольших устройствах, таких как Iphone, я был вполне уверен, что он будет хорошо работать на Android Galaxy Tab, который является Android-устройством, с которым я могу запускать тесты в данный момент.

Теперь я установил несколько браузеров на Galaxy Tab, чтобы протестировать:

  • Собственный браузер Android
  • Chrome для Android
  • Firefox для Android

На рабочем столе я использовал

  • Fire Fox
  • Гугл Хром

и наконец у меня есть Iphone для тестирования.

Веб-сайт использует холст HTML5 для рисования на основе пикселей и спрайтов без каких-либо необычных преобразований, фильтров или эффектов, в основном простых путей и полигонов. Я слушаю сенсорные события и использую requestAnimationFrame для правильной перерисовки.

В целом, приложение хорошо работает в браузерах для настольных компьютеров, а также отлично работает на iOS Safari (iPhone) и Firefox-на-Android. Тем не менее, собственный браузер Android доставляет мне неприятности. Я настроил его так, чтобы экран мигал красным, когда JavaScript не реагирует, и он почти всегда мигает при касании экрана.

Поэтому мне интересно, есть ли какие-либо известные проблемы с Android Native App и HTML5. Из-за несуществующего названия родного браузера довольно сложно найти информацию об этом в Google. Любые идеи для меня, где я могу получить больше информации? Есть идеи, что может вызвать отставание родного браузера Android?

Есть несколько идей по этому вопросу:

  • iOS не поддерживает requestAnimationFrame, поэтому я заменил его заменой на основе тайм-аута. Если я использую эту замену в родном браузере Android, проблема не исчезнет.

  • Я использую AJAX (google clojure xhrio) довольно регулярно для получения данных с сервера. Может ли быть так, что обратные вызовы поиска данных засоряют мой конвейер событий?

  • Известно ли, что сообщения консоли журнала (console.log) замедляют работу приложений? Могут ли они запустить браузер для повторного запуска через дерево DOM или что-нибудь связанное?

2 ответа

Решение

Я провел много экспериментов с canvas во многих браузерах. Некоторые проблемы с производительностью, которые я заметил:

Сначала о ваших предположениях:

  • когда requestAnimationFrame поддерживается браузером, материалы для рисования и само приложение более отзывчивы. использование setTimeout или же setInterval Как запасной вариант всегда возможен, но вы должны быть осторожны с выбором времени. Этот надежный polyfill может немного помочь, но ничто по сравнению с нативным requestAnimationFrame.

  • Если console.log вызывается каждый кадр (или почти), да, производительность снижается. Поскольку собственный браузер Android не имеет консольного объекта, каждый раз, когда он вызывается, генерируется ошибка, которая также способствует замедлению работы вашего приложения. Вы можете сделать это:

    if(typeof console === "undefined"){ console = {}; }

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

Примечание. Chrome для Android поддерживает большинство перечисленных здесь функций HTML5, включая requestAnimationFrame а также websockets,

Дополнительная информация:

  • Рисование текста с использованием контекста 2d fillText это слишком дорого, но в некоторых браузерах это еще хуже. Предварительно отредактируйте текст на другом холсте или используйте растровые шрифты. (В родном браузере Android, после замены filltext рисование для предварительного рендеринга, производительность выросла с 10-15 FPS до 30-45 FPS в некоторых играх, которые я сделал).

  • Избегайте масштабирования и поворота контекста, поскольку они также приводят к снижению производительности. Если вам нужно масштабировать или вращать спрайт только один раз, используйте предварительную визуализацию.

  • Вам нужно минимизировать рисование в реальном времени. Предварительно рендеринг ваших вещей, когда вы можете. Перерисовывать только то, что изменилось и нуждается в обновлении.

  • Попробуйте написать эффективный для памяти и дружественный к сборщику код.

Есть еще много вещей, которые нужно сделать. Я только что привел несколько.

СОВЕТ: проведите несколько стресс-тестов для функций, которые не уверены в том, что они снижают производительность, и запишите результаты тестов.

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

Для получения дополнительной информации перейдите по ссылкам ниже:

Также ищите производительность в сообщениях и учебниках.

РЕДАКТИРОВАТЬ
Этот фрагмент кода jsfiddle показывает некоторые вещи, описанные в этом ответе, и предоставляет приблизительный счетчик кадров в секунду для теста производительности. Отредактируйте эту скрипку самостоятельно и проверьте ее.

В зависимости от того, что вы рисуете, наиболее распространенной стратегией повышения производительности с использованием Html5 canvas является использование слоев (т. Е. Нескольких холстов) и обновление только слоев, которые необходимо перерисовать, а не перерисовывание всего объекта в каждом кадре анимации. Вы можете свернуть что-то вроде этого самостоятельно или использовать что-то вроде http://www.concretejs.com/ которая представляет собой облегченную структуру холста Html5, которая позволяет выполнять такие периферийные функции, как обнаружение попаданий, расслоение, кэширование, поддержка соотношения пикселей, загрузки и т. Д. Вы бы сделали что-то вроде этого:

var wrapper = new Concrete.Wrapper({
  width: 500,
  height: 300,
  container: el
});

var layer1 = new Concrete.Layer();
var layer2 = new Concrete.Layer();

wrapper.add(layer1).add(layer2);

// something happens which requires you to redraw layer2, but not layer1...
layer2.sceneCanvas.context.fillStyle = 'red';
layer2.sceneCanvas.context.fillRect(0, 0, 200, 100);
Другие вопросы по тегам