Portál o technologiích a vývoji

Obrázky ve webových aplikacích – Využití HTML5

Autor: Adam Klvač Datum: 22.10.2013 Počet shlédnutí: 0x

S HTML5 přišla kromě jiného také rozšíření API pro práci s obrázky přímo v prohlížeči. Časy, kdy sadističtí programátoři vyhazovali hlášky při každé, byť napravitelné chybě, jsou téměř pryč. Opravdu nic nepotěší víc, než když na pomalém připojení po 2 minutách nahrávání dojde k chybě s tím, že obrázek je moc velký a nahráváme znova. Zmenši si sám.

HTML5 nám umožňuje obrázek zobrazit hned po jeho výběru z disku, umožňuje nám dělat jeho předběžné úpravy (výřez, překlopení) a při tvorbě galerií přijde vhod také možnost přetáhnout soubory do okna prohlížeče, což je známo jako Drag & Drop.

Obrázek můžeme zobrazit vcelku jednoduše odchycením události change na prvek formuláře pro výběr souboru. Získáme vybraný soubor, který načteme a zobrazíme. V případě, že uživatel po vybrání souboru klikne na nový výběr a klikne na zrušit, bude mít proměnná file v následující jednoduché ukázce hodnotu undefined.

$('input[type="file"]').on('change', function(event) {
	var file = this.files[0];
	if(file !== undefined) {
		var input = this;
		var reader = new FileReader;
		reader.onloadend = function() {
			if(file.type.split('/')[0] === 'image') {
				var image = document.createElement('img');
				image.src = reader.result;
				$(input).after(image);
			} else {
				alert("Vybraný soubor není platný obrázek.");
			}
		};
		reader.readAsDataURL(file);
	}
});

K otáčení využijeme CSS vlastnost transform. Budeme si ukládat postupné otáčení a při každé změně si do skrytého prvku formuláře nastavíme hodnotu otočení, kterou pak budeme potřebovat pro skutečné zpracování na serveru.

$('a.rotate').on('click', function(event) {
	var preview = $('img.preview');
	if(preview.css('transform') === 'none') {
		$(preview).css('transform', 'rotate(90deg)').attr('rotated', 90);
	} else {
		var rotated = parseInt($(preview).attr('rotated')) + 90;
		if(rotated == 360) rotated = 0;
		$(preview).css('transform', 'rotate(' + rotated + 'deg)').attr('rotated', rotated);
	}
	$('input.rotate').val($(preview).attr('rotated'));
});

K výběru na obrázku můžeme použít canvas – plátno, na které vykreslíme obrázek a navěšenými událostmi zajistíme možnost výběru myší. Velmi jednoduchou implementaci lze vidět v následující ukázce, v praxi by bylo vhodné samozřejmě vynutit určité proporce výběru, posun selekce a také změnu kurzoru při najetí na obrázek, nebo poznámku pod náhled, aby uživatel věděl, že má možnost s obrázkem něco dělat.

$('input[type="file"]').on('change', function(event) {
	var file = this.files[0];
	if(file !== undefined) {
		var input = this;
		var reader = new FileReader;
		reader.onloadend = function() {
			if(file.type.split('/')[0] === 'image') {
				var image = new Image;
				image.src = reader.result;

				// Vytvoříme si plátno
				var canvas = document.createElement('canvas');
				canvas.width = image.width;
				canvas.height = image.height;
				$(input).after(canvas);

				// Získáme kontext
				var context = canvas.getContext('2d');

				// Nakreslíme obrázek
				context.drawImage(image, 0, 0);
				context.strokeStyle = '#F00'; // Barva výběru (červená)

				// Objekt s nastavením selekce
				var selection = {x: 0, y: 0, width: 0, height: 0};

				// Navěsíme funkci pro selekci
				$(canvas)
				.on('mousedown', function(event) {

					// Nastavíme nulový bod selekce
					selection.x = event.pageX - $(this).position().left;
					selection.y = event.pageY - $(this).position().top;

				})
				.on('mousemove', function(event) {
					if(event.which === 1) { // Stisknuto levé tlačítko myši
						var x = event.pageX - $(this).position().left;
						var y = event.pageY - $(this).position().top;

						// Nastavíme nové rozměry selekce
						selection.width = x - selection.x;
						selection.height = y - selection.y;

						// Překreslíme plátno
						context.drawImage(image, 0, 0);
						context.beginPath();
						context.rect(selection.x, selection.y, selection.width, selection.height);
						context.stroke();

					}
				}).on('mouseleave', function(event) {

					// Přepočítáme selekci
					x = selection.width >= 0 ? selection.x : selection.x + selection.width;
					y = selection.height >= 0 ? selection.y : selection.y + selection.height;
					width = Math.round(Math.abs(selection.width));
					height = Math.round(Math.abs(selection.height));

					// Předáme do formuláře
					$('input[name="selection"]').val('x=' + x + '&y=' + y '&width=' + width + '&height=' + height);

				});

			} else {
				alert("Vyberte, prosím, obrázek.");
			}
		};
		reader.readAsDataURL(file);
	}
});

Kód do skrytého formulářového políčka nastaví souřadnice a rozměry výběru v podobě, ve které je lze na serveru parsovat například pomocí funkce parse_str. Je nutné si na straně serveru všechny vstupní údaje ohlídat a nepropustit cokoliv, co neodpovídá masce. To ale platí u validace v kombinaci s použitím Javascriptu obecně.

S HTML5 přišla také podpora Drag & Drop, což velice oceňuji. Nerad vybírám jednotlivé obrázky ze složky pomocí dialogu prohlížeče a je mi příjemnější přetáhnout obrázky z průzkumníku myší, občas je také užitečný fakt, že se dají do stránky přetáhnout obrázky z lišty stahovaných souborů (což mi připomíná – dejte uživatelům možnost nahrát svůj avatar ze vzdáleného serveru).

var formData = new FormData;

$('form')
.on('dragover', function(event) {
	event.preventDefault(); // Prvek přijímá soubory
})
.on('drop', function(event) {
	event.preventDefault(); // Potlačíme výchozí chování prohlížeče
	for(i = 0; (file = event.originalEvent.dataTransfer.files[i]) !== undefined; i++) {

		if(file.type.split('/')[0] !== 'image') {
			continue; // Přeskočíme neobrázkové soubory
		}

		// Přidáme soubory k odeslání
		formData.append('image[' + i + ']', file);

	}
})
.on('submit', function() {
	event.preventDefault(); // Budeme manipulovat s daty k odeslání
	var request = new XMLHttpRequest;
	request.open('POST', this.action);
	request.send(formData);
});

Obrázky bychom samozřejmě mohli také vykreslit a dát možnost jejich editace, dá se toho dosáhnout prostou kombinací s výše uvedenými ukázkami. Tento způsob posílá obrázky na server asynchronně. Odesílání běžným způsobem se mi zdá problematické, navíc v případě hromadného uploadu je asi lepší odesílat právě AJAXem a řekl bych, že rozumné by bylo dělat to po jednotlivých obrázcích, klidně ve více vláknech. To z toho důvodu, že při případném přerušení spojení (např. při odpojení od WiFi a podobně) nepřijdeme o všechny nahrávané obrázky, protože server je v tomto případě schopen zpracovat pouze celý požadavek najednou. Také by bylo příjemné zobrazovat stav uploadu pro jednotlivé soubory, což není již problém docílit za použití Progress Events.

Štítky: | | | |

Žádné komentáře

Poslat komentář

Vaše emailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *