Szukaj…
Uwagi
W tym temacie omówiono różne typy mediów i sposoby ich użycia z płótnem w interfejsie 2D.
Typy mediów mają kategorie ogólne i specyficzne dla formatu
Rodzaje mediów
- Animacje
- Wideo
- Zdjęcia
- Obrazy HD
- Grafika wektorowa
- Animowane obrazy
Formaty mediów
- Jpg / Jpeg
- Png
- Gif
- SVG
- M-JPEG
- Webm
- Webp
Zdjęcia
Istnieje wiele różnych formatów obrazów obsługiwanych przez przeglądarki, ale żadna przeglądarka nie obsługuje ich wszystkich. Jeśli masz określone formaty obrazów, chcesz korzystać z Przeglądarek Wiki, a obsługiwane formaty obrazów zapewniają dobry przegląd.
Najlepsze wsparcie jest dla 3 głównych formatów, „jpeg”, „png” i „gif” ze wszystkimi głównymi przeglądarkami zapewniającymi wsparcie.
JPEG
Obrazy JPEG najlepiej nadają się do zdjęć i obrazów podobnych do zdjęć. Nie pożyczają sobie dobrze wykresów, diagramów i tekstu. Obrazy JPEG nie obsługują przezroczystości.
Canvas może wyprowadzać obrazy JPEG przez canvas.toDataURL
i canvas.toBlob
i zapewnia ustawienie jakości. Ponieważ JPEG nie obsługuje przezroczystości, wszystkie przezroczyste piksele zostaną zmieszane z czernią dla ostatecznego wyjścia JPG. Powstały obraz nie będzie idealną kopią płótna.
PNG
Obraz PNG to obrazy najwyższej jakości i mogą również zawierać kanał alfa dla przezroczystych pikseli. Dane obrazu są kompresowane, ale nie tworzą artefaktów, takich jak obrazy JPG.
Ze względu na bezstratną kompresję i obsługę kanału alfa pliki PNG są używane do gier, obrazów elementów interfejsu użytkownika, wykresów, diagramów, tekstu. Gdy używasz ich do zdjęć i obrazów podobnych do zdjęć, ich rozmiar pliku może być znacznie większy niż JPEG. .
Format PNG zapewnia również obsługę animacji, chociaż obsługa przeglądarki jest ograniczona, a dostęp do animacji do użytku na kanwie można uzyskać tylko za pośrednictwem interfejsów API i bibliotek JavaScript
Canvas może być używany do kodowania obrazów PNG przez canvas.toDataURL
i canvas.toBlob
chociaż format wyjściowy jest ograniczony do skompresowanego 32-bitowego RGBA. PNG zapewni idealną pikselową kopię płótna.
GIF
Pliki GIF są używane do krótkich animacji, ale można ich także używać do tworzenia wysokiej jakości wykresów, diagramów i obrazów podobnych do tekstu. Pliki GIF mają bardzo ograniczoną obsługę kolorów, maksymalnie 256 kolorów na ramkę. Dzięki przetwarzaniu obrazu tasakiem obrazy gif mogą dawać zaskakująco dobre wyniki, szczególnie gdy są animowane. Gify zapewniają również przezroczystość, chociaż jest to ograniczone do włączania lub wyłączania
Podobnie jak w przypadku PNG, animacje GIF nie są bezpośrednio dostępne do użycia na płótnie i będziesz potrzebować JavaScript API lub biblioteki, aby uzyskać dostęp. GIF nie może być zapisany przez płótno i wymaga do tego API lub biblioteki.
Ładowanie i wyświetlanie obrazu
Aby załadować obraz i umieścić go na płótnie
var image = new Image(); // see note on creating an image
image.src = "imageURL";
image.onload = function(){
ctx.drawImage(this,0,0);
}
Tworzenie obrazu
Istnieje kilka sposobów tworzenia obrazu
-
new Image()
-
document.createElement("img")
-
<img src = 'imageUrl' id='myImage'>
Jako część treści HTML i odzyskane za pomocądocument.getElementById('myImage')
Obraz jest HTMLImageElement
Właściwość Image.src
Obraz src
może być dowolnym poprawnym adresem URL obrazu lub zakodowanym adresem danychURL. Aby uzyskać więcej informacji na temat formatów obrazów i obsługi, zobacz Uwagi do tego tematu.
-
image.src = "http://my.domain.com/images/myImage.jpg"
-
image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="
*
* DataURL to obraz gif 1 na 1 piksel zawierający czerń
Uwagi na temat ładowania i błędów
Ładowanie obrazu rozpocznie się po ustawieniu jego właściwości src. Ładowanie jest zsynchronizowane, ale zdarzenie onload
nie zostanie wywołane, dopóki funkcja lub kod nie wyjdzie / nie zwróci.
Jeśli otrzymasz obraz ze strony (na przykład document.getElementById("myImage")
) i ustawiono src
może on zostać załadowany lub nie. Możesz sprawdzić status obrazu za pomocą HTMLImageElement.complete
co będzie true
jeśli zostanie ukończone. Nie oznacza to, że obraz został załadowany, to znaczy, że ma albo
- załadowany
- Wystąpił błąd
- Właściwość src nie została ustawiona i jest równa pustemu ciągowi
""
Jeśli obraz pochodzi z niewiarygodnego źródła i może nie być dostępny z różnych powodów, wygeneruje zdarzenie błędu. Kiedy to nastąpi, obraz będzie w stanie zepsutym. Jeśli następnie spróbujesz narysować go na płótnie, pojawi się następujący błąd
Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.
image.onerror = myImgErrorHandler
zdarzenie image.onerror = myImgErrorHandler
, możesz podjąć odpowiednie działania, aby zapobiec błędom.
Rysowanie obrazu SVG
Aby narysować wektorowy obraz SVG, operacja nie różni się od obrazu rastrowego:
Najpierw musisz załadować obraz SVG do elementu HTMLImage, a następnie użyć metody drawImage()
.
var image = new Image();
image.onload = function(){
ctx.drawImage(this, 0,0);
}
image.src = "someFile.SVG";
Obrazy SVG mają pewne zalety w stosunku do obrazów rastrowych, ponieważ nie stracisz jakości, bez względu na skalę, którą narysujesz na płótnie. Ale uwaga, może być nieco wolniejsza niż rysowanie obrazu rastrowego.
Jednak obrazy SVG mają więcej ograniczeń niż obrazy rastrowe.
Ze względów bezpieczeństwa nie można załadować żadnej zawartości zewnętrznej z obrazu SVG, do którego odwołuje się HTMLImageElement (
<img>
)
Brak zewnętrznego arkusza stylów, brak zewnętrznego obrazu przywoływanego w elementach SVGImage (<image/>
), brak zewnętrznego filtra lub elementu połączonegoxlink:href
(<use xlink:href="anImage.SVG#anElement"/>
) lub funcIRI metoda atrybutu (url()
) itp.
Ponadto arkusze stylów dołączone w dokumencie głównym nie będą miały żadnego wpływu na dokument SVG po odwołaniu do niego w elemencie HTMLImage.
Wreszcie, żaden obraz nie zostanie wykonany wewnątrz obrazu SVG.
Obejście: Przed odwołaniem się do elementu HTMLImage musisz dołączyć wszystkie elementy zewnętrzne do samego SVG. (w przypadku obrazów lub czcionek należy dołączyć wersję dataURI zasobów zewnętrznych).Element główny (
<svg>
) musi mieć ustawione atrybuty szerokości i wysokości na wartość bezwzględną.
Jeśli użyjesz długości względnej (np.%
), Przeglądarka nie będzie w stanie wiedzieć, co to jest względna. Niektóre przeglądarki (Blink) spróbują zgadnąć, ale większość po prostu zignoruje Twój obraz i nie narysuje niczego bez ostrzeżenia.Niektóre przeglądarki skazą płótno po narysowaniu obrazu SVG.
W szczególności Internet Explorer <Edge w każdym przypadku i Safari 9, kiedy<foreignObject>
jest obecny na obrazie SVG.
Podstawowe ładowanie i odtwarzanie wideo na płótnie.
Płótno może służyć do wyświetlania wideo z różnych źródeł. Ten przykład pokazuje, jak załadować film jako zasób pliku, wyświetlić go i dodać proste kliknięcie przełącznika odtwarzania / pauzy na ekranie.
To pytanie z odpowiedzią na pytanie stackoverflow Jak wyświetlić wideo przy użyciu tagu canvas HTML5 pokazuje następujący przykładowy kod w akcji.
Po prostu obraz
W przypadku płótna wideo jest tylko obrazem. Możesz narysować go jak dowolny obraz. Różnica polega na tym, że wideo może być odtwarzane i ma dźwięk.
Uzyskaj płótno i podstawową konfigurację
// 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
Tworzenie i ładowanie wideo
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,
};
W przeciwieństwie do elementów graficznych filmy nie muszą być w pełni ładowane, aby mogły zostać wyświetlone na płótnie. Filmy zawierają również szereg dodatkowych zdarzeń, które można wykorzystać do monitorowania stanu filmu.
W takim przypadku chcemy wiedzieć, kiedy wideo jest gotowe do odtworzenia. oncanplay
oznacza, że załadowano wystarczającą ilość wideo, aby odtworzyć część, ale może nie wystarczyć do końca.
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
Alternatywnie możesz użyć oncanplaythrough
który uruchomi się, gdy oncanplaythrough
wystarczającą ilość wideo, aby można go było odtworzyć do końca.
video.oncanplaythrough = readyToPlayVideo; // set the event to the play function that
// can be found below
Używaj tylko jednego zdarzenia canPlay, a nie obu.
Zdarzenie może odtwarzać (równoważne obciążeniu obrazu)
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);
}
Wyświetlanie
Wideo nie będzie odtwarzane na płótnie. Musisz narysować go dla każdej nowej ramki. Ponieważ trudno jest ustalić dokładną liczbę klatek na sekundę, a kiedy się pojawiają, najlepszym rozwiązaniem jest wyświetlenie wideo w trybie 60 klatek na sekundę. Jeśli liczba klatek na sekundę jest niższa, w renderuj tę samą klatkę dwa razy. Jeśli liczba klatek jest wyższa, nie ma nic, co mogłoby zobaczyć dodatkowe klatki, więc po prostu je ignorujemy.
Element wideo jest tylko elementem obrazu i można go narysować jak każdy obraz, można skalować, obracać, przesuwać wideo, odzwierciedlać go, przyciemniać, przycinać i wyświetlać tylko części, rysować dwa razy za pomocą globalnego trybu złożonego aby dodać efekty takie jak rozjaśnienie, ekran itp.
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);
}
Podstawowa kontrola pauzy w odtwarzaniu
Teraz mamy załadowane wideo i wyświetlamy wszystko, czego potrzebujemy, to kontrola odtwarzania. Zrobimy to jako przełącznik kliknięcia na ekranie. Gdy wideo jest odtwarzane, a użytkownik klika, wideo jest wstrzymane. Po wstrzymaniu kliknięcie wznawia odtwarzanie. Dodamy funkcję, aby przyciemnić wideo i narysować ikonę odtwarzania (trójkąt)
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
}
Teraz zdarzenie pauzy w odtwarzaniu
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);
streszczenie
Odtwarzanie wideo jest bardzo łatwe przy użyciu płótna, dodawanie efektu w czasie rzeczywistym jest również łatwe. Istnieją jednak pewne ograniczenia dotyczące formatów, sposobu grania i wyszukiwania. MDN HTMLMediaElement to miejsce, w którym można uzyskać pełne odniesienie do obiektu wideo.
Po narysowaniu obrazu na płótnie możesz użyć ctx.getImageData
aby uzyskać dostęp do ctx.getImageData
w nim pikseli. Możesz też użyć canvas.toDataURL
aby zrobić zdjęcie i pobrać je. (Tylko jeśli wideo pochodzi z zaufanego źródła i nie powoduje skażenia płótna).
Uwaga: jeśli wideo ma dźwięk, wówczas odtwarzanie go również odtworzy dźwięk.
Miłego filmowania.
Uchwyć płótno i zapisz jako wideo webM
Tworzenie wideo WebM z ramek obszaru roboczego i odtwarzanie w obszarze roboczym, przesyłanie lub pobieranie.
Przykład przechwytywania i odtwarzania płótna
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
Zamiast wkładać wiele wysiłku, by zostać odrzuconym, jest to szybka wstawka, aby sprawdzić, czy jest do zaakceptowania. Poda pełne szczegóły, jeśli zostaną zaakceptowane. Obejmują także dodatkowe opcje przechwytywania dla lepszych szybkości przechwytywania HD (usunięte z tej wersji, mogą przechwytywać HD 1080 przy 50 fps na dobrych komputerach).
Zostało to zainspirowane przez Wammy, ale jest to kompletne przepisanie z metodologią kodowania podczas pracy, co znacznie zmniejsza pamięć wymaganą podczas przechwytywania. Może przechwytywać dane o ponad 30 sekund lepiej, algorytmy obsługi.
Uwaga ramki są zakodowane w obrazach webP. Tylko Chrome obsługuje kodowanie płótna webP. W przypadku innych przeglądarek (Firefox i Edge) konieczne będzie użycie zewnętrznego kodera webP, takiego jak Libwebp JavaScript. Kodowanie obrazów WebP przez JavaScript jest wolne. (obejmie dodanie surowej obsługi obrazów webp, jeśli zostanie zaakceptowane).
Koder webM inspirowany Whammy: A Real Time 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,
}
})()