У меня есть отлично работающая демонстрационная анимация в элементе canvas, которую я могу записать в виде веб-видеофайла с помощью MediaRecorder и captureStream()
из элемента <canvas>
.
Анимации из API 2-контекста контекста прекрасно воспроизводятся в полученном видео, но когда я пытаюсь использовать drawImage()
для добавления изображения на холст, я не могу заставить его работать должным образом,В последнем случае обработчик MediaRecorder.ondataavailable
не получает действительные данные, а полученный видеофайл представляет собой 0-байтовый файл.
Я даже реализовал демонстрационную программу, в которой я могу переключаться независимо от того, вызывается ли drawImage()
выполняется.в приведенном ниже коде, если drawImage = false
видео генерируется без проблем, но если drawImage
переключено на true
, вместо этого будет сгенерирован 0-байтовый файл.
Для демонстрации,Я собрал этот jsfiddle https://jsfiddle.net/keyboardsamurai/3tkm0dp6/16/
Я выполняю этот код на "Chrome версии 75.0.3770.100 (Официальная сборка) (64-разрядная версия)" на MacOS - даже не уверен, что он должен работать на Firefoxи т. д., поскольку API MediaRecorder генерирует кажущиеся несвязанными ошибки в FF.
См. также полный код здесь:
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<canvas id="drawing_canvas" width="1280" height="720"></canvas>
<script>
const image = new Image();
image.src = 'https://66.media.tumblr.com/84d332cafeb1052c477c979281e5713b/tumblr_owe3l0tkCj1wxdq3zo1_1280.jpg';
window.requestAnimationFrame(animation);
const drawImage = false; // toggle to 'true' to make this example fail
const canvas = document.getElementById('drawing_canvas');
const allChunks = [];
const recorder = initMediaRecorder(canvas);
recorder.start();
setTimeout(function (e) {
console.log("Video ended");
recorder.stop();
}, 5000);
function initMediaRecorder(canvasElement) {
const stream = canvasElement.captureStream(60);
const recorder = new MediaRecorder(stream, {mimeType: 'video/webm'});
recorder.ondataavailable = function (e) {
console.log("data handler called");
if (e.data) {
console.log("data available: " + e.data.size)
if (e.data.size > 0) {
console.log("data added");
allChunks.push(e.data);
}
} else {
console.error("Data handler received no data in event: " + JSON.stringify(e))
}
};
recorder.onstop = function (e) {
const fullBlob = new Blob(allChunks);
const link = document.createElement('a');
link.style.display = 'none';
link.href = window.URL.createObjectURL(fullBlob);
link.download = 'media.webm';
document.body.appendChild(link);
link.click();
link.remove();
};
return recorder;
}
function animation() {
const now = new Date();
const ctx = document.getElementById('drawing_canvas').getContext('2d');
if (drawImage) {
ctx.drawImage(image, 0, 0);
}
ctx.clearRect(0, 0, 150, 150);
ctx.strokeStyle = 'white';
ctx.fillStyle = 'white';
ctx.rect(0, 0, 1280, 720);
ctx.stroke();
ctx.save();
ctx.translate(75, 75);
ctx.scale(0.4, 0.4);
ctx.rotate(-Math.PI / 2);
ctx.strokeStyle = 'black';
ctx.fillStyle = 'white';
ctx.lineWidth = 8;
ctx.lineCap = 'round';
// Hour marks
ctx.save();
for (var i = 0; i < 12; i++) {
ctx.beginPath();
ctx.rotate(Math.PI / 6);
ctx.moveTo(100, 0);
ctx.lineTo(120, 0);
ctx.stroke();
}
ctx.restore();
// Minute marks
ctx.save();
ctx.lineWidth = 5;
for (i = 0; i < 60; i++) {
if (i % 5 != 0) {
ctx.beginPath();
ctx.moveTo(117, 0);
ctx.lineTo(120, 0);
ctx.stroke();
}
ctx.rotate(Math.PI / 30);
}
ctx.restore();
const sec = now.getSeconds();
const min = now.getMinutes();
let hr = now.getHours();
hr = hr >= 12 ? hr - 12 : hr;
ctx.fillStyle = 'black';
// write Hours
ctx.save();
ctx.rotate(hr * (Math.PI / 6) + (Math.PI / 360) * min + (Math.PI / 21600) * sec);
ctx.lineWidth = 14;
ctx.beginPath();
ctx.moveTo(-20, 0);
ctx.lineTo(80, 0);
ctx.stroke();
ctx.restore();
// write Minutes
ctx.save();
ctx.rotate((Math.PI / 30) * min + (Math.PI / 1800) * sec);
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(-28, 0);
ctx.lineTo(112, 0);
ctx.stroke();
ctx.restore();
// Write seconds
ctx.save();
ctx.rotate(sec * Math.PI / 30);
ctx.strokeStyle = '#D40000';
ctx.fillStyle = '#D40000';
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(-30, 0);
ctx.lineTo(83, 0);
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 10, 0, Math.PI * 2, true);
ctx.fill();
ctx.beginPath();
ctx.arc(95, 0, 10, 0, Math.PI * 2, true);
ctx.stroke();
ctx.fillStyle = 'rgba(0, 0, 0, 0)';
ctx.arc(0, 0, 3, 0, Math.PI * 2, true);
ctx.fill();
ctx.restore();
ctx.beginPath();
ctx.lineWidth = 14;
ctx.strokeStyle = '#325FA2';
ctx.arc(0, 0, 142, 0, Math.PI * 2, true);
ctx.stroke();
ctx.restore();
window.requestAnimationFrame(animation);
}
</script>
</body>
</html>
Обновление : приведенное выше поведение подтверждена возможность репликации как минимум на следующие версии Chrom (e / ium):
Версия 75.0.3770.100 (официальная сборка) (64-разрядная версия) в MacOS Mojave 10.14.5
Версия 77.0.3849.0 (Официальная сборка) (64-разрядная версия) на MacOS Mojave 10.14.5
Версия 77.0.3770.100 (Официальная сборка) (64-разрядная версия) на Ubuntu 19.04 Disco Dingo