Spis treści
- Wprowadzenie
- Kod bazowy dla porównania ImageCapture vs canvas
- Robienie zdjęcia
- Wsparcie ImageCapture
- Kod przykładu na Github
- Klikalny przykład na Github Pages
- Podsumowanie
Wprowadzenie
We wszystkich aplikacjach WWW w których pracowałem do tej pory robienie zdjęcia polegało na podpięciu się do wideo z kamery i przechwyceniu pojedynczej klatki strumienia. Klatka ta była potem rysowana na elemencie <canvas>
a następnie zapisywana jako blob
.
To podejście jest hakiem, wymaga nadmiarowych elementów. Niedawno natknąłem się na dedykowane przeglądarkowe API ImageCapture, opisane m.in. na Mozilli. Pozwala ono zrobić zdjęcie w znacznie prostszy sposób, bez elementów pośrednich i obejść.
Sprawdźmy więc czym różni się nowe podejście od starego.
Kod bazowy dla porównania ImageCapture vs canvas
Stwórzmy kod który pozwoli porównać oba sposoby – zawierał on będzie możliwość wyboru kamery przód/tył oraz możliwość włączenia lampy. UI będzie się prezentował następująco:
Funkcja obsługująca przyciski wygląda tak:
const selectCamera = async () => { stopCamera(); const camera = cameras.find(el => el.checked).value; const facingMode = camera === 'front'? 'user' : 'environment'; const stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode }, }); [track] = stream.getVideoTracks(); preview.srcObject = stream; const capabilities = track.getCapabilities(); if (capabilities.torch) { const torch = light.checked; await track.applyConstraints({ advanced: [{ torch }] }) } preview.play(); };
Na początku sprawdzamy, czy wybrana została przednia czy tylna kamera. Dla kamery przedniej parametr facingMode
należy ustawić na 'user'
, dla tylnej na 'environment'
.
Kolejnym krokiem jest pobranie ścieżki, która zostanie przekazana do ImageCapture
.
Jeżeli użytkownik wybrał włączoną lampę, sprawdzamy czy w danej kamerze jest możliwość uruchomienia lampy. Jeżeli próbowalibyśmy uruchomić lampę bez jej wsparcia, dostalibyśmy błąd.
Samo narysowanie zrobionego zdjęcia wygląda następująco:
const drawPhoto = (blob, photo) => { if (photo.src) { URL.revokeObjectURL(photo.src); } photo.src = URL.createObjectURL(blob); }
Robienie zdjęcia
Stary sposób z canvas
Oto kod starego sposobu robienia zdjęcia:
const takePhotoOld = () => { const canvas = document.createElement('canvas'); const { width, height } = track.getSettings(); canvas.width = width; canvas.height = height; const context = canvas.getContext('2d'); context.drawImage(preview, 0, 0, canvas.width, canvas.height); canvas.toBlob(blob => drawPhoto(blob, photoOld)); }
Musimy stworzyć pośredni element <canvas>
, na którym zarysowana zostanie przechwycona klatka.
Jest to rozwiązanie bardzo popularne, jednak jego działanie nie polega na zrobieniu zdjęcia a na wychwyceniu klatki ze strumienia.
Nowy sposób z ImageCapture
Nowy sposób jest znacznie bardziej zwięzły:
const takePhotoNew = async () => { const imageCapture = new ImageCapture(track); const blob = await imageCapture.takePhoto(); drawPhoto(blob, photoNew); };
Polega na przekazaniu pobranej wcześniej ścieżki do ImageCapture
i wywołania funkcji takePhoto
.
Jeżeli na telefonie włączymy lampę błyskową, zauważymy flash tej lampy przy robieniu zdjęcia – ponieważ dochodzi tutaj do rzeczywistego zrobienia zdjęcia, a nie tylko pobrania klatki ze streama.
Wsparcie ImageCapture
Wsparcie jest pełne we wszystkich przeglądarkach opartych na Chromium, co daje nam ok 70% udziału w rynku. Okazuje się jednak, że Mozilla również pracuje nad tą funkcjonalnością – aktualnie za flagą dostępna jest metoda grabFrame, pobierająca pojedynczą klatkę ze streama. Dlatego jest szansa na to, że również Firefox będzie wspierał to API – a póki co można dla starszych przeglądarek dostarczać sposób z <canvas>
jako fallback.
Kod przykładu na Github
Pełny kod przykładu znajdziesz pod adresem:
https://github.com/radek-anuszewski/image-capture-demo
Klikalny przykład na Github Pages
Działający przykład znajdziesz poniżej. Pamiętaj, by uruchomić go również w przeglądarce mobilnej by móc zobaczyć w akcji zmianę kamery przód/tył i lampę błyskową:
https://radek-anuszewski.github.io/image-capture-demo/
Podsumowanie
ImageCapture
umożliwia zrobienie zdjęcia w sposób prostszy i bez elementów pośrednich. To, że inni dostawcy też zaczynają implementować to API daje szansę na to, że haki z <canvas>
odejdą do lamusa – nie spodziewałbym się jednak, że stanie się to szybko.