Использование веб-рабочих для рисования с использованием встроенных функций холста
Возможно отправить CanvasPixelArray
, полученный с помощью getImageData
рабочему script, и позволить рабочему script манипулировать пикселями в фоновом потоке и, в конце концов, отправить обратно обработанный массив пикселей.
Однако я использую собственные функции рисования холста, например drawImage
. В настоящее время вызовы drawImage
блокируют поток пользовательского интерфейса. Это приводит к медленному перерисовке кнопок и заметной задержке при нажатии кнопки, чтобы назвать несколько недостатков. (Изменить: теперь можно выполнить небольшое улучшение с помощью ctx.imageSmoothingEnabled = false
, по крайней мере, на WebKit с префиксом webkit
.)
Я хотел бы переместить чертеж из основного потока в фоновый поток с помощью веб-рабочих. Однако, похоже, я не могу отправить холст или контекст рабочему.
Я нашел это уведомление о MDN:
Примечание. Как обычно, фоновые потоки, включая рабочих, не могут манипулировать DOM. Если действия, выполняемые фоновым потоком, должны привести к изменениям в DOM, они должны отправлять сообщения своим создателям для выполнения этой работы.
Но я хотел бы оставить DOM как есть; Я просто хочу рисовать вещи на элементе холста. Возможно ли это, или веб-работникам действительно разрешено вычислять и не рисовать?
(Или возможно ли использовать функции типа drawImage
для управления CanvasPixelArray
вместо рисования на холсте?)
Ответы
Ответ 1
[community edit: Этот ответ был написан и принят в 2011 году. Появились (или появляются новые технологии), которые могут позволить Web Workers и Canvas сосуществовать лучше; читатель должен знать все ответы на этой странице, кроме этого ответа.]
Вы не можете передать объект canvas или canvas в рабочий поток, потому что холст является частью DOM.
Ответ 2
[Edit ~ 5 лет спустя: некоторые из них начинают меняться, и есть новые возможности веб-платформы, которые фактически позволяют рендерить холст от Рабочего! См. Этот блог для получения дополнительной информации: https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/ - остальная часть ответа предоставлена для информации в эпоху 2011 года;)]
Работники Web могут вычислять, а не изменять DOM или делать любые призывы к рисованию на холст. Однако, как вы говорите, вы можете отправить массив пикселей веб-работнику, чтобы обработать его там и опубликовать обратно. Поскольку это асинхронно, я не понимаю, почему это может вызвать замедление в потоке пользовательского интерфейса, если вы не намеренно блокируете до тех пор, пока веб-рабочий не ответит (что вам не нужно).
Так что кажется странным, что ваши вызовы drawImage
занимают так много времени, что влияют на пользовательский интерфейс. В настоящее время большинство полотенец ускоряют аппаратное ускорение, поэтому они должны хорошо пропустить. Мое предположение заключается в том, что вы рисуете через веб-рабочего на массив пиксельных пикселей каждый кадр, что фактически означает, что вы программно визуализируете холст в javascript. Javascript по-прежнему слишком медленный, чтобы сделать это - даже программные рендереры С++ выглядят медленными, поэтому важно ускорить аппаратное ускорение. Таким образом, вы можете сделать что-то для массива пикселей холста в веб-работнике один раз, а затем, когда вы получите свой кэш результатов в Image
один раз, а затем нарисуйте Image
на холст столько, сколько хотите. Это должно быть очень быстро.
Изменить: вы можете захотеть заглянуть в WebGL, где вы можете писать фрагментарные шейдеры, которые практически не работают для обработки пиксельных эффектов. Они полностью работают на графической карте, поэтому они тупо быстро.
Ответ 3
Вы можете отправить ImageData
веб-работнику, который отправляет управляемый ImageData
обратно в поток вызывающего (основного интерфейса).
например:.
-
Создайте веб-исполнителя:
this.renderer = new Worker("renderer.js");
-
Отправьте ImageData
, созданный с холста, веб-работнику:
var ctx = this.canvas.getContext('2d');
var imageData = ctx.createImageData(width, height);
this.renderer.postMessage({ image: imageData });
-
Сделайте ImageData
манипуляцию в Web Worker и отправьте ее обратно в главный поток:
onmessage = function(e) {
var processedImage = self.doImageProcessing(e.data.image);
postMessage({ image: processedImage });
};
-
Установите управляемый ImageData
на холст в основном потоке:
this.renderer.onmessage = function (e) {
var ctx = this.canvas.getContext('2d');
ctx.putImageData(e.data.image, 0, 0);
}
Ответ 4
Для этого есть новый API (в настоящее время поддерживается только в Firefox, если вы включите pref).
См. https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas и
https://hacks.mozilla.org/2016/01/webgl-off-the-main-thread/.