Поиск…


замечания

В этом разделе рассматриваются различные типы носителей и способы их использования с холстом в 2D-интерфейсе.

Типы носителей имеют общие и специфичные для формата категории

Типы носителей

  • Анимации
  • Видео
  • Изображений
  • HD-изображения
  • Векторные изображения
  • Анимированные изображения

Форматы мультимедиа

  • Jpg / Jpeg
  • Png
  • Gif
  • SVG
  • M-JPEG
  • WebM
  • WebP

Изображений

Существует широкий спектр форматов изображений, поддерживаемых браузерами, хотя ни один браузер не поддерживает их всех. Если у вас есть определенные форматы изображений, которые вы хотите использовать Wiki-браузеры, а поддерживаемые форматы изображений обеспечивают хороший обзор.

Лучшая поддержка для 3 основных форматов: «jpeg», «png» и «gif» со всеми основными браузерами, поддерживающими поддержку.

JPEG

Изображения JPEG лучше всего подходят для фотографий и фотографий, похожих на изображения. Они не очень хорошо предоставляют себе диаграммы, диаграммы и текст. Изображения JPEG не поддерживают прозрачность.

Canvas может выводить изображения JPEG через canvas.toDataURL и canvas.toBlob и обеспечивает настройку качества. Поскольку JPEG не поддерживает прозрачность, прозрачные пиксели будут смешиваться с черным для окончательного вывода JPG. Полученное изображение не будет идеальной копией холста.

JPEG в Википедии

PNG

PNG Image - это изображения самого высокого качества, а также альфа-канал для прозрачных пикселей. Данные изображения сжаты, но не создают артефакты, такие как изображения в формате JPG.

Из-за сжатия без потерь и поддержки альфа-канала PNG используются для игр, изображений компонентов, диаграмм, диаграмм, текста. При использовании их для фотографий и фотографий, как изображения, их размер файла может быть намного больше, чем JPEG. ,

Формат PNG также поддерживает анимацию, хотя поддержка браузера ограничена, а доступ к анимации для использования на холсте может быть выполнен только с помощью API и библиотек JavaScript

Холст можно использовать для кодирования изображений PNG через canvas.toDataURL и canvas.toBlob хотя выходной формат ограничен сжатым 32Bit RGBA. PNG предоставит идеальную копию холста.

PNG в Википедии

GIF

GIF используются для коротких анимаций, но также могут использоваться для обеспечения высококачественных диаграмм, диаграмм и текстовых изображений. GIF имеют очень ограниченную поддержку цвета с 256 цветами на кадр. С обработкой изображений графических изображений gif можно получить удивительно хорошие результаты, особенно при анимации. Gif также обеспечивают прозрачность, хотя это ограничивается включением или выключением

AS с PNG, анимации GIF напрямую не доступны для использования на холсте, и для получения доступа вам понадобится API или библиотека Javascript. GIF нельзя сохранить с помощью холста, и для этого потребуется API и библиотека.

GIF в Википедии

Загрузка и отображение изображения

Чтобы загрузить изображение и поместить его на холст

var image = new Image();  // see note on creating an image
image.src = "imageURL";
image.onload = function(){
    ctx.drawImage(this,0,0);
}

Создание изображения

Существует несколько способов создания изображения

  • new Image()
  • document.createElement("img")
  • <img src = 'imageUrl' id='myImage'> Как часть тела HTML и извлекается с помощью document.getElementById('myImage')

Изображение представляет собой HTMLImageElement

Свойство Image.src

Изображение src может быть любым допустимым URL изображения или закодированным dataURL. См. Примечания к этому разделу для получения дополнительной информации о форматах изображений и поддержке.

  • image.src = "http://my.domain.com/images/myImage.jpg"
  • image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=" *

* DataURL - это изображение размером в 1 пиксель gif, содержащее черный

Замечания по загрузке и ошибкам

Изображение начнет загружаться, когда будет установлено его свойство src. Загрузка является синхронизацией, но событие onload не будет вызываться до тех пор, пока функция или код не выйдет / не вернется.

Если вы получаете изображение со страницы (например document.getElementById("myImage") ), и его src установлен, он может быть или не быть загружен. Вы можете проверить статус изображения с помощью HTMLImageElement.complete который будет true если он будет завершен. Это не означает, что изображение загружено, это означает, что оно имеет либо

  • нагруженный
  • Там была ошибка
  • Свойство src не было установлено и равно пустой String ""

Если изображение из ненадежного источника и может быть недоступно по разным причинам, оно генерирует событие ошибки. Когда это произойдет, изображение будет в неисправном состоянии. Если вы затем попытаетесь нарисовать его на холсте, это приведет к следующей ошибке

Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.

image.onerror = myImgErrorHandler событие image.onerror = myImgErrorHandler вы можете предпринять соответствующие действия для предотвращения ошибок.

Рисование изображения svg

Чтобы нарисовать векторное изображение SVG, операция не отличается от растрового изображения:
Сначала вам нужно загрузить изображение SVG в элемент HTMLImage, а затем использовать метод drawImage() .

var image = new Image();
image.onload = function(){
    ctx.drawImage(this, 0,0);
}
image.src = "someFile.SVG";

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

Однако изображения SVG имеют больше ограничений, чем растровые изображения.

  • В целях безопасности внешний контент не может быть загружен из изображения SVG, указанного в элементе HTMLImageElement ( <img> )
    Нет внешней таблицы стилей, никакого внешнего изображения, на которое ссылаются элементы SVGImage ( <image/> ), никакого внешнего фильтра или элемента, связанного xlink:href ( <use xlink:href="anImage.SVG#anElement"/> ) или funcIRI ( url() ) и т. д.
    Кроме того, таблицы стилей, добавленные в основном документе, не будут влиять на документ SVG после ссылки в элементе HTMLImage.
    Наконец, сценарий не будет выполнен внутри SVG-изображения.
    Обход проблемы: вам нужно будет добавить все внешние элементы внутри самого SVG, прежде чем ссылаться на элемент HTMLImage. (для изображений или шрифтов вам необходимо добавить версию ваших внешних ресурсов dataURI).

  • Корневой элемент ( <svg> ) должен иметь атрибуты ширины и высоты, установленные на абсолютное значение.
    Если вы использовали относительную длину (например, % ), браузер не сможет узнать, что это относительно. Некоторые браузеры (Blink) попытаются сделать предположение, но большинство просто проигнорирует ваш образ и ничего не рисует без предупреждения.

  • Некоторые браузеры будут обманывать холст, когда на него нарисовано изображение SVG.
    В частности, Internet Explorer <Edge в любом случае и Safari 9, когда <foreignObject> присутствует в изображении SVG.

Основная загрузка и воспроизведение видео на холсте.

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

Ответ на этот вопрос stackoverflow сам. Как отобразить видео с помощью HTML5 canvas tag показывает следующий пример кода в действии.

Просто изображение

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

Получить холст и основные настройки

// It is assumed you know how to add a canvas and correctly size it.
var canvas = document.getElementById("myCanvas"); // get the canvas from the page
var ctx = canvas.getContext("2d");
var videoContainer; // object to hold video and associated info

Создание и загрузка видео

var video = document.createElement("video"); // create a video element
video.src = "urlOffVideo.webm"; 
// the video will now begin to load.
// As some additional info is needed we will place the video in a
// containing object for convenience
video.autoPlay = false; // ensure that the video does not auto play
video.loop = true; // set the video to loop.
videoContainer = {  // we will add properties as needed
     video : video,
     ready : false,   
};

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

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

video.oncanplay = readyToPlayVideo; // set the event to the play function that 
                                  // can be found below

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

video.oncanplaythrough = readyToPlayVideo; // set the event to the play function that
                                         // can be found below

Используйте только одно из событий canPlay не для обоих.

Событие может воспроизводиться (эквивалентно загрузке изображения)

function readyToPlayVideo(event){ // this is a referance to the video
    // the video may not match the canvas size so find a scale to fit
    videoContainer.scale = Math.min(
                         canvas.width / this.videoWidth, 
                         canvas.height / this.videoHeight); 
    videoContainer.ready = true;
    // the video can be played so hand it off to the display function
    requestAnimationFrame(undateCanvas);
}

Отображение

Видео не будет воспроизводиться на холсте. Вам нужно нарисовать его для каждого нового кадра. Поскольку трудно узнать точную частоту кадров, и когда они возникают, наилучший способ - отобразить видео, как будто он работает со скоростью 60 кадров в секунду. Если частота кадров ниже, то w просто визуализирует тот же кадр дважды. Если частота кадров выше, то нет ничего, что можно было бы увидеть, чтобы увидеть дополнительные рамки, поэтому мы просто игнорируем их.

Видеоэлемент является только элементом изображения и может быть нарисован как любое изображение, вы можете масштабировать, поворачивать, панорамировать видео, зеркально его отображать, выцветать, кликать и отображать только части, рисовать его дважды во второй раз с помощью глобального композитного режима добавить FX как lighten, screen и т. д.

function updateCanvas(){
    ctx.clearRect(0,0,canvas.width,canvas.height); // Though not always needed 
                                                     // you may get bad pixels from 
                                                     // previous videos so clear to be
                                                     // safe
    // only draw if loaded and ready
    if(videoContainer !== undefined && videoContainer.ready){ 
        // find the top left of the video on the canvas
        var scale = videoContainer.scale;
        var vidH = videoContainer.video.videoHeight;
        var vidW = videoContainer.video.videoWidth;
        var top = canvas.height / 2 - (vidH /2 ) * scale;
        var left = canvas.width / 2 - (vidW /2 ) * scale;
        // now just draw the video the correct size
        ctx.drawImage(videoContainer.video, left, top, vidW * scale, vidH * scale);
        if(videoContainer.video.paused){ // if not playing show the paused screen 
            drawPayIcon();
        }
    }
    // all done for display 
    // request the next frame in 1/60th of a second
    requestAnimationFrame(updateCanvas);
}

Основное управление паузой воспроизведения

Теперь мы загрузили видео и отобразили все, что нам нужно, это управление воспроизведением. Мы сделаем это как щелчок по нажатию на экране. Когда видео воспроизводится и пользователь нажимает видео, он приостанавливается. При приостановке воспроизведения нажмите. Мы добавим функцию, чтобы затемнить видео и нарисовать значок воспроизведения (треугольник)

function drawPayIcon(){
     ctx.fillStyle = "black";  // darken display
     ctx.globalAlpha = 0.5;
     ctx.fillRect(0,0,canvas.width,canvas.height);
     ctx.fillStyle = "#DDD"; // colour of play icon
     ctx.globalAlpha = 0.75; // partly transparent
     ctx.beginPath(); // create the path for the icon
     var size = (canvas.height / 2) * 0.5;  // the size of the icon
     ctx.moveTo(canvas.width/2 + size/2, canvas.height / 2); // start at the pointy end
     ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 + size);
     ctx.lineTo(canvas.width/2 - size/2, canvas.height / 2 - size);
     ctx.closePath();
     ctx.fill();
     ctx.globalAlpha = 1; // restore alpha
}    

Теперь событие паузы воспроизведения

function playPauseClick(){
     if(videoContainer !== undefined && videoContainer.ready){
          if(videoContainer.video.paused){                                 
                videoContainer.video.play();
          }else{
                videoContainer.video.pause();
          }
     }
}
// register the event
canvas.addEventListener("click",playPauseClick);

Резюме

Воспроизведение видео очень просто с использованием холста, добавление эффекта в реальном времени также легко. Однако есть некоторые ограничения в форматах, как вы можете играть и искать. MDN HTMLMediaElement - это место, где можно получить полный доступ к видеообъекту.

После того, как изображение было нарисовано на холсте, вы можете использовать ctx.getImageData для доступа к содержащимся в нем пикселам. Или вы можете использовать canvas.toDataURL чтобы привязать неподвижное и загрузить его. (Только если видео получено из надежного источника и не загрязняет полотно).

Обратите внимание, что при воспроизведении звука звук воспроизводится, а также воспроизводит звук.

Счастливое видео.

Захват холста и сохранение в виде веб-видео

Создание видео WebM из фреймов холста и воспроизведение в холсте, загрузка или загрузка.

Пример захвата и воспроизведения холста

name = "CanvasCapture"; // Placed into the Mux and Write Application Name fields of the WebM header
quality = 0.7; // good quality 1 Best < 0.7 ok to poor
fps = 30; // I have tried all sorts of frame rates and all seem to work
          // Do some test to workout what your machine can handle as there
          // is a lot of variation between machines.
var video = new Groover.Video(fps,quality,name)
function capture(){
    if(video.timecode < 5000){ // 5 seconds
         setTimeout(capture,video.frameDelay);             
    }else{
         var videoElement = document.createElement("video");
         videoElement.src = URL.createObjectURL(video.toBlob());
         document.body.appendChild(videoElement);
         video = undefined; // DeReference as it is memory hungry.
         return;
    }
    // first frame sets the video size
    video.addFrame(canvas); // Add current canvas frame
}
capture(); // start capture

Вместо того, чтобы прилагать огромные усилия только для того, чтобы быть отвергнутым, это быстрая вставка, чтобы увидеть, приемлемо ли это. Дает полную информацию, если будет принято. Также включают дополнительные возможности захвата для повышения скорости захвата HD (удалены из этой версии, можно захватить HD 1080 со скоростью 50 кадров в секунду на хороших машинах).

Это было вдохновлено Wammy, но это полная переписывание с кодировкой, поскольку вы идете методологию, значительно уменьшая объем памяти, необходимый во время захвата. Может захватывать более 30 секунд лучшие данные, обрабатывая алгоритмы.

Примечание кадры кодируют в WebP изображений. Только Chrome поддерживает кодирование canvas webP. Для других браузеров (Firefox и Edge) вам нужно будет использовать сторонний веб-кодировщик, такой как Libwebp Javascript Кодирование изображений WebP через Javascript происходит медленно. (будет включать в себя добавление поддержки изображений с поддержкой веб-страниц, если они будут приняты).

WebM-кодировщик, вдохновленный Whammy: в реальном времени Javascript WebM

var Groover = (function(){
    // ensure webp is supported 
    function canEncode(){
        var canvas = document.createElement("canvas");
        canvas.width = 8;
        canvas.height = 8;
        return canvas.toDataURL("image/webp",0.1).indexOf("image/webp") > -1;
    }
    if(!canEncode()){
        return undefined;
    }    
    var webmData = null;
    var clusterTimecode = 0;
    var clusterCounter = 0;
    var CLUSTER_MAX_DURATION = 30000;
    var frameNumber = 0;
    var width;
    var height;
    var frameDelay;
    var quality;
    var name;
    const videoMimeType = "video/webm"; // the only one.
    const frameMimeType = 'image/webp'; // can be no other
    const S = String.fromCharCode;
    const dataTypes = {
        object : function(data){ return toBlob(data);},
        number : function(data){ return stream.num(data);},
        string : function(data){ return stream.str(data);},
        array  : function(data){ return data;}, 
        double2Str : function(num){
            var c = new Uint8Array((new Float64Array([num])).buffer);
            return S(c[7]) + S(c[6]) + S(c[5]) + S(c[4]) + S(c[3]) + S(c[2]) + S(c[1]) + S(c[0]);
        }
    };    
   
    const stream = {
        num : function(num){ // writes int
            var parts = [];
            while(num > 0){ parts.push(num & 0xff); num = num >> 8; }
            return new Uint8Array(parts.reverse());
        },
        str : function(str){ // writes string
            var i, len, arr;
            len = str.length;
            arr = new Uint8Array(len);
            for(i = 0; i < len; i++){arr[i] = str.charCodeAt(i);}
            return arr;
        },
        compInt : function(num){ // could not find full details so bit of a guess
            if(num < 128){       // number is prefixed with a bit (1000 is on byte 0100 two, 0010 three and so on)
                num += 0x80;
                return new Uint8Array([num]);
            }else
            if(num < 0x4000){
                num += 0x4000;
                return new Uint8Array([num>>8, num])
            }else
            if(num < 0x200000){
                num += 0x200000;
                return new Uint8Array([num>>16, num>>8, num])
            }else
            if(num < 0x10000000){
                num += 0x10000000;
                return new Uint8Array([num>>24, num>>16, num>>8, num])
            }            
        }
    }
    const ids = { // header names and values
        videoData          : 0x1a45dfa3, 
        Version            : 0x4286,
        ReadVersion        : 0x42f7,
        MaxIDLength        : 0x42f2,
        MaxSizeLength      : 0x42f3,
        DocType            : 0x4282,
        DocTypeVersion     : 0x4287,
        DocTypeReadVersion : 0x4285,
        Segment            : 0x18538067,
        Info               : 0x1549a966,
        TimecodeScale      : 0x2ad7b1,
        MuxingApp          : 0x4d80,
        WritingApp         : 0x5741,
        Duration           : 0x4489,
        Tracks             : 0x1654ae6b,
        TrackEntry         : 0xae,
        TrackNumber        : 0xd7,
        TrackUID           : 0x63c5,
        FlagLacing         : 0x9c,
        Language           : 0x22b59c,
        CodecID            : 0x86,
        CodecName          : 0x258688,
        TrackType          : 0x83,
        Video              : 0xe0,
        PixelWidth         : 0xb0,
        PixelHeight        : 0xba,
        Cluster            : 0x1f43b675,
        Timecode           : 0xe7,
        Frame              : 0xa3,
        Keyframe           : 0x9d012a,
        FrameBlock         : 0x81,
    };
    const keyframeD64Header = '\x9d\x01\x2a'; //VP8 keyframe header 0x9d012a
    const videoDataPos = 1; // data pos of frame data header
    const defaultDelay = dataTypes.double2Str(1000/25);
    const header = [  // structure of webM header/chunks what ever they are called.
        ids.videoData,[
            ids.Version, 1,
            ids.ReadVersion, 1,
            ids.MaxIDLength, 4,
            ids.MaxSizeLength, 8,
            ids.DocType, 'webm',
            ids.DocTypeVersion, 2,
            ids.DocTypeReadVersion, 2
        ],
        ids.Segment, [
            ids.Info, [
                ids.TimecodeScale, 1000000,
                ids.MuxingApp, 'Groover',
                ids.WritingApp, 'Groover',
                ids.Duration, 0
            ],
            ids.Tracks,[
                ids.TrackEntry,[
                    ids.TrackNumber, 1,
                    ids.TrackUID, 1,
                    ids.FlagLacing, 0,     // always o
                    ids.Language, 'und',   // undefined I think this means
                    ids.CodecID, 'V_VP8',  // These I think must not change
                    ids.CodecName, 'VP8',  // These I think must not change
                    ids.TrackType, 1,
                    ids.Video, [
                        ids.PixelWidth, 0,
                        ids.PixelHeight, 0
                    ]
                ]
            ]
        ]
    ];    
    function getHeader(){
        header[3][2][3] = name;
        header[3][2][5] = name;
        header[3][2][7] =  dataTypes.double2Str(frameDelay);
        header[3][3][1][15][1] =  width;
        header[3][3][1][15][3] =  height;
        function create(dat){
            var i,kv,data;
            data = [];
            for(i = 0; i < dat.length; i += 2){
                kv = {i : dat[i]};
                if(Array.isArray(dat[i + 1])){
                    kv.d = create(dat[i + 1]);
                }else{
                    kv.d = dat[i + 1];
                }
                data.push(kv);
            }
            return data;
        }
        return create(header);
    }
    function addCluster(){
        webmData[videoDataPos].d.push({ i: ids.Cluster,d: [{ i: ids.Timecode, d: Math.round(clusterTimecode)}]}); // Fixed bug with Round
        clusterCounter = 0;
    }
    function addFrame(frame){
        var VP8, kfS,riff;
        riff = getWebPChunks(atob(frame.toDataURL(frameMimeType, quality).slice(23)));
        VP8 = riff.RIFF[0].WEBP[0];
        kfS = VP8.indexOf(keyframeD64Header) + 3;
        frame = {
            width: ((VP8.charCodeAt(kfS + 1) << 8) | VP8.charCodeAt(kfS)) & 0x3FFF,
            height: ((VP8.charCodeAt(kfS + 3) << 8) | VP8.charCodeAt(kfS + 2)) & 0x3FFF,
            data: VP8,
            riff: riff
        };
        if(clusterCounter > CLUSTER_MAX_DURATION){
            addCluster();            
        }
        webmData[videoDataPos].d[webmData[videoDataPos].d.length-1].d.push({
            i: ids.Frame, 
            d: S(ids.FrameBlock) + S( Math.round(clusterCounter) >> 8) +  S( Math.round(clusterCounter) & 0xff) + S(128) + frame.data.slice(4),
        });
        clusterCounter += frameDelay;        
        clusterTimecode += frameDelay;
        webmData[videoDataPos].d[0].d[3].d = dataTypes.double2Str(clusterTimecode);
    }
    function startEncoding(){
        frameNumber = clusterCounter = clusterTimecode = 0;
        webmData  = getHeader();
        addCluster();
    }    
    function toBlob(vidData){
        var data,i,vData, len;
        vData = [];
        for(i = 0; i < vidData.length; i++){
            data = dataTypes[typeof vidData[i].d](vidData[i].d);
            len  = data.size || data.byteLength || data.length;
            vData.push(stream.num(vidData[i].i));
            vData.push(stream.compInt(len));
            vData.push(data)
        }
        return new Blob(vData, {type: videoMimeType});
    }
    function getWebPChunks(str){
        var offset, chunks, id, len, data;
        offset = 0;
        chunks = {};
        while (offset < str.length) {
            id = str.substr(offset, 4);
            // value will have top bit on (bit 32) so not simply a bitwise operation
            // Warning little endian (Will not work on big endian systems)
            len = new Uint32Array(
                new Uint8Array([
                    str.charCodeAt(offset + 7),
                    str.charCodeAt(offset + 6),
                    str.charCodeAt(offset + 5),
                    str.charCodeAt(offset + 4)
                ]).buffer)[0];
            id = str.substr(offset, 4);
            chunks[id] = chunks[id] === undefined ? [] : chunks[id];
            if (id === 'RIFF' || id === 'LIST') {
                chunks[id].push(getWebPChunks(str.substr(offset + 8, len)));
                offset += 8 + len;
            } else if (id === 'WEBP') {
                chunks[id].push(str.substr(offset + 8));
                break;
            } else {
                chunks[id].push(str.substr(offset + 4));
                break;
            }
        }
        return chunks;
    }
    function Encoder(fps, _quality = 0.8, _name = "Groover"){ 
        this.fps = fps;
        this.quality = quality = _quality;
        this.frameDelay = frameDelay = 1000 / fps;
        this.frame = 0;
        this.width = width = null;
        this.timecode = 0;
        this.name = name = _name;
    }
    Encoder.prototype = {
        addFrame : function(frame){
            if('canvas' in frame){
                frame = frame.canvas;    
            }
            if(width === null){
                this.width = width = frame.width,
                this.height = height = frame.height
                startEncoding();
            }else
            if(width !== frame.width || height !== frame.height){
                throw RangeError("Frame size error. Frames must be the same size.");
            }            
            addFrame(frame);   
            this.frame += 1;
            this.timecode = clusterTimecode;
        },        
        toBlob : function(){
            return toBlob(webmData);
        }
    }
    return {
        Video: Encoder,
    }
})()


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow