Ricerca…
Osservazioni
Questo argomento tratta dei vari tipi di media e di come possono essere utilizzati con il canvas nell'interfaccia 2D.
I tipi di media hanno categorie generiche e di formato specifico
Tipi di media
- animazioni
- video
- immagini
- Immagini HD
- Immagine vettoriale
- Immagini animate
Formati multimediali
- Jpg / Jpeg
- png
- gif
- SVG
- M-JPEG
- webm
- WebP
immagini
Esistono numerosi formati di immagini supportati dai browser, sebbene nessun browser li supporti tutti. Se si dispone di formati di immagine particolari che si desidera utilizzare Browser Wiki e formati di immagine supportati fornisce una buona panoramica.
Il miglior supporto è per i 3 formati principali, "jpeg", "png" e "gif" con tutti i principali browser che forniscono supporto.
JPEG
Le immagini JPEG sono più adatte a foto e immagini simili a foto. Non si prestano bene a grafici, diagrammi e testi. Le immagini JPEG non supportano la trasparenza.
Canvas può emettere immagini JPEG tramite canvas.toDataURL
e canvas.toBlob
e fornisce un'impostazione di qualità. Dato che JPEG non supporta la trasparenza, tutti i pixel trasparenti verranno mescolati con il nero per l'output finale JPG. L'immagine risultante non sarà una copia perfetta della tela.
PNG
Le immagini PNG sono immagini della massima qualità e possono anche includere un canale alfa per i pixel trasparenti. I dati dell'immagine sono compressi ma non producono artefatti come le immagini JPG.
A causa della compressione lossless e del supporto del canale alfa, i PNG vengono utilizzati per i giochi, le immagini dei componenti ui, i grafici, i diagrammi, il testo. Quando li si utilizza per foto e foto come immagini, le dimensioni del file possono essere molto più grandi di quelle di JPEG. .
Il formato PNG fornisce anche il supporto dell'animazione anche se il supporto del browser è limitato e l'accesso all'animazione per l'utilizzo sulla tela può essere effettuato solo tramite API e librerie Javascript
Il canvas può essere utilizzato per codificare le immagini PNG tramite canvas.toDataURL
e canvas.toBlob
anche se il formato di output è limitato a 32Bit RGBA compressi. Il PNG fornirà una copia pixel perfetta della tela.
GIF
Le GIF vengono utilizzate per brevi animazioni, ma possono anche essere utilizzate per fornire grafici, diagrammi e immagini di testo di alta qualità. Le GIF hanno un supporto dei colori molto limitato con un massimo di 256 colori per fotogramma. Con le immagini gif di elaborazione delle immagini della mannaia si ottengono risultati sorprendentemente buoni, specialmente quando sono animati. Le GIF forniscono trasparenza anche se questo è limitato a on o off
AS con PNG, le animazioni GIF non sono direttamente accessibili per l'uso sul canvas e per accedere è necessaria un'API o una libreria Javascript. GIF non può essere salvato tramite la tela e richiederà e API o libreria per farlo.
Caricamento e visualizzazione di un'immagine
Per caricare un'immagine e posizionarla sulla tela
var image = new Image(); // see note on creating an image
image.src = "imageURL";
image.onload = function(){
ctx.drawImage(this,0,0);
}
Creare un'immagine
Esistono diversi modi per creare un'immagine
-
new Image()
-
document.createElement("img")
-
<img src = 'imageUrl' id='myImage'>
Come parte del corpo HTML e recuperato condocument.getElementById('myImage')
L'immagine è un HTMLImageElement
Proprietà Image.src
L'immagine src
può essere qualsiasi URL di immagine valido o dataURL codificato. Vedi le osservazioni di questo argomento per ulteriori informazioni su formati e supporto di immagine.
-
image.src = "http://my.domain.com/images/myImage.jpg"
-
image.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="
*
* Il dataURL è un'immagine GIF 1 x 1 pixel contenente il nero
Note sul caricamento e errori
L'immagine inizierà il caricamento quando viene impostata la sua proprietà src. Il caricamento è sincronizzato ma l'evento onload
non verrà chiamato fino a quando la funzione o il codice non saranno usciti / restituiti.
Se si ottiene un'immagine dalla pagina (ad esempio, document.getElementById("myImage")
) e il relativo src
è impostato potrebbe essere caricato o meno. Puoi controllare lo stato dell'immagine con HTMLImageElement.complete
che sarà true
se completo. Questo non significa che l'immagine sia caricata, significa che lo ha anche
- caricato
- c'era un errore
- la proprietà src non è stata impostata ed è uguale alla stringa vuota
""
Se l'immagine proviene da una fonte inaffidabile e potrebbe non essere accessibile per una serie di motivi, genererà un evento di errore. Quando ciò accade l'immagine sarà in uno stato rotto. Se tenti di disegnarlo sulla tela, verrà generato il seguente errore
Uncaught DOMException: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The HTMLImageElement provided is in the 'broken' state.
Fornendo l'evento image.onerror = myImgErrorHandler
puoi prendere le misure appropriate per evitare errori.
Disegnare un'immagine svg
Per disegnare un'immagine SVG vettoriale, l'operazione non è diversa da un'immagine raster:
Per prima cosa è necessario caricare l'immagine SVG in un elemento HTMLImage, quindi utilizzare il metodo drawImage()
.
var image = new Image();
image.onload = function(){
ctx.drawImage(this, 0,0);
}
image.src = "someFile.SVG";
Le immagini SVG hanno alcuni vantaggi rispetto a quelle raster, dal momento che non perderai la qualità, a prescindere dalla scala su cui la disegnerai sulla tela. Ma attenzione, potrebbe anche essere un po 'più lento del disegno di un'immagine raster.
Tuttavia, le immagini SVG hanno più restrizioni rispetto alle immagini raster.
Per motivi di sicurezza, nessun contenuto esterno può essere caricato da un'immagine SVG a cui si fa riferimento in un HTMLImageElement (
<img>
)
Nessun foglio di stile esterno, nessuna immagine esterna referenziata in elementi SVGImage (<image/>
), nessun filtro esterno o elemento collegato dall'attributoxlink:href
(<use xlink:href="anImage.SVG#anElement"/>
) o il funcIRI (url()
) metodo di attributo ecc.
Inoltre, i fogli di stile aggiunti nel documento principale non avranno alcun effetto sul documento SVG una volta fatto riferimento in un elemento HTMLImmagine.
Infine, nessuno script verrà eseguito all'interno dell'immagine SVG.
Soluzione: è necessario aggiungere tutti gli elementi esterni all'interno dello SVG stesso prima di fare riferimento all'elemento HTMLImage. (per immagini o caratteri, è necessario aggiungere una versione dataURI delle risorse esterne).L'elemento radice (
<svg>
) deve avere gli attributi width e height impostati su un valore assoluto.
Se si dovesse utilizzare la lunghezza relativa (ad esempio%
), il browser non sarà in grado di sapere a cosa è relativo. Alcuni browser (Blink) tenteranno di indovinare, ma la maggior parte semplicemente ignorerà la tua immagine e non disegnerà nulla, senza un avvertimento.Alcuni browser macchieranno la tela quando un'immagine SVG è stata disegnata su di essa.
In particolare, Internet-Explorer <Edge in ogni caso e Safari 9 quando<foreignObject>
è presente nell'immagine SVG.
Caricamento di base e riproduzione di un video sulla tela.
La tela può essere utilizzata per visualizzare video da una varietà di fonti. Questo esempio mostra come caricare un video come risorsa file, visualizzarlo e aggiungere un semplice clic sullo schermo play / pause toggle.
Questa domanda con risposta automatica StackOverflow Come faccio a visualizzare un video utilizzando il tag canvas HTML5 mostra il seguente codice di esempio in azione.
Solo un'immagine
Un video è solo un'immagine per quanto riguarda la tela. Puoi disegnarlo come qualsiasi immagine. La differenza è che il video può suonare e ha un suono.
Ottieni canvas e impostazioni di base
// 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
Creazione e caricamento del video
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,
};
A differenza degli elementi delle immagini, i video non devono essere completamente caricati per essere visualizzati sulla tela. I video forniscono anche una serie di eventi extra che possono essere utilizzati per monitorare lo stato del video.
In questo caso desideriamo sapere quando il video è pronto per la riproduzione. oncanplay
significa che è stato caricato un numero sufficiente di video per riprodurne alcuni, ma potrebbe non essere sufficiente per suonare fino alla fine.
video.oncanplay = readyToPlayVideo; // set the event to the play function that
// can be found below
In alternativa puoi usare oncanplaythrough
che si oncanplaythrough
quando una parte del video è stata caricata in modo tale da poter essere riprodotta fino alla fine.
video.oncanplaythrough = readyToPlayVideo; // set the event to the play function that
// can be found below
Utilizzare solo uno degli eventi canPlay non entrambi.
L'evento can play (equivalente al caricamento di immagini)
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);
}
Visualizzazione
Il video non verrà riprodotto sulla tela. Devi disegnarlo per ogni nuovo fotogramma. Poiché è difficile conoscere il frame rate esatto e quando si verificano, il miglior approc è quello di visualizzare il video come se fosse a 60fps. Se la frequenza dei fotogrammi è inferiore, w solo renderizza due volte lo stesso fotogramma. Se la frequenza dei fotogrammi è superiore, non c'è nulla che possa essere visto per vedere i fotogrammi aggiuntivi, quindi li ignoriamo.
L'elemento video è solo un elemento dell'immagine e può essere disegnato come qualsiasi immagine, è possibile ridimensionare, ruotare, spostare il video, specularlo, sfumarlo, ritagliarlo e visualizzare solo le parti, disegnarlo due volte la seconda volta con una modalità composita globale aggiungere FX come schiarire, schermo, ecc.
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);
}
Controllo di pausa riproduzione di base
Ora abbiamo il video caricato e visualizzato tutto ciò di cui abbiamo bisogno è il controllo di gioco. Lo faremo come un clic per attivare la riproduzione sullo schermo. Quando il video è in riproduzione e l'utente fa clic sul video viene messo in pausa. Quando viene messo in pausa, il clic riprende la riproduzione. Aggiungeremo una funzione per scurire il video e disegnare un'icona di riproduzione (triangolo)
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
}
Ora l'evento di pausa di riproduzione
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);
Sommario
Riproduzione di un video è molto semplice con la tela, anche l'aggiunta di effetti in tempo reale è facile. Ci sono tuttavia alcune limitazioni sui formati, come puoi giocare e cercare. MDN HTMLMediaElement è il posto dove ottenere il riferimento completo all'oggetto video.
Una volta che l'immagine è stata disegnata sulla tela, puoi usare ctx.getImageData
per accedere ai pixel che contiene. Oppure puoi usare canvas.toDataURL
per scattare una foto e scaricarla. (Solo se il video proviene da una fonte attendibile e non macchia la tela).
Nota se il video ha un suono, quindi riprodurrà anche il suono.
Buon video.
Cattura tela e Salva come video WebM
Creazione di un video WebM da cornici su tela e riproduzione in tela, caricamento o download.
Esempio di acquisizione e riproduzione della tela
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
Piuttosto che fare uno sforzo enorme solo per essere respinto, questo è un inserto veloce per vedere se accettabile. Darà tutti i dettagli se accettato. Includere anche opzioni di acquisizione aggiuntive per una migliore velocità di acquisizione HD (rimosso da questa versione, può catturare HD 1080 a 50 fps su macchine buone).
Questo è stato ispirato da Wammy, ma è una completa riscrittura con codifica mentre si va metodologia, riducendo notevolmente la memoria necessaria durante l'acquisizione. Può acquisire più di 30 secondi di dati migliori, gestendo algoritmi.
I frame di nota sono codificati in immagini WebP. Solo Chrome supporta la codifica del canvas webP. Per gli altri browser (Firefox e Edge) sarà necessario utilizzare un codificatore WebP di terze parti come Libwebp Javascript Codifica delle immagini WebP tramite Javascript è lento. (includerà l'aggiunta del supporto di immagini Webp raw se accettato).
Il codificatore webM ispirato a Whammy: WebM Javascript in tempo reale
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,
}
})()