Анимация на холсте Canvas с помощью MediaRecorder и ее сохрание|JavaScript

HTML canvas - это простой и полезный инструмент в генеративном искусстве и анимации. У меня не было большого опыта работы с этим, но недавно я закончил проект, который потребовал, чтобы мы взяли его в руки, и с тех пор я был поражен сложными и универсальными изображениями, которые люди могут создавать с его помощью. Я также по-новому оценил математические принципы, стоящие за этими психоделическими визуальными эффектами концерта edm.
Однако одним из недостатков анимации canvas является то, что они существуют исключительно в среде браузера. Это затрудняет воспроизведение или использование анимации в других мультимедийных приложениях. Конкретно для нашего проекта нам пришлось сохранить динамически сгенерированную анимацию и переформатировать ее, чтобы ею можно было делиться на медиа-платформах, таких как Instagram. Документации о том, как это сделать, было немного, поэтому я подумал, что было бы полезно поделиться тем, что я узнал. К счастью, API MediaStream Recording идеально подходит для этого и FFmpeg.wasm позволяет вам выполнять все виды операций с кодированием мультимедиа внутри браузера.
Для нашего последнего проекта мне пришлось записать и сохранить анимацию на холсте HTML5. Что-то совершенно новое для меня. К счастью, с помощью [MediaStream Recording API].

Демонстрация

Через 10 секунд появится кнопка загрузки.Загрузка файла внутри iframe может быть отключена в вашем браузере. Возможно, вам придется открыть "песочницу", чтобы полностью просмотреть демонстрационную версию.
Анимация была сделана по учебнику. Обязательно посмотрите другие видео от автора, там много классных руководств.
Запись анимации на холстеЧтобы сохранить и загрузить анимацию Canvas в формате HTML, вам понадобится:
  • Анимация на холсте для анимации.
  • Media Recorder - документация здесь: предоставляет функциональность для простой записи мультимедиа.
  • (необязательно) ffmpeg - для преобразования видеозаписи из WEBM в mp4.
Именно здесь мы определяем наш холст.

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>Animation Sandbox</title>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="src/styles.css" />
  </head>
  <body>
    <div>
      <canvas id="canvas"></canvas>
      <script src="src/script.js"></script>
    </div>
  </body>
</html>

script.js

const startRecording = () => {
  const canvas = document.querySelector("#canvas");
  const data = []; // here we will store our recorded media chunks (Blobs)
  const stream = canvas.captureStream(30); // records the canvas in real time at our preferred framerate 30 in this case.
  const mediaRecorder = new MediaRecorder(stream); // init the recorder
  // whenever the recorder has new data, we will store it in our array
  mediaRecorder.ondataavailable = (e) => data.push(e.data);
  // only when the recorder stops, we construct a complete Blob from all the chunks
  mediaRecorder.onstop = (e) =>
    downloadVideo(new Blob(data, { type: "video/webm;codecs=h264" }));
  mediaRecorder.start();
  setTimeout(() => {
    mediaRecorder.stop();
  }, 6000); // stop recording in 6s
};

const downloadVideo = async (blob) => {
  const div = document.querySelector("div");
  var url = URL.createObjectURL(blob);
  var a = document.createElement("a");
  a.href = url;
  a.download = "test.webm";
  a.className = "button";
  a.innerText = "click here to download";
  div.appendChild(a);
};

// animation code...

startRecording();
animate();
При startRecording вызове мы получаем наш холст, как определено в index.html используя document.querySelector. Мы присвоили нашему холсту идентификатор#canvas, чтобы его было легко выбрать.
const canvas = document.querySelector("#canvas");
Затем мы начинаем транслировать холст в режиме реального времени со скоростью 30 кадров в секунду, определяемой частотой кадров, на которую мы переходим canvas.captureStream(). После создания экземпляра нового объекта Media Recorder мы можем записать поток в виде фрагментов данных, которые затем можно экспортировать в виде объекта Blob.
Media Recorder предоставляет некоторые опции, такие как установка типа MIME контейнеров (например: "video / webm"), а также возможность записи звука.
Продолжительность нашей записи определяется задержкой в нашей setTimeout функции, которая в данном случае установлена равной 10 секундам.
setTimeout(() => {
  rec.stop();
}, 10); // stop recording in 10s
Через 10 секунд мы вызываем rec.stop(), который запускает наше событие остановки. Здесь у нас есть наш видеоблок.
rec.onstop = (e) =>
  downloadVideo(new Blob(chunks, { type: "video/webm;codecs=h264" }));

Автоматическая загрузка записи в виде webm-файла.

Используя javascript, мы можем легко загрузить нашу запись в виде WebM файла: бесплатного формата медиафайлов, предназначенного для Интернета.
WebM воспроизводится практически во всех основных браузерах.
const exportVid(blob) {
const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.style = "display: none";
  a.href = url;
  a.download = "recording.webm";
  document.body.appendChild(a);
  a.click();
}

Конвертировать видео в mp4 с помощью ffmpeg.wasm в JavaScript

MediaRecorder не способен обрабатывать запись в mp4 в Chrome или safari (по состоянию на 2022 год), но ffmpeg.wasm - это опция, которая позволяет конвертировать webm в mp4 внутри браузера.
В onstop нашей функции startRecording мы можем передать наш большой двоичный объект этой exportVid функции, которая преобразует наш большой двоичный объект в mp4 перед его загрузкой.
import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";

const ffmpeg = createFFmpeg({
  corePath: "https://unpkg.com/@ffmpeg/core@0.11.0/dist/ffmpeg-core.js",
  log: true,
});

const exportVid = async (blob) => {
  if (!ffmpeg.isLoaded()) {
    await ffmpeg.load();
    ffmpeg.FS("writeFile", "test.avi", await fetchFile(blob));
    await ffmpeg.run("-i", "test.avi", "test.mp4");
    const data = ffmpeg.FS("readFile", "test.mp4");
    const url = URL.createObjectURL(
      new Blob([data.buffer], { type: "video/mp4" })
    );
    const a = document.createElement("a");
    a.style = "display: none";
    a.href = url;
    a.download = "recording.webm";
    document.body.appendChild(a);
    a.click();
  }
};

Поделиться записью с помощью Web Share API

Большинство устройств и браузеров могут обмениваться ссылками и файлами с помощью navigator.share() метода Web Share API
Базовая реализация этого в react заключалась бы в:
  1. В функции exportVid предыдущего фрагмента вместо загрузки файла задайте переменную состояния для вывода URL-адреса большого двоичного объекта.
        const exportVid = async (blob) => {
        await ffmpeg.run("-i", "test.avi","test.mp4");
        const data = ffmpeg.FS("readFile", "test.mp4");
        setVideoSrc(URL.createObjectURL(new Blob([data.buffer], { type: "video/mp4" })))
  1. Создайте кнопку, вызывающую onShare:
    <button onClick={onShare}>share!</button>
  2. Используйте web share api для совместного использования файла.
    const onShare = async () => {
      if (!videoSrc) return;
      const blob = await fetch(videoSrc).then((response) => response.blob());
      const file = new File([blob], "test.mp4", { type: "video/mp4" });
      const filesArray = [file];
      if (navigator.canShare && navigator.canShare({ files: filesArray })) {
        await navigator.share({
          files: filesArray,
        });
      }
    };

Глоссарий:

  • Blob: сокращение от двоичного большого объекта. Файлоподобный объект с неизменяемыми необработанными данными; может быть прочитан как текст или двоичные данные
  • Тип MIME: предоставляет информацию о формате файла. Браузеры используют тип MIME, а не расширение файла, чтобы определить, как обрабатывать URL.
  • FFmpeg: инструмент для преобразования медиафайлов между форматами
Теги:анимация на холсте canvas с помощью mediarecorder и ее сохрание|javascript,true

Комментариев больше нет

Осталось символов: 2048