arkenidar/camera — fotocamera del browser in <40 righeUna pagina che mostra l'anteprima live della fotocamera; toccandola scatta una foto statica; toccando la foto la scarichi sul dispositivo. Tutto client-side, zero server, zero dipendenze — quasi il caso limite del "minimale" che ti interessa.
video (anteprima live) → click → canvas (frame fermo) → img (foto) → a[download] (file)
<video id="video"></video>
<canvas id="canvas" style="display: none"></canvas>
<a id="link" download="photo.png"><img id="photo"></a>
Tre tag nativi del browser, ognuno con un ruolo preciso: <video> mostra lo stream live, <canvas> (nascosto) serve solo da "lavagna" temporanea per catturare un fotogramma, <img> dentro un <a download> mostra ed espone per il download la foto scattata.
navigator.mediaDevices.getUserMedia({ video: true })
.then(function (stream) {
var video = $id('video')
video.srcObject = stream
video.play()
video.onclick = click
})
getUserMedia è una Promise: il browser mostra all'utente il popup di permesso, e solo se concesso lo stream arriva nel .then(). srcObject (non src!) è la proprietà giusta per collegare uno stream live a un elemento <video>, a differenza di un normale file video.
function $id(id) {
return document.getElementById(id)
}
Una funzione di una riga, ma significativa: accorcia ogni accesso al DOM nel resto del file ($id('video') invece di document.getElementById('video')) senza tirare in causa jQuery o altro — un micro-helper scritto a mano invece di una dipendenza.
function click() {
var canvas = $id('canvas')
var video = $id('video')
var height = canvas.height = video.videoHeight
var width = canvas.width = video.videoWidth
canvas.getContext('2d').drawImage(video, 0, 0, width, height)
...
Nota lo stile canvas.height = video.videoHeight: un assegnamento concatenato che imposta sia la variabile locale height sia la proprietà canvas.height in una riga. drawImage(video, ...) è la chiamata chiave: un <canvas> può disegnare il fotogramma corrente di un <video>, congelandolo in pixel statici.
$id('photo').src = canvas.toDataURL('image/png')
toDataURL() serializza il contenuto del canvas in una stringa data:image/png;base64,... — un'immagine PNG completa codificata come testo, utilizzabile direttamente come src di un <img>. Nessun upload, nessuna richiesta di rete: la foto esiste solo nella memoria del browser.
$id('link').href = $id('photo').src.replace("image/png", "image/octet-stream")
Stesso data URL della foto, ma con il MIME type cambiato da image/png a image/octet-stream. Questo è il segnale che dice al browser "non sai cosa fare con questo, scaricalo come file binario" invece di provare a visualizzarlo — combinato con l'attributo download="photo.png" già nell'HTML, il click sul link scarica la foto.
caricamento pagina
→ getUserMedia({video:true}) [chiede permesso]
→ stream assegnato a video.srcObject, video.play()
→ video.onclick = click
utente tocca il video
→ click()
→ canvas dimensionato come il video
→ drawImage(video, ...) cattura il fotogramma
→ canvas.toDataURL('image/png') → mostrato in <img id="photo">
→ stesso data URL, MIME cambiato → href del link di download
Due eventi (caricamento pagina, click) e una manciata di API native del browser (getUserMedia, canvas 2D context, toDataURL) bastano per un'app fotocamera completa.
Questo esempio è interessante anche per la sua origine dichiarata: parte da un tutorial MDN (Media Capture and Streams API) e viene ridotto e commentato (bilingue, IT/EN) riga per riga — un buon caso di studio di come adattare un riferimento ufficiale al proprio stile minimale invece di copiarlo as-is.
Il repository: github.com/arkenidar/camera — demo live su arkenidar.github.io/camera