Подтвердить что ты не робот

Рекомендации по производительности и оптимизации холста HTML5, рекомендации и рекомендации по кодированию и кодированию

ЗНАЕТЕ ЛИ ВЫ НЕКОТОРЫЕ БОЛЬШЕ ЛУЧШИЕ ПРАКТИКИ ДЛЯ КАНВАС?

Пожалуйста, добавьте в эту тему то, что вы знаете, узнали или читали в Интернете все лучшие практики Canvas, советы/рекомендации для производительности

С Canvas, все еще очень новым для Интернета, и никаких признаков того, что он когда-либо стареет, что я вижу в будущем, не так много документированных "лучших практик" или других действительно важных советов, которые должны "знать", для развития с ним в любом конкретном месте. Такие вещи разбросаны вокруг и много раз на менее известных сайтах.

Там так много вещей, о которых люди должны знать, и еще много чего узнать.


Я хотел поделиться некоторыми вещами, чтобы помочь людям, которые изучают Canvas и, возможно, некоторым, кто уже хорошо это знает, и я надеюсь получить от других отзывы о том, что они чувствуют, это некоторые лучшие практики или другие советы и рекомендации для работы с Холст в HTML5.

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

1. Отступ вашего кода

Так же, как и в любое другое время, на любом другом языке, в любом случае. Это было лучшей практикой для всего остального, и я пришел к выводу, что в сложном приложении canvas все может немного запутываться при работе с несколькими различными контекстами и состояниями сохранения/восстановления. Не говоря уже о том, что код является более читаемым и в целом чистым выглядит тоже.

Например:

...
// Try to tell me this doesn't make sense to do
ctx.fillStyle = 'red';
ctx.fill();
ctx.save();
    if (thing < 3) {
        // indenting
        ctx.beginPath();
            ctx.arc(2, 6, 11, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.beginPath();
            ctx.moveTo(20, 40);
            ctx.lineTo(10, 200);
            ctx.moveTo(20, 40);
            ctx.lineTo(100, 40);
        ctx.closePath();
        ctx.save();
            ctx.fillStyle = 'blue'
            ctx.fill();
        ctx.restore();
    } else { 
        // no indenting
        ctx.drawImage(img, 0, 0, 200, 200);
        ctx.save();
        ctx.shadowBlur();
        ctx.beginPath();
        ctx.arc(2, 60, 10, 0, Math.PI*2, false);
        ctx.closePath();
        ctx.fillStyle 'green';
        ctx.fill();
        ctx.restore();
    }
ctx.restore();
ctx.drawRect();
ctx.fill();
...

Является ли утверждение IF проще и чище читать и знать, что же происходит, чем утверждение ELSE? Вы видите, что я здесь говорю? Я думаю, что это должен быть метод, который разработчики должны продолжать практиковать так же, как и при написании простого JavaScript-кода или любого другого языка.

Использовать requestAnimationFrame вместо setInterval/setTimeout

setInterval и setTimeout никогда не предназначались для использования в качестве таймеров анимации, это всего лишь общие методы для вызова функций после задержки. Если в будущем вы установите интервал на 20 мс, но ваша очередь функций займет больше времени, чем будет выполняться, ваш таймер не будет срабатывать до тех пор, пока эти функции не будут завершены. Это может быть какое-то время, что не идеально подходит для анимации. RequestAnimationFrame - это метод, который сообщает браузеру, что анимация имеет место, поэтому она может оптимизировать перерисовку. Он также дросселирует анимацию для неактивных вкладок, поэтому она не будет убивать батарею вашего мобильного устройства, если вы оставите ее открытой в фоновом режиме.

Николас Закас написал в своем блоге чрезвычайно подробный и информативный материал Пол Ирланд написал прокрутку requestAnimationFrame - это то, что я использовал в каждом из Приложения холста, которые я сделал до недавнего времени.

<ФАКТИЧЕСКИ/h2 >

Даже лучше, чем использование requestAnimationFrame вместо setTimeout и setInterval, Джо Ламберт написал NEW и улучшенную прокладку под названием requestInterval и requestTimeout, которые он объясняет, какие проблемы существуют при использовании requestAnimFrame. Вы можете просмотреть сущность script.

АКТИВНО x2

Теперь, когда все браузеры догоняли спецификацию для этого, было обновление для requestAnimFrame() polyfill, который, вероятно, останется тем, который будет использоваться для охвата всех поставщиков.

Использовать более одного холста

Техника для анимационно-тяжелых игр, которые @nicolahibbert писал в опубликовать ее на оптимизацию игр Canvasупоминает, что лучше использовать несколько холстов, наложенных друг на друга, а не делать все в одном холсте. Никола объясняет, что "одновременное рисование слишком большого количества пикселей на одном и том же холсте приведет к падению частоты кадров через пол. Возьмите, например, Breakout. Пытаемся нарисовать кирпичи, шар, весло, любые бонусы или оружие, а затем каждая звезда в фоновом режиме - это просто не сработает, требуется слишком много времени, чтобы выполнить каждую из этих инструкций поочередно. Разделив звездное поле и остальную часть игры на отдельные холсты, вы сможете обеспечить достойную фреймрейт".

Render Elements Off-screen

Мне пришлось сделать это для нескольких приложений, которые я сделал, включая приложение Samsung Olympic Genome Project facebook. Это очень полезная вещь, чтобы знать и использовать, нужно ли это или нет. Это значительно сокращает время загрузки, а также может быть действительно полезным методом загрузки изображений с экрана, поскольку они могут иногда занимать некоторое время.

var tmpCanvas = document.createElement('canvas'),
    tmpCtx = tmpCanvas.getContext('2d'),
    img = document.createElement('img');

img.onload = function() {
    tmpCtx.drawImage(thumbImg, 0, 0, 200, 200);
};
img.src = '/some/image/source.png';

Обратите внимание, что src изображения устанавливается после его загрузки. Это тоже важно помнить. После того, как изображения будут загружены и вписаны в эти временные холсты, вы можете нарисовать их на свой основной холст, используя тот же ctx.drawImage(), но вместо того, чтобы помещать изображение в качестве первого аргумента, вы используете 'tmpCtx.canvas' для ссылки на холст temp.

Другие советы, рекомендации и ресурсы

Холст имеет обратную ссылку

В контексте 2d имеется обратная ссылка на связанный с ним элемент DOM:

var ctx = doc.getElementById('canvas').getContext('2d');
console.log(ctx.canvas);    // HTMLCanvasElement

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

4b9b3361

Ответ 1

Перерисовать регионы

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

Повторно используйте как можно больше пикселей между кадрами. Это означает, что чем меньше пикселей нужно обрабатывать каждый кадр, тем быстрее будет работать ваша программа. Например, при стирании пикселей с помощью метода clearRect (x, y, w, h) очень полезно очистить и перерисовать только измененные пиксели, а не полный холст.

Процедурные спрайты

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

Можно обойтись без сочетания обоих. Нарисуйте графические объекты процедурно на холсте один раз, когда ваше приложение запустится. После этого вы можете повторно использовать одни и те же спрайты, нарисуя их копии, вместо того, чтобы неоднократно генерировать те же тени, градиенты и штрихи.

State Stack and Transformation

Холст можно манипулировать с помощью таких преобразований, как вращение и масштабирование, что приводит к изменению системы координат холста. Здесь важно знать о стеке состояний, для которого доступны два метода: context.save() (толкает текущее состояние в стек) и context.restore() (возвращается к предыдущему состоянию). Это позволяет применить преобразование к чертежу, а затем вернуться к предыдущему состоянию, чтобы убедиться, что предыдущая трансформация не повлияет на следующую форму. Состояния также включают такие свойства, как цвета заливки и штриха.

Compositing

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

Сглаживание

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

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

Использование целых чисел для drawImage() x и y позиций

Если вы назовете drawImage на элементе Canvas, он будет намного быстрее, если вы округлите позицию x и y до целого числа.

Здесь тестовый пример jsperf, показывающий, насколько быстрее использование целых чисел сравнивается с использованием десятичных знаков.

Итак, округлите свои позиции x и y до целых чисел перед рендерингом.

Быстрее, чем Math.round()

Другой тест jsperf показывает, что Math.round() не обязательно является самым быстрым методом округления чисел. Использование побитового хака на самом деле оказывается быстрее, чем встроенный метод.

Оптимизация холст-спрайт

Очистка холста

Чтобы очистить весь холст любого существующего пикселя context.clearRect(x, y, w, h) обычно используется, но есть еще один доступный вариант. Всякий раз, когда ширина/высота холста установлены, даже если они установлены на одно и то же значение несколько раз, холст reset. Это полезно знать при работе с холстом с динамическим размером, поскольку вы заметите, что рисунки исчезают.

Распределение вычислений

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

Методы оптимизации

Ответ 2

Вот мои советы

1) Используйте clearRect, чтобы очистить холст вместо canvas.width = canvas.width, потому что позже сбрасывает состояния холста

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

/**  returns the xy point where the mouse event was occured. 
 @param ev The event object.
*/
function getXY(ev){
   return getMousePosition(ev, ev.srcElement || ev.originalTarget);
}

 /**  returns the top-left point of the element
       @param elem The element
   */
function getElementPos(elem){
   var obj = elem;
   var top = 0;
   var left = 0;
    while (obj && obj.tagName != "BODY") {
      top += obj.offsetTop-obj.scrollTop;
      left += obj.offsetLeft -obj.scrollLeft ;
      obj = obj.offsetParent;
     }
  return {
    top: top,
    left: left
    };
};

/**  returns the xy point where the mouse event was occured inside an element. 
@param ev The event object.
 @param elem The element
*/
function getMousePosition(evt, elem){
var pageX, pageY;
if(typeof(window.pageYOffset)=='number') {
    pageX=window.pageXOffset;
    pageY=window.pageYOffset;
}else{
    pageX=document.documentElement.scrollLeft;
    pageY=document.documentElement.scrollTop;
}
var mouseX = evt.clientX - getElementPos(elem).left + pageX;
var mouseY = evt.clientY - getElementPos(elem).top + pageY;
return {
    x: mouseX,
    y: mouseY
};
};

3) Используйте ExplorerCanvas, если вы хотите поддерживать IE7

4) Вместо того, чтобы очистить весь холст, очистите только часть, которую необходимо очистить. Это хорошо для производительности.

Ответ 3

После работы над недавно появившимся приложением Facebook, которое использует Canvas, и информацией о профиле пользователей Facebook (объем данных, который он должен учитывать, является массовым для некоторые), чтобы соответствовать вам и вашим друзьям, также использующим приложение, для олимпийских спортсменов, таких как 6-градусный тип разделения, я довольно многому научился в своих многочисленных усилиях, чтобы сделать все, что я мог бы попытаться повысить производительность в приложение.

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

Используйте элементы DOM, если возможно

Дело в том, что браузеры все еще не готовы обрабатывать более интенсивные запущенные приложения в Canvas, особенно если вам необходимо разработать приложение с поддержкой IE 8. Бывают случаи, когда DOM быстрее, чем текущий реализация Canvas API на момент написания этого. По крайней мере, я обнаружил, что он работает над массово сложной одностраничной анимацией html5 и холста для Samsung.

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

За несколько дней до запуска мы решили попробовать другую технику и вместо того, чтобы создавать временные холсты вне экрана, которые были помещены на видимый холст, однажды обрезанные в круги и т.д., мы просто добавили элементы изображения DOM на холсте, используя координаты x и y, которые мы использовали для размещения временных холстов раньше.

Для обрезки изображений в круги, что было просто, мы просто использовали свойство border-radius CSS3 для этого, что было гораздо меньше, чем сложная серия изменений состояния и в то время как изобретательное и творческое, но чрезмерное использование .clip().

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

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

Ответ 4

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

  • Не включайте jQuery, если вам не нужно делать больше, чем просто выбрать <canvas>.

    Мне удалось обойтись без него почти для всего, что я сделал в холсте

  • Создать абстрактные функции и отделить ваш код. Отдельная функциональность от внешнего вида или начального состояния захвата.

    Сделать общие функции многократными. В идеале вы должны использовать шаблон модуля, который вы можете создать объект utils, который содержит общие функции.

  • Использовать имена переменных одной и двух букв, когда имеет смысл (x, y, z).

    Система координат в Canvas добавляет больше одиночных букв, которые обычно объявляемые как переменные. Это может привести к созданию нескольких одиночные/двойные переменные (dX, dY, aX, aY, vX, vY) как часть элемента.

    Я предлагаю вам напечатать или абр. слово (dirX, accelX, velX) или быть описательным, иначе вещи могут стать довольно запутанными для вас позже, поверьте мне.

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

    Пример функции конструктора шара, которую я сделал:

    // Ball constructor
    var Ball = function(x, y) {
        this.x = x;
        this.y = y;
    
        this.radius = 10;
        this.color = '#fff';
    
        // Direction and min, max x,y
        this.dX = 15;
        this.dY = -15;
    
        this.minX = this.minY = 20 + this.radius;
        this.maxX = this.radius - (canvasWidth - 20);
        this.maxY = this.radius + canvasHeight;
    
        this.draw = function(ctx) {
            ctx.beginPath();
                ctx.arc(this.x, this.y, this.radius, 0, twoPI, true);
            ctx.closePath();
            ctx.save();
                ctx.fillStyle = this.color;
                ctx.fill();
            ctx.restore();
        };
    };
    

Создание шара

ball = new Ball(centerX, canvasHeight - paddle.height - 30);
ball.draw(ctx);
  • Хорошей базой для работы является создание 3 функций:   init() - выполнить всю начальную работу и настроить базовые классы и обработчики событий и т.д....   draw() - один раз, чтобы начать игру, и рисует первый кадр игры, включая создание элементов, которые могут меняться или нуждаться в конструировании.   update() - вызывается в конце draw() и внутри себя через requestAnimFrame. Обновляет свойства изменяющихся элементов, только делайте то, что вам нужно сделать здесь.

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

    Цикл анимации часто является рекурсивной функцией, то есть он быстро и многократно вызывает себя во время анимации для рисования каждого кадра.

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

    Вы можете заставить каждый элемент игры иметь собственные методы таймера, рисования и анимации в конструкторе.

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

Или другой вариант:

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