Согласно спецификациям о методе CanvasRenderingContext2D drawImage
,
В частности, когда объект CanvasImageSource представляет анимированное изображение в HTMLImageElement, пользовательский агент должен использовать изображение анимации по умолчанию (то, которое определяет формат, должно использоваться когда анимация не поддерживается или отключена) или, если такого изображения нет, первый кадр анимации при рендеринге изображения для CanvasRenderingContext2D API.
Это относится к файлам .gif, SMIL-анимациям .svg и .mjpeg. Поэтому, как только вы получили данные, на холсте должен быть отрисован только один кадр.
Обратите внимание, что в Chrome есть ошибка, и учитывайте ее только для .gif изображения, но когда-нибудь они могут это исправить.
Одно из решений, как вы сами заметили, состоит в том, чтобы получить другой свежий кадр с помощью хака очистки кеша ('your.url/?' + new Date().getTime();
), но вы потеряете все преимущества формата mjpeg (частичное содержимое кадра) и не можете быть уверены, когда произойдет обновление .
Таким образом, лучшим решением, если это применимо, было бы использование формата видео. Каждый кадр видео можно нарисовать на холсте.
Редактировать 2018
Третье решение пришло мне в голову два года спустя:
Пользовательские агенты не привязаны к тому, чтобы хранить в памяти одно и то же изображение по умолчанию для всех 2DContexts в документе.
В то время как для других форматов мы все еще немного застряли, для потоков MJPEG, которые не имеют хорошо определенное изображение по умолчанию, мы фактически попадаем в первый кадр анимации.
Таким образом, рисуя <img>
, содержащий наш поток MJPEG, на двух разных холстах в разное время, мы теоретически можем получить два разных кадра одного и того же потока MJPEG, которые будут отрисованы на холстах.
Вот доказательство концепции, проверенное только на Firefox 62.
var ctx_stream = stream.getContext('2d');
var ctx_direct = direct.getContext('2d');
img.onload = function() {
stream.width = direct.width = this.naturalWidth;
stream.height = direct.height = this.naturalHeight;
// onload should fire multiple times
// but it seems it's not at every frames
// so we'll disable t and use an interval instead
this.onload = null;
setInterval(draw, 500);
};
function draw() {
// create a *new* 2DContext
var ctx_off = stream.cloneNode().getContext('2d');
ctx_off.drawImage(img, 0,0);
// and draw it back to our visible one
ctx_stream.drawImage(ctx_off.canvas, 0,0);
// draw the img directly on 'direct'
ctx_direct.drawImage(img, 0,0);
}
img.src = "http://webcam.st-malo.com/axis-cgi/mjpg/video.cgi?resolution=704x576&dummy=1491717369754";
canvas,img{
max-height: 75vh;
}
Using a new offcreen canvas every frame: <br><canvas id="stream"></canvas><br>
The original image: <br><img id="img"><br>
Drawing directly the <img> (if this works your browser doesn't follow the specs): <br><canvas id="direct"></canvas><br>
Таким образом, хотя это решение, очевидно, повлияет на производительность (мы создаем совершенно новый элемент холста и его 2DContext в каждом кадре), оно все же, вероятно, лучше, чем переполнение сети. И все это в любом случае должно легко собираться мусором.
person
Kaiido
schedule
22.03.2016