fbpx

BLOG

Progresivne spletne aplikacije in osnove kako zajeti sliko uporabnika

input type="file" accept="image/*" v kontekstu Angular spletne aplikacije

Progresivne spletne aplikacije so poskus kako premostiti velik prepad med aplikacijami, ki so zasnovane za specifični operacijski sistem in izkušnjo, ki jo trenutno ponuja spletna platforma. Moderni splet je potrebno razumeti kot platformo. Svetovni splet je bil zasnovan  za hitro in učinkovito izmenjavo informacij. V začetku so te informacije prevzele obliko dokumentov in morda kakšne slike. Danes svetovni splet omogoča bančništvo, zabavo in prosti čas, stike s prijatelji, iskanje ljubezni in orodja za praktično katerokoli digitalno opravilo, ki si ga lahko izmislimo. Splet je platforma v polnem pomenu te besede! V primerjavi z ostalimi bolj tradicionalnimi definicijami digitalnih platform, je celo daleč največja in najbolj uporabljana. Največja prednost za uporabnike je, da deluje na vsaki moderni napravi.

S sprejetjem dejstva, da je splet najpomembnejša platforma, moramo razvijalci ponuditi izkušnje, ki presegajo tiste, ki so trenutno možne na ravni aplikacij operacijskega sistema. Največji izziv za razvijalce izhaja iz dejstva, da splet deluje na praktično vseh modernih napravah. Ali ni bila to največja prednost še prejšnji odstavek? Z vidika razvijalcev je zgodba veliko bolj zapletena, saj vse naprave nimajo istih zmožnosti. Tukaj nastopi progresivnost v progresivnih spletnih aplikacijah. Moderne spletne aplikacije morajo dodati funkcionalnosti na način, da ni motena uporabniška izkušnja, tudi če je brskalnik ali naprava ne podpirata.

Številni moderni brskalniki imajo možnost zajema video in/ali fotografije uporabnikov. Vendar je odvisno od posameznega brskalnika ali v celoti podira vse zmogljivosti ali pa določene naloge delegira drugim aplikacijam. Vsaka naprava tudi nima kamere za zajem tovrstnih vnosov. Vsi pa se lahko strinjamo, da so slike pomemben del aplikacij na spletu. Vprašanje torej nastane, kako je mogoče na progresivni način zagotoviti izkušnjo za dodajanje slik na vseh brskalnikih?

Začnimo preprosto. Če želimo progresivno dodajati modernejše funkcionalnosti moramo začeti z izkušnjo, ki deluje vsepovsod. Najprepostejši prvi korak je, da enostavno zahtevamo od uporabnika prenos že shranjene fotografije. Uporabimo lahko HTML input, ki zahteva datoteko, ki je fotografija takole:


<input type="file" accept="image/*">

Ta metoda deluje na vseh brskalnikih, na vseh operacijskih sistemih. Na računalnikih se prikaže možnost izbire datoteke shranjene na disku. Na mobilnih napravah, ki imajo kamero bo uporabniku predstavljena izbira ali želi zajeti novo fotografijo ali pa želi prenesti fotografijo, ki je shranjena na napravi. Za predstavitev opisane funkcionalnosti bom uporabil PWA, ki sem ga izdelal za Slovensko fundacijo za UNICEF za praznovanje Svetovnega dneva otrok 20. novembra. Spletna aplikacija je galerija otroških slik na temo: “V kakšnem svetu si želimo odraščati otroci”. V okviru natečaja so otroci izdelali risbe, starši pa so s pomočjo PWA aplikacije prenesli risbe v galerijo. Aplikacijo si lahko v celoti ogledate tukaj. Input element na mobilnih napravah sproži izbiro za vnos fotografije, kot prikazuje slika.

Kako ekstrahiramo fotografijo?

Po uporabnikovi izbiri je mogoče izbrano fotografijo zajeti z uporabo form HTML elementa ali z uporabo JavaScript in “onchange eventa input” elementa. Ker v članku govorimo o modernih spletnih aplikacijah bomo uporabili TypeScript z uporabo Googlovega ogrodja za izdelavo Single Page Aplikacij (SPA) imenovano Angular za prikaz kode. Primer pa je dovolj razumljivo prikazan, da ni veliko dela, kode prenesti v golo JavaScript implementacijo. 

HTML:
<input type="file" accept="image/*">

TypeScript:
narediNekajZDateko(event) {

// preveri ali je uporabnik izbral kakšno datoteko, drugače prekini. 
    if (event.target.files.length <= 0) {
        return false;
    }

// izberi prvo datoteko. To kodo je potrebno popraviti, če želimo dovoliti uporabniku, da lahko izbere več kot eno datoteko.
    const file: File = event.target.files[0];
}

Kako preverimo ali je izbrana datoteka res fotografija?

Zgornja koda prikazuje kako v HTML zahtevamo fotografijo. Atribut accept=”image/*” na input elementu omogoča uporabniku samo izbiro fotografije. Ne smemo se zanašati samo na to zagotovilo, da je uporabnik izbral pravilno datoteko. Sami moramo preveriti ali je izbrana datoteka res fotografija. Na žalost nimajo vsi uporabniki spleta dobrega namena. Svetujemo, da tip datoteke preverite tudi na strežniku, preden jo shranite. Na strani uporabnika, pa lahko datoteko preverim sledeče. Tip vseh vrst fotografij se prične z “image/”. Razširimo dosedanjo funkcijo narediNekajZDateko: 

narediNekajZDateko(event) {

// preveri ali je uporabnik izbral kakšno datoteko, drugače prekini. 
    if (event.target.files.length <= 0) {
        return false;
    }

// izberi prvo datoteko. To kodo je potrebno popraviti, če želimo dovoliti uporabniku, da lahko izbere več kot eno datoteko.
   const datoteka: File = event.target.files[0];

// pridobi prvih 6 znakov tipa datoteke
    const tipDatoteke = datoteka.type.substring(0, 6);


// preveri ali se prvih 6 znakov ujema z “image/”, če se ne, prekini. 
    if (tipDatoteke !== 'image/' ) {
      return false;
    }
}

Na tej točki imamo fotografijo. Pri preizkusu kode opazimo, da sicer deluje, vendar izgled ni primeren za moderno spletno aplikacijo. Element input type=”file” namreč sam po sebi izgleda takole: 

Dolgočasno je morda še najboljši pridevnik za ta prikaz. Prva progresivna izboljšava je moderni gumb, ki bo estetsko ustrezal celotnemu izgledu aplikacije, kot je gumb v našem PWA primeru:

Preoblikovanje input elementa v zgornji gumb bi bila CSS nočna mora. Zato lahko uporabimo naslednji pristop, ki še vedno deluje na vseh brskalnikih in napravah. V aplikacijo dodamo gumb po naši želji in input element skrijemo pred uporabnikom. S pritiskom na naš novi gumb v ozadju sprožimo skriti input element. Vse to lahko dosežemo v HTML takole:


<input type="file" accept="image/*"id="file-input" (change)="narediNekajZDateko($event) style="display: none;">

<button>Dodaj risbo</button>

V zgornjem primeru je pomemben atribut style=”display: none;” na input elementu, ki ga skrije. In onclick event handler na button elementu, ki sproži skriti input.

Do sedaj smo uspešno zajeli uporabnikovo fotografijo na način, ki deluje na vseh brskalnikih in napravah in je brezhibno integriran v izgled naše aplikacije. Kaj pa v primeru, ko ne želimo uporabnika obremenjevati s tem, od kod lahko izbere fotografijo? V naši aplikaciji za likovni natečaj bi lahko bilo smiselno, da zahtevamo uporabo hrbtne kamere, da starši bolje razumejo pričakovanje, da fotografirajo risbico otroka. Kako bi se lotili tega. Na srečo ima input element atribut imenovan capture. S tem atributom brskalniku nakažemo, da si predvsem želimo sliko iz uporabnikove kamere. Atribut deluje progresivno. Če uporabnikova naprava nima kamere, se bo prikazalo sistemsko okno za izbiro datoteke. Prav tako lahko brskalniku nakažemo katero kamero naj uporabi. Pametni telefoni imajo ponavadi kamero, ki je usmerjena v uporabnika in kamero, ki je na hrbtni strani telefona. Če atribut capture enačimo z “user”, bo brskalnik, če je to mogoče, uporabil spredno kamero, če pa atribut enačimo z “environment” bo uporabil hrbtno. HTML bi izgledal takole:

<input type="file" accept="image/*" capture>

<input type="file" accept="image/*" capture="user">

<input type="file" accept="image/*" capture="environment">

Kako prikažemo zajeto sliko nazaj uporabniku?

Sedaj ko smo zajeli fotografijo uporabnika, je smiselno, da zajeto fotografijo prikažemo uporabniku. Tako se lahko odloči ali je zadovoljen s posnetkom, preden ga objavi. Element img pričakuje pot do fotografije za prikaz. Vendar src atributi ne moremo enostavni posredovati poti, ki smo jo prejeli s datoteko. Pot, ki jo prejmemo je namreč sistemska pot in brskalniki nimajo ustreznih pravic direktne manipulacije sistemskih datotek. Da lahko fotografijo prikažemo jo moramo najprej prebrati s pomočjo brskalnikovega FileReader API-ja.

FileReader je asinhroni API brskalnika in kot vsi asinhroni API-ji brskalnika, ni zasnovan kot “Promise”. To nam preprečuje uporabo moderne Javascript sintakse async/await, ki je bila predstavljena v ES15 JavaScript specifikaciji. Zato bomo funkcijo, ki bo uporabljala FileReader zavili v “Promise”, da bomo nato lahko v narediNekajZDateko() z besedo await čakali, da FileReader vrne primerni URL, ko brskalnik uspešno prebere datoteko. Šele potem jo lahko prikažemo uporabniku. 

pridobiPravilnoPotDoDatoteke(datoteka: File): Promise {
// nova instanca FileReader objekta
    const reader = new FileReader();

// vrni Promise objekt, ki se bo razrešil kot pravilna pot
    return new Promise((resolve, reject) => {
		
    // poslušaj za error stanje
    reader.onerror = () => {
    // prekini in vrni error
        reader.abort();
        reject(new Error('Opiši error.'));
    };

    // poslušaj za uspešno prebran file
    reader.onload = event => {
	// vrni pot
    	resolve(reader.result);
    };

    // prični brati datoteko
    reader.readAsDataURL(datoteka);
  });
}


async narediNekajZDateko(event) {

// preveri ali je uporabnik izbral kakšno datoteko, drugače prekini.
    if (event.target.files.length <= 0) {
        return false;
    }

// izberi prvo datoteko. To kodo je potrebno popraviti, če želimo dovoliti uporabniku, da lahko izbere več kot eno datoteko.
   const datoteka: File = event.target.files[0];

// pridobi prvih 6 znakov tipa datoteke
    const tipDatoteke = datoteka.type.substring(0, 6);


// preveri ali se prvih 6 znakov ujema z “image/”, če se ne, prekini. 
    if (tipDatoteke !== 'image/' ) {
      return false;
    }

// pridobi pot s katero lahko prikažemo sliko uporabniku in jo shranimo v spremenljivko klase imenovano prikaznSlika v trenutni komponenti. 
    this.prikaznSlika = await pridobiPravilnoPotDoDatoteke(datoteka);
}

V HTML lahko uporabimo Angular-ov property binding in ngIf, da prikažemo sliko.


<img [src]=”prikaznaSlika” *ngIf=”prikaznaSlika”>

S tem smo uspeli pridobiti datoteko, preveriti ali je datoteka res slika in jo prikazati uporabniku. Uporabili smo način, ki deluje v vseh brskalnikih in napravah. Prav tako smo dodali že nekatere progresivne izboljšave, ki pozitivno vplivajo na uporabniško izkušnjo. V naslednjem članku se bomo lotili progresivnih izboljšav, ki delujejo samo v modernejših brskalnikih. Sedaj ko imamo trdni temelj, za katerega vemo, da bo deloval v vseh okoliščinah, lahko samozavestno nadaljujemo v vsem kar omogoča moderni splet.

O avtorju

Aljaž Daković

Aljaž Daković

Deli

Share on facebook
Share on twitter
Share on linkedin

Mogoče ti bo všeč tudi

Kaj je progresivna spletna aplikacija (PWA)?

Progresivna spletna aplikacija ali na kratko PWA (Progressive Web App) je izraz, ki zajema nekaj …

MackBook Pro 16 inč mnenje in ocena

Mnenje in ocena Štiri leta. Štiri leta je dovolj časa, da povprečni slovenski najstnik zaključi …