risposta

20

ci sono quattro fasi principali:

  1. Creare <canvas> e <video> elementi.
  2. Caricare il src del file video generato dalla URL.createObjectURL nell'elemento <video> e attendere che si carichi ascoltando per eventi specifici di essere licenziato.
  3. Impostare l'ora del video nel punto in cui si desidera scattare un'istantanea e ascoltare altri eventi.
  4. Usa la tela per catturare l'immagine.

Fase 1 - Creare gli elementi

questo è molto facile: basta creare un <canvas> e uno <video> elemento e li aggiunge alla <body> (o dovunque in realtà, non ha molta importanza):

var canvasElem = $('<canvas class="snapshot-generator"></canvas>').appendTo(document.body)[0]; 
var $video = $('<video muted class="snapshot-generator"></video>').appendTo(document.body); 

Si noti che l'elemento video ha l'attributo muted. Non inserire altri attributi come autoplay o controls. Notare anche che entrambi hanno la classe snapshot-generator. Questo è così che possiamo impostare lo stile per entrambi in modo che siano fuori strada:

.snapshot-generator { 
    display: block; 
    height: 1px; 
    left: 0; 
    object-fit: contain; 
    position: fixed; 
    top: 0; 
    width: 1px; 
    z-index: -1; 
} 

Alcuni browser lavorare con loro impostato display: none, ma altri browser avrà seri problemi a meno che non siano resi sul pagina, quindi li rendiamo minuscoli in modo che siano essenzialmente invisibili. (Non li muoversi al di fuori della finestra, però, come altrimenti si può vedere alcune barre di scorrimento brutte sulla tua pagina.)

Fase 2 - Caricare il video

Qui è dove le cose cominciano a diventare difficile. Devi ascoltare gli eventi per sapere quando continuare. Browser diversi generano eventi, orari e ordini diversi, quindi ti risparmierò lo sforzo. Ci sono tre eventi che devono sempre sparare almeno una volta prima che il video sia pronto; essi sono:

  • loadedmetadata
  • loadeddata
  • sospendere

impostare il gestore di eventi per questi eventi e tenere traccia di come molti hanno sparato. Una volta che tutti e tre hanno sparato, sei pronto per procedere. Tieni presente che, poiché alcuni di questi eventi possono essere attivati ​​più di una volta, devi solo gestire il primo evento di ogni tipo che viene generato e scartare i licenziamenti successivi. Ho usato jQuery's .one, che si occupa di questo.

var step_2_events_fired = 0; 
$video.one('loadedmetadata loadeddata suspend', function() { 
    if (++step_2_events_fired == 3) { 
     // Ready for next step 
    } 
}).prop('src', insert_source_here); 

La fonte dovrebbe essere solo l'URL oggetto creato tramite URL.createObjectURL(file), dove file è l'oggetto file.

Fase 3 - Impostare l'ora

Questa fase è simile alla precedente: impostare l'ora e poi ascoltare per un evento. All'interno del nostro if isolato dal codice precedente:

$video.one('seeked', function() { 
    // Ready for next step 
}).prop('currentTime', insert_time_here_in_seconds); 

Per fortuna il suo solo un evento questa volta, quindi è abbastanza chiaro e conciso. Infine ...

Fase 4 - Afferra l'istantanea

Questa parte è solo con l'elemento <canvas> per afferrare uno screenshot. All'interno del nostro gestore di eventi seeked:

canvas_elem.height = this.videoHeight; 
canvas_elem.width = this.videoWidth; 
canvas_elem.getContext('2d').drawImage(this, 0, 0); 
var snapshot = canvas_elem.toDataURL(); 

// Remove elements as they are no longer needed 
$video.remove(); 
$(canvas_elem).remove(); 

La tela deve corrispondere alle dimensioni del video (non l'elemento <video>) per ottenere un'immagine corretta. Inoltre, stiamo impostando le proprietà interne della tela .height e .width, , non i valori di stile CSS altezza/larghezza del canvas.

Il valore di snapshot è un URI di dati, che è fondamentalmente solo una stringa che inizia con data:image/jpeg;base64 e quindi i dati di base64.

Il nostro codice JS finale dovrebbe essere simile:

var step_2_events_fired = 0; 
$video.one('loadedmetadata loadeddata suspend', function() { 
    if (++step_2_events_fired == 3) { 
    $video.one('seeked', function() { 
     canvas_elem.height = this.videoHeight; 
     canvas_elem.width = this.videoWidth; 
     canvas_elem.getContext('2d').drawImage(this, 0, 0); 
     var snapshot = canvas_elem.toDataURL(); 

     // Delete the elements as they are no longer needed 
     $video.remove(); 
     $(canvas_elem).remove(); 
    }).prop('currentTime', insert_time_here_in_seconds); 
    } 
}).prop('src', insert_source_here); 

Celebrate!

Hai la tua immagine in base64! Invia questo al tuo server, inseriscilo come src di un elemento <img> o altro.

Ad esempio, è possibile decodificarlo in binario e scriverlo direttamente in un file (ritagliare prima il prefisso), che diventerà un file di immagine JPEG.

Si potrebbe anche usare questo per offrire anteprime di video mentre sono caricati. Se lo stai inserendo come src di un <img>, utilizza l'URI di dati completo (non rimuovere il prefisso).