Превращение анимированного svg в видеофайл

Посмотрите видео здесь, чтобы увидеть, как это делается с Томасом!

Локальный SVG-рекордер

Запишите анимированный SVG в видеофайл, ничего не загружая.

Давайте воспользуемся современными API JavaScript браузера для копирования кадров SVG на холст и записи результата в видеофайл формата WebM.

Это будет сделано на статическом сайте, без серверной части.

Нет ни реальной загрузки SVG, ни загрузки видео.

схема ? SVG-превью -> canvas -> MediaRecorder

  • Локальный SVG-рекордер

    • Предварительный просмотр SVG
    • Копирование SVG на холст
    • Воспроизвести SVG
    • Копирование SVG на холст
    • Задайте цикл копирования в течение определенного времени
    • Запись на холст
    • Заключительная функция startRecord
    • Сохраните файл с помощью функции saveResult.
    • Идем дальше

Предварительный просмотр SVG

Давайте начнем с формы, которая позволит пользователям выбирать SVG и управлять конфигурацией записей.

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

Мы также не будем устанавливать правила CSS, поскольку это не то, что вам здесь нужно.

Вот основная HTML-страница:


<!DOCTYPE html>

<html lang="en">

    <head>

        <meta charset='utf-8'>

        <meta http-equiv='X-UA-Compatible' content='IE=edge'>

        <title>SVG Recorder</title>

        <meta name="description" content="Record an animated SVG and export it as WebM video">

        <meta name='viewport' content='width=device-width, initial-scale=1'>

    </head>

    <body>

        <h1>SVG Recorder</h1>

        <form>

            <label for="svg"><input id="svg" name="svg" type="file" accept="image/svg+xml" placeholder="Upload the SVG" required/></label>

            <button>Record</button>

        </form>

        <script src='main.js' defer></script>

    </body>

</html>

Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете видеть input типа file для выбора SVG и кнопку Record для начала записи.

Вот основной файл JavaScript main.js:


"use strict";

const form = document.querySelector('form');

form.onsubmit = function (event) {

    /**

     * @type {File}

     */

    const file = form.svg.files[0];

    // start recording

    startRecord(URL.createObjectURL(file))

        .then(blob => saveResult(file.name.replace(/.svgz?$/i, '.webm'), blob));

    // prevent form submit

    event.preventDefault();

    event.stopPropagation();

    return;

}

/**

 * Start recording a SVG from it URL

 * @param {URL} url The SVG URL

 * @returns {Promise<Blob>} The recoreded video blob

 */

function startRecord(url) {

    return new Promise(function(resolve, reject) {

        // TODO: implement

    });

}

/**

 * Creates a download button auto-clicked if the browser manage it

 * @param {string} name The recoreded video blob

 * @param {Blob} video The recoreded video blob

 */

function saveResult(name, video) {

    // TODO: implement

}

/**

 * Render the canvas with the given background and SVG

 * @param {CanvasRenderingContext2D} ctx The target canvas context

 * @param {HTMLImageElement} image The image component to render

 */

function render(ctx, image) {

    // TODO: implement

}

Вход в полноэкранный режим Выход из полноэкранного режима

В следующих шагах мы увидим, как реализовать содержимое обещания startRecord, функции saveResult и render.

Копирование SVG на холст

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

Мы сделаем это с помощью простого тега img и цикла JavaScript.

Воспроизведение SVG

Здесь мы начнем реализацию обещания startRecord, создав тег img и добавив его в элемент body для воспроизведения выбранного SVG-файла:


const image = document.createElement('img');

body.appendChild(image);

image.src = url;

Вход в полноэкранный режим Выход из полноэкранного режима

Копирование SVG на холст

Теперь нам нужно создать холст в startRecord и использовать функцию render для рендеринга изображения на холсте.

Нам также нужно изменить размер холста в соответствии с естественным размером изображения (обратите внимание, что SVG файл нуждается в атрибутах width и height в теге svg), как показано ниже:


const canvas = document.createElement('canvas'),

    ctx = canvas.getContext('2d');

image.onload = function() {

    canvas.width = image.naturalWidth;

    canvas.height = image.naturalHeight;

}

body.appendChild(canvas);

render(ctx, image);

Войти в полноэкранный режим Выйти из полноэкранного режима

Вот содержимое функции render, которая устанавливает SVG белый прямоугольник полного размера и копирует в него изображение:


function render(ctx, image) {

    ctx.fillStyle = 'white';

    ctx.rect(0, 0, image.naturalWidth, image.naturalWidth);

    ctx.fill();

    ctx.drawImage(image, 0, 0, image.naturalWidth, image.naturalWidth);

}

Вход в полноэкранный режим Выйти из полноэкранного режима

Установить цикл копирования длительностью

Теперь нам нужно вызвать функцию render в цикле, чтобы получить все кадры SVG.

Мы создадим пятисекундный цикл в startRecord и заменим вызов render на вызов requestAnimationFrame:


let initTime = null;

requestAnimationFrame(renderLoop);

/**

 * Loop rendering the canvas

 * @param {number} time The loop time

 */

function renderLoop(time) {

    render(ctx, image);

    if (initTime==null) {

        // First call

        initTime = time; 

    }

    else if (time - initTime > 5000) { // 5 seconds

        return;

    }

    requestAnimationFrame(renderLoop);

}

Вход в полноэкранный режим Выход из полноэкранного режима

Запись на холст

Из холста мы можем использовать метод captureStream для получения потока анимации.

Далее мы создадим MediaRecorder этого потока и массив для сохранения данных записи.

Мы разрешим обещание startRecord и удалим теги img и canvas, когда рекордер будет остановлен:


const chunks = [],

    stream = canvas.captureStream(30),

    recorder = new MediaRecorder(stream, { mimeType: "video/webm" });

recorder.ondataavailable = function(event) {

    const blob = event.data;

    if (blob && blob.size) { 

        chunks.push(blob); 

    }

};

recorder.onstop = function(event) {

    // remove temp components

    body.removeChild(image);

    body.removeChild(canvas);

    resolve(new Blob(chunks, { type: "video/webm" }));

};

Войти в полноэкранный режим Выход из полноэкранного режима

Мы также добавим вызовы методов MediaRecorder start и stop в функцию renderLoop:


function renderLoop(time) {

    render(ctx, image);

    if (initTime==null) {

        // First call

        recorder.start();

        initTime = time; 

    }

    else if (time - initTime > 5000) { // 5 seconds

        // stop recording after defined duration

        recorder.stop();

        return;

    }

    requestAnimationFrame(renderLoop);

}

Вход в полноэкранный режим Выход из полноэкранного режима

Заключительная функция startRecord.

Вот полная функция startRecord:


function startRecord(url) {

    return new Promise(function(resolve, reject) {

        // create image, canvas and recorder

        const image = document.createElement('img'),

            canvas = document.createElement('canvas'),

            ctx = canvas.getContext('2d'),

            chunks = [],

            stream = canvas.captureStream(30),

            recorder = new MediaRecorder(stream, { mimeType: "video/webm" });

        let initTime = null;

        image.onload = function() {

            canvas.width = image.naturalWidth;

            canvas.height = image.naturalHeight;

        }

        body.appendChild(image);

        body.appendChild(canvas);

        recorder.ondataavailable = function(event) {

            const blob = event.data;

            if (blob && blob.size) { 

                chunks.push(blob); 

            }

        };

        recorder.onstop = function(event) {

            // remove temp components

            body.removeChild(image);

            body.removeChild(canvas);

            resolve(new Blob(chunks, { type: "video/webm" }));

        };

        image.src = url;

        requestAnimationFrame(renderLoop);

        /**

         * Loop rendering the canvas

         * @param {number} time The loop time

         */

        function renderLoop(time) {

            render(ctx, image);

            if (initTime==null) {

                // First call

                recorder.start();

                initTime = time; 

            }

            else if (time - initTime > 5000) { // 5 seconds

                // stop recording after defined duration

                recorder.stop();

                return;

            }

            requestAnimationFrame(renderLoop);

        }

    });

}

Вход в полноэкранный режим Выход из полноэкранного режима

Сохраните файл с помощью функции saveResult.

Теперь, когда SVG записано как WebM видео, мы можем сохранить его в файл с помощью функции saveResult:


function saveResult(name, video) {

    const generatedFile = new File([new Blob([blob], {type: 'application/octet-stream'})], name);

    const a = document.createElement('a');

    a.download = generatedFile.name;

    a.href = URL.createObjectURL(generatedFile);

    a.dataset.downloadurl = [generatedFile.type, a.download, a.href].join(':');

    const mouseEvent = document.createEvent('MouseEvents');

    mouseEvent.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);

    a.dispatchEvent(mouseEvent);

}

Вход в полноэкранный режим Выход из полноэкранного режима

Идем дальше

Чтобы пойти дальше, мы можем добавить некоторые функции:

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

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

Мы можем связать их, чтобы сохранить исходное соотношение.

  • определить частоту кадров записи.

  • определить продолжительность записи.

Она может быть предварительно заполнена рассчитанной длительностью из SVG-анимации.

  • определить цвет фона холста.

  • определить видеокодек, определив те, которыми управляет браузер.

  • проверить вводимые данные формы перед записью

  • добавить некоторые CSS

Ознакомиться с текущей версией проекта можно на сайте: https://lenra-io.github.io/svg-recorder/.

Помогите нам улучшить этот проект на Github

Оцените статью
Procodings.ru
Добавить комментарий