수색…


비고

이 주제는 다양한 미디어 유형과 캔버스를 2D 인터페이스에서 사용하는 방법에 대해 다룹니다.

미디어 유형에는 일반 및 형식 특정 카테고리가 있습니다.

미디어 유형

  • 애니메이션
  • 비디오
  • 이미지
  • HD 이미지
  • 벡터 이미지
  • 애니메이션 이미지

미디어 형식

  • Jpg / Jpeg
  • PNG
  • GIF
  • SVG
  • M-JPEG

이미지

브라우저가 지원하는 다양한 이미지 형식이 있지만 브라우저가 지원하지는 않습니다. 특정 이미지 형식을 사용하려는 경우 Wiki 브라우저 및 지원되는 이미지 형식 을 사용하면 좋은 개요를 제공합니다.

가장 좋은 지원은 모든 주요 브라우저가 지원하는 3 가지 기본 형식 인 "jpeg", "png"및 "gif"입니다.

JPEG

JPEG 이미지는 사진과 같은 이미지에 가장 적합합니다. 차트, 다이어그램 및 텍스트에 잘 맞지 않습니다. JPEG 이미지는 투명도를 지원하지 않습니다.

Canvas는 canvas.toDataURLcanvas.toBlob 통해 JPEG 이미지를 출력 할 수 있으며 품질 설정을 제공합니다. JPEG는 투명도를 지원하지 않으므로 투명 픽셀은 최종 출력 JPG에 대해 검정색과 혼합됩니다. 결과 이미지는 캔버스의 완벽한 사본이 아닙니다.

위키피디아의 JPEG

PNG

PNG 이미지는 최고 품질의 이미지이며 투명한 픽셀을위한 알파 채널을 포함 할 수도 있습니다. 이미지 데이터는 압축되지만 JPG 이미지와 같은 아티팩트는 생성되지 않습니다.

무손실 압축 및 알파 채널 지원으로 인해 게임, UI 구성 요소 이미지, 차트, 다이어그램, 텍스트에 PNG가 사용됩니다. 이미지와 같은 사진과 사진의 경우 파일 크기가 JPEG보다 훨씬 클 수 있습니다. .

PNG 형식은 브라우저 지원이 제한되지만 애니메이션 지원도 제공하며 캔버스에서 사용할 애니메이션에 대한 액세스는 Javascript API 및 라이브러리를 통해서만 수행 할 수 있습니다.

캔버스는 canvas.toDataURLcanvas.toBlob 를 통해 PNG 이미지를 인코딩하는 데 사용할 수 있지만 출력 형식은 압축 된 32 비트 RGBA로 제한됩니다. PNG는 캔버스의 완전한 픽셀 사본을 제공합니다.

위키피디아의 PNG

GIF

GIF는 짧은 애니메이션에 사용되지만 고품질의 차트, 다이어그램 및 이미지와 같은 텍스트를 제공하는 데 사용할 수도 있습니다. GIF는 매우 제한된 색상을 지원하며 프레임 당 최대 256 색을 지원합니다. 식칼 이미지 처리로 GIF 이미지는 놀라 울 정도로 좋은 결과를 얻을 수 있습니다. 온 / 오프로 제한되지만 GIF는 투명성도 제공합니다.

PNG와 마찬가지로 GIF 애니메이션은 캔버스에서 직접 사용할 수 없으므로 액세스하려면 Javascript API 또는 라이브러리가 필요합니다. 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은 검정색을 포함하는 1x1 픽셀 GIF 이미지입니다.

로딩 및 오류에 대한 설명

src 속성이 설정되면 이미지가로드되기 시작합니다. 로딩은 syncriouse이지만 함수 또는 코드가 종료되거나 반환 될 때까지 onload 이벤트는 호출되지 않습니다.

페이지에서 이미지를 가져 오면 (예 : document.getElementById("myImage") ) src 가 설정되어 있거나로드되지 않았을 수 있습니다. 완료되면 true 가 될 HTMLImageElement.complete 로 이미지의 상태를 확인할 수 있습니다. 이것은 이미지가로드되었음을 의미하지 않으며, 이미지가로드되었음을 의미하지는 않습니다.

  • 짐을 실은
  • 오류가있었습니다
  • src 속성이 설정되지 않았으며 빈 문자열 ( "" )과 같습니다.

이미지가 신뢰할 수없는 소스에서 왔고 다양한 이유로 액세스 할 수없는 경우 오류 이벤트가 생성됩니다. 이 경우 이미지가 깨진 상태가됩니다. 캔버스에 그릴 시도하면 다음 오류가 throw됩니다.

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

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 이미지에는 래스터 이미지보다 많은 제한 사항이 있습니다.

  • 보안을 위해 HTMLImageElement ( <img> )에서 참조하는 SVG 이미지에서 외부 내용을로드 할 수 없습니다.
    외부 스타일 시트가 없으며 SVGImage ( <image/> ) 요소에서 참조 된 외부 이미지가 없으며 xlink:href 속성 ( <use xlink:href="anImage.SVG#anElement"/> ) 또는 funcIRI ( url() ) 속성 메소드 등.
    또한 주 문서에 추가 된 스타일 시트는 HTMLImage 요소에서 참조 된 SVG 문서에 영향을 미치지 않습니다.
    마지막으로 SVG 이미지 내에서 스크립트가 실행되지 않습니다.
    해결 방법 : HTMLImage 요소를 참조하기 전에 SVG 자체에 모든 외부 요소를 추가해야합니다. (이미지 또는 글꼴의 경우 외부 리소스의 dataURI 버전을 추가해야 함).

  • 루트 요소 ( <svg> )는 width 및 height 속성을 절대 값으로 설정해야합니다.
    상대 길이 (예 : % )를 사용하면 브라우저는 상대적인 길이를 알 수 없습니다. 일부 브라우저 (Blink)는 추측을 시도하지만 대부분 사용자의 이미지를 무시하고 경고없이 아무 것도 그릴 수 없습니다.

  • 일부 브라우저는 SVG 이미지가 그려 질 때 캔버스오염시킵니다 .
    특히 인터넷 익스플로러 <어떤 경우 든 Edge <foreignObject> , <foreignObject> 가 SVG 이미지에있는 경우 Safari 9.

캔버스에서 기본 로딩 및 비디오 재생.

캔버스를 사용하여 다양한 소스의 비디오를 표시 할 수 있습니다. 이 예는 비디오를 파일 리소스로로드하고 표시하고 화면 재생 / 일시 중지 토글을 클릭하는 방법을 보여줍니다.

이 stackoverflow 자기 대답 질문 HTML5 캔버스 태그를 사용하여 비디오를 표시하려면 어떻게 행동에 다음 예제 코드를 보여줍니다.

그냥 이미지

비디오는 캔버스에 관한 한 이미지에 불과합니다. 이미지처럼 그릴 수 있습니다. 차이점은 동영상 재생 및 사운드가 있습니다.

캔버스 및 기본 설정 가져 오기

// 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);
}

표시 중

비디오가 캔버스에서 재생되지 않습니다. 새로운 프레임이 생길 때마다 그려야합니다. 정확한 프레임 속도를 알기가 어려우며 발생 시점이 60fps 인 것처럼 비디오를 표시하는 것이 가장 좋습니다. 프레임 속도가 낮 으면 w는 같은 프레임을 두 번 렌더링합니다. 프레임 속도가 더 높으면 여분의 프레임을 볼 필요가 없으므로 무시합니다.

비디오 요소는 이미지 요소 일 뿐이며 어떤 이미지처럼 그릴 수 있습니다. 비디오를 스케일링, 회전, 팬, 미러링, 페이드 아웃, 클립핑 및 파트 만 표시, 글로벌 합성 모드로 두 번 다시 그립니다. 밝게, 화면 등의 FX를 추가 할 수 있습니다.

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 을 사용하여 canvas.toDataURL 을 스냅하고 다운로드 할 수 있습니다. (비디오가 신뢰할 수있는 출처에서 온 것이고 캔버스에 얼룩이없는 경우에만 해당).

비디오에 사운드가 있으면 재생하면 사운드도 재생됩니다.

해피 비디오.

캔버스 캡처 및 webM 비디오로 저장

캔버스 프레임에서 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을 50fps로 캡처 할 수 있음).

이것은 Wammy 에서 영감을 얻었 으나 방법론을 따라갈 때 인코딩으로 완전히 다시 작성되어 캡처하는 동안 필요한 메모리를 크게 줄입니다. 30 초 이상 데이터를 캡처하고 알고리즘을 처리 할 수 ​​있습니다.

참고 프레임은 WebP 이미지로 인코딩됩니다. Chrome 만이 webP 캔버스 인코딩을 지원합니다. 다른 브라우저 (Firefox 및 Edge)의 경우 Libwebp Javascript 와 같은 타사 WebP 인코더를 사용해야합니다. Javascript를 통한 WebP 이미지 인코딩은 느립니다. (허용되는 경우 원시 웹 이미지 지원 추가 포함).

Whammy에서 영감을 얻은 WebM 인코더 : 실시간 자바 스크립트 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