Оптимизация производительности холста

внешний интерфейс игра Государственный аппарат Canvas

недавняя параhtml5Небольшие игры немного интересны, потому что я чувствую, что эта вещь может стать важным сценарием внешнего интерфейса в будущем, например, каждый праздник сейчас, например, Alipay, Taobao или другие.APPЭто может дать вам push-уведомление, а затем нажать на него, это будет небольшая игра.По сути, люди, которые нажимают на него, будут играть в игру, пока они не слишком конфликтуют.getк пользователюGЭто может еще больше улучшить бизнес, будь то пользовательский опыт или развитие бизнеса, это очень хороший способ улучшить.

Кроме того, я сказал этоhtml5мини-игры включеныWebGL,WebVRи т. д., не ограничиваясь только играми, но и другими сценариями, в которых используются связанные технологии, например изображения продуктов.360°Если смотреть это онлайн, то причина, по которой мы начинаем с мини-игр, заключается в том, что мини-игры требуют всеобъемлющих технологий.Если вы можете сделать игру хорошо, а затем использовать ту же технологию для других вещей, это более удобно.

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

Итак, я собираюсь написатьcanvasМалая игровая практика, связаннаяUIМатериал собран, но как говоритсяЕсли вы хотите делать хорошую работу, вы должны сначала заточить свои инструменты,Поскольку у меня нет опыта в этой области, чтобы избежать всяких ям в процессе, я специально читал некоторые связанные статьи по наступлению на ямы.Я чувствую, что производительность - это то, на что нужно обратить внимание, и есть много дверных проемов, поэтому я разобрался.

использоватьrequestNextAnimationFrameСделать анимационный цикл

setTimeoutа такжеsetIntervalНе предназначен для непрерывного циклаAPI, поэтому может не получиться добиться плавной анимации, поэтому используйтеrequestNextAnimationFrame,может понадобитьсяpolyfill:

const raf = window.requestAnimationFrame
  || window.webkitRequestAnimationFrame
  || window.mozRequestAnimationFrame
  || window.oRequestAnimationFrame
  || window.msRequestAnimationFrame
  || function(callback) {
    window.setTimeout(callback, 1000 / 60)
  }

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

Для простых анимаций стирание и перерисовка всего на холсте в каждом кадре — хорошая идея, но для сложных фонов вы можете использоватьобласть отсечениятехника для повышения производительности за счет меньшего отрисовки на кадр

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

  • передачаcontext.save(), сохранить экранcanvasположение дел
  • позвонивbeginPathначать новый путь
  • существуетcontextвызов на объектarc(),rect()и т. д., чтобы установить путь
  • передачаcontext.clip()метод, установите текущий путь к экрануcanvasобласть отсечения
  • протрите экранcanvas(фактически стирается только область, где находится область отсечения)
  • вывести фоновое изображение на экранcanvasвключено (операции рисования на самом деле влияют только на размер области отсечения, поэтому изображение рисуется с меньшим количеством пикселей на кадр)
  • восстановить экранcanvasпараметр состояния, сбрасывает область отсечения

Закадровый буфер (закадровый холст)

Первый рисунок за кадромcanvas, а затем черезdrawImageвне экранаcanvasподтянуться к главномуcanvas, это поставить за кадромcanvasкак буфер. Кэшируйте данные экрана, которые необходимо многократно отрисовывать, чтобы уменьшить число вызовов.canvasизAPIпотребление

const cacheCanvas = document.createElement('canvas')
const cacheCtx = cacheCanvas.getContext('2d')
cacheCtx.width = 200
cacheCtx.height = 200
// 绘制到主canvas上
ctx.drawImage(0, 0)

Хотя за кадромcanvasЕго нельзя увидеть в поле зрения перед отрисовкой, но его ширину и высоту лучше установить равными размеру элемента кэша, чтобы не тратить ресурсы и не рисовать ненужные ненужные изображения.drawImageмасштабирование изображения также будет потреблять ресурсы При необходимости можно использовать несколько экрановcanvasКроме того, за кадромcanvasКогда эта ссылка больше не используется, лучше вручную сбросить ссылку наnull, избегайте, потому чтоjsа такжеdomВзаимосвязь между ними приводит к тому, что механизм сборки мусора не работает должным образом и занимает ресурсы.

сделать большую частьCSS

фоновая картинка

Если есть большое статичное фоновое изображение, рисуйте прямо наcanvasЭто может быть не очень хорошей практикой, если вы можете использовать это большое фоновое изображение какbackground-imageположить вDOMэлемент (например,div), затем поместите этот элемент вcanvasПосле этого одним меньшеcanvasрисовать визуализацию

трансформировать изменения

CSSизtransformпревосходитcanvasизtransfomr API, потому что первый может быть хорошо использован на основеGPU, так что, если вы можете,transformИзмените, пожалуйста, используйтеCSSконтролировать

Отключить прозрачность

СоздайтеcanvasконтекстуальныйAPIЕсть второй параметр:

canvas.getContext(contextType, contextAttributes)

contextTypeявляется типом контекста, а общие значения2d,Кроме тогоwebgl,webgl2,bitmaprendererТри значения, но последние три браузера поддерживают слишком низкие, обычно не используются

contextAttributesатрибут контекста, используемый для инициализации некоторых атрибутов контекста для различныхcontextType,contextAttributesВозможные значения также различны, для часто используемых2d,contextAttributesВозможные значения:

  • alpha

booleanвведите значение, указывающееcanvasсодержитalphaканал.По умолчаниюtrue, если установленоfalse, браузер рассмотритcanvasФон всегда непрозрачный, что ускоряет отрисовку прозрачного содержимого и изображений.

  • willReadFrequently

booleanВведите значение, указывающее, существует ли план повторного чтения. часто используемыйgetImageData(), что заставит программное обеспечение использовать2D canvasИ экономия памяти (вместо аппаратного ускорения). Эта схема работает для экзистенциальных свойствgfx.canvas.willReadFrequentlyсреда г. и установить наtrue(По умолчанию толькоB2G / Firefox OS)

Низкая поддержка, пока толькоGeckoПоддержка браузера ядра, обычно не используется

  • storage

stringУказывает способ хранения (по умолчанию: постоянный (persistent))

Низкая поддержка, пока толькоBlinkПоддержка браузера ядра, обычно не используется

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

const ctx = canvas.getContext('2d', { alpha: false })

Старайтесь не часто вызывать трудоемкие API.

Например

shadowСвязанныйAPI, такойAPIвключатьshadowOffsetX,shadowOffsetY,shadowBlur,shadowColor

связанные с рисованиемAPI,НапримерdrawImage,putImageData, масштабирование во время рисования также увеличивает время

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

Избегайте координат с плавающей запятой

использоватьcanvasПри анимации, если вычисляемые координаты являются числами с плавающей запятой, может появитьсяCSS Sub-pixelПроблема в том, что значение с плавающей запятой будет автоматически округляться до целого числа, то в процессе анимации, так как реальная траектория движения элемента получается не строго по расчетной формуле, может возникнуть дрожание, и элемент может тоже дергаться.сглаживание по краям Это также аспект, который может повлиять на производительность, поскольку все время выполняются ненужные криминалистические расчеты.

Операции рендеринга и рисования не следует вызывать часто

Визуализированоapi,Напримерstroke(),fill,drawImage, будетctxСостояние конечного автомата на самом деле рисуется на холсте, что также требует большей производительности.

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

for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.moveTo(x1[i], y1[i])
  context.lineTo(x2[i], y2[i])
  // 每条线段都单独调用绘制操作,比较耗费性能
  context.stroke()
}

for (let i = 0; i < 10; i++) {
  context.beginPath()
  context.moveTo(x1[i], y1[i])
  context.lineTo(x2[i], y2[i])
}
// 先绘制一条包含多条线条的路径,最后再一次性绘制,可以得到更好的性能
context.stroke()

Измените состояние конечного автомата ctx как можно меньше

ctxЕго можно рассматривать как конечный автомат, напримерfillStyle,globalAlpha,beginPath,ЭтиapiизменитсяctxЧто касается состояния внутри, то частое изменение состояния конечного автомата влияет на производительность.

Вы можете повысить производительность, лучше планируя операции и уменьшая количество изменений конечного автомата, таких как рисование нескольких строк текста на холсте, а шрифты верхнего и нижнего текста одинаковы.30px, цветаyellowgreen, средний текст20px pink, то вы можете сначала нарисовать верхний и нижний текст, а затем нарисовать средний текст вместо того, чтобы рисовать сверху вниз, потому что первый уменьшает изменение состояния конечного автомата один раз

const c = document.getElementById("myCanvas")
const ctx = c.getContext("2d")

ctx.font = '30 sans-serif'
ctx.fillStyle = 'yellowgreen'
ctx.fillText("大家好,我是最上面一行", 0, 40)

ctx.font = '20 sans-serif'
ctx.fillStyle = 'red'
ctx.fillText("大家好,我是中间一行", 0, 80)

ctx.font = '30 sans-serif'
ctx.fillStyle = 'yellowgreen'
ctx.fillText("大家好,我是最下面一行", 0, 130)

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

ctx.font = '30 sans-serif'
ctx.fillStyle = 'yellowgreen'
ctx.fillText("大家好,我是最上面一行", 0, 40)
ctx.fillText("大家好,我是最下面一行", 0, 130)

ctx.font = '20 sans-serif'
ctx.fillStyle = 'red'
ctx.fillText("大家好,我是中间一行", 0, 80)

звонить как можно режеcanvas API

Эм,canvasтакже манипулируяjsрисовать, но по сравнению с обычнымjsработай, звониcanvas APIБудет потреблять больше ресурсов, поэтому планируйте, прежде чем рисовать, поУмеренный jsСокращение собственных вычисленийcanvas APIЗвонок - более рентабельная вещь

Конечно, обратите вниманиеУмеренныйДва слова, если одну строку сократитьcanvas APIСтоимость звонка десять дополнительных линийjsПосчитай, тогда этого может и не надо делать

избежать блокировки

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

web worker

web workerНаиболее часто используемый сценарий — большое количество частых вычислений для снижения нагрузки на основной поток.Если вы столкнулись с крупномасштабными вычислениями, вы можете использовать этотAPIРазделите основное давление нити, этоAPIСовместимость уже очень хорошая, т.к.canvasможно использовать, т.web workerТакже можно рассмотреть возможность использования

Разбивайте задачи

Разделите большой процесс задачи на несколько небольших задач и используйте опрос таймера для выполнения операций.Для декомпозиции задачи задача должна соответствовать следующим условиям:

  • Операции циклической обработки, не требующие синхронизации
  • Данные не требуется обрабатывать для

Задачи декомпозиции включают две ситуации:

  • Распределить по общему количеству задач

Например, для выполнения общей операционной задачи из десятков миллионов уровней ее можно разложить на10вычислительные задачи уровня миллионов

// 封装 定时器分解任务 函数
function processArray(items, process, callback) {
  // 复制一份数组副本
  var todo=items.concat();
  setTimeout(function(){
    process(todo.shift());
    if(todo.length>0) {
      // 将当前正在执行的函数本身再次使用定时器
      setTimeout(arguments.callee, 25);
    } else {
      callback(items);
    }
  }, 25);
}

// 使用
var items=[12,34,65,2,4,76,235,24,9,90];
function outputValue(value) {
  console.log(value);
}
processArray(items, outputValue, function(){
  console.log('Done!');
});

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

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

  • Распределить на основе времени выполнения

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

function timedProcessArray(items, process, callback) {
  var todo=items.concat();
  setTimeout(function(){
    // 开始计时
    var start = +new Date();
    // 如果单个数据处理时间小于 50ms ,则无需分解任务
    do {
      process(todo.shift());
    } while (todo.length && (+new Date()-start < 50));

    if(todo.length > 0) {
      setTimeout(arguments.callee, 25);
    } else {
      callback(items);
    }
  });
}

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

Суммировать

что я собираюсь делатьcanvasИгра кажется долго делается.Кроме того,чтобы каждый день ходить на работу,осталось не так много времени.Я не знаю,когда она будет закончена.Если все пойдет хорошо,я бы хотел поиграть еще в какую-нибудь игру двигатели, такие какEgret,LayaAir,Cocos CreatorПеределайте его, чтобы ознакомиться с использованием этих игровых движков, а затем напишите серию руководств...

Эй, похоже, война будет затяжной.