Simple Image Filter Menggunakan JavaScript Canvas (LKSN 2021 Web Technologies)

Halo, Kali ini saya akan membagikan tutorial HTML Canvas + JavaScript untuk membuat simple image filter. Ini merupakan lanjutan dari seri LKS Nasional 2021 Web Technologies.

Seperti di seri-seri sebelumnya, anda dapat melihat source code dan juga soal dari tutorial ini di repository saya:

GitHub - hsnfirdaus/lksn21-web-technologies: Soal dan contoh pengerjaan LKSN Web Technologies 2021.
Soal dan contoh pengerjaan LKSN Web Technologies 2021. - GitHub - hsnfirdaus/lksn21-web-technologies: Soal dan contoh pengerjaan LKSN Web Technologies 2021.
github.com
Preview link to GitHub - hsnfirdaus/lksn21-web-technologies: Soal dan contoh pengerjaan LKSN Web Technologies 2021.

Simple Image Filter dengan Canvas

Canvas adalah sebuah teknologi canggih dari HTML 5 yang memungkinkan kita untuk melakukan berbagai hal yang berhubungan dengan grafis. Membuat objek gambar, mengubah ukuran gambar, hingga membuat sebuah game interaktif. Jika anda belum pernah mengenal canvas sama sekali, saya sarankan untuk membaca referensi HTML Canvas di w3schools.

Dalam soal speedtest ini kita dituntut untuk dapat membuat sebuah filter image "darken" dan "lighten" di dalam HTML Canvas. Untuk gambar yang akan digunakan tersedia dalam modul (file media dalam repository github saya). Tampilan yang diinginkan adalah kurang lebih berikut ini:

Simple Image Filter dengan HTML Canvas

Membuat Struktur HTML

Pertama-tama tentunya kita harus menyiapkan struktur HTML yang memiliki layout seperti gambar diatas:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Simple Image Filter</title>
</head>
<body>
	<main>
		<canvas id="canvas" height="320px" width="460px"></canvas>
		<div>
			Image: 
			<select id="image">
				<option value="athena.jpg">Image:</option>
				<option value="athena.jpg">Athena.jpg</option>
				<option value="mona-lisa.jpg">mona-lisa.jpg</option>
				<option value="theKiss.jpg">theKiss.jpg</option>
				<option value="young-pearl.jpg">young-pearl.jpg</option>
			</select>
			Filter: 
			<select id="filter">
				<option value="">filter</option>
				<option value="darker">Darker</option>
				<option value="lighter">Lighter</option>
			</select>
		</div>
	</main>
</body>
</html>

Kita membuat sebuah elemen canvas dengan ukuran height 320px dan width 460px (sesuai dengan requirements soal). Selanjutnya membuat select untuk image dan juga filter. Disini kita menambahkan id untuk element canvas dan select agar mudah kita targetkan elementnya dengan JavaScript. Perhatikan di select ber id image, value disitu adalah daftar image yang akan kita tampilkan, ketika diubah canvas akan merender image tersebut. Masukan semua gambar yang akan digunakan ke dalam folder assets agar lebih rapi.

Membuat Filter dengan JavaScript

Disini adalah langkah yang paling penting, kita akan menggunakan JavaScript untuk mengakses objek canvas yang tersedia. Disini saya akan menggunakan class agar mudah mengisolasi dan menggunakan kembali fungsi yang ada. Silahkan membuat sebuah class bernama CanvasFilter di dalam file berekstensi js (disini saya akan menamainya canvas-filter.js):

class CanvasFilter {
	// Method atau fungsi akan kita taruh disini
}

Selanjutnya kita akan membuat constructor (method/fungsi yang akan dipanggil ketika class di panggil). Di dalam constructor ini kita akan memberikan sebuah argumen yang akan berisikan dom canvas yang kita gunakan:

constructor(dom){
	this.ctx = dom.getContext('2d');
	this.image = new Image();
	
	var self = this;
	this.image.onload=function(){
		self.renderImage()
	}
	this.filter = '';
}

Informasi

di dalam variable image kita memanggil new Image, Image disini adalah sebuah alias dari document.createElement('img') yang akan membuat sebuah objek dom Image baru.

Disana kita menyimpan context dari canvasnya kedalam variable ctx yang berada didalam class tersebut. Juga ada variable image yang akan menyimpan image yang akan di draw nantinya. Kita juga membuat variable lokal self, variable ini berguna untuk kode dibawahnya.

onload berisikan fungsi yang dipanggil ketika objek image yang telah kita buat tadi src (source imagenya) sudah terload oleh browser. Kita tidak menggunakan this didalam callback function karena akan menimbulkan error (ketika menggunakan this didalam callback function objek image, this yang akan digunakan adalah this dari objek image, bukan this dari tempat memanggilnya / canvasFilter). Method renderImage akan kita buat nanti.

Selanjutnya, kita akan membuat method untuk mengubah jenis filter dan gambar yang akan di draw/render:

setImage(url){
	this.image.src = url;
	this.render();
}
setFilter(filter){
	this.filter = filter;
	this.draw();
	this.renderImage();
}

Didalam method setImage kita mengatur src (source) dari objek Image yang sudah kita buat sebelumnya. Perhatikan juga di kedua method tersebut kita memanggil this.render() yang memanggil method render di class ini sendiri (CanvasFilter). Method render akan kita gunakan sebagai method yang akan merender ulang content dari canvas setiap kali setImage atau setFilter dipanggil. Dibawah ini adalah method render serta renderImage:

render(){
	this.ctx.fillStyle = "#42c78b";
	this.ctx.fillRect(0, 0, 460, 320);
}
renderImage(){
	this.ctx.drawImage(this.image, 10, 10, 210, 300);
	
	if(this.filter){
		this.ctx.drawImage(this.image, 240, 10, 210, 300);

		switch(this.filter){
			case 'darker':
				this.ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
				break;

			case 'lighter':
				this.ctx.fillStyle="rgba(255, 255, 255, 0.6)";
				break;
		}
		this.ctx.fillRect(240, 10, 210, 300);
	}
}

Di method render kita hanya akan melakukan resetting canvas dengan background berwarna #42c78b (hijau). fillStyle berfungsi untuk mendefinisikan warna yang akan kita gunakan. Kita menggunakan fungsi fillRect untuk melakukan fill warna berbentuk rectangle (atau kotak).

Syntaxnya fillRect(x, y, w, h). x berisikan koordinat sumbu x (horizontal) tempat awal rectangle akan dibentuk, y berisikan koordinat sumbu y (vertikal) tempat awal rectangle akan dibentuk. Jika kita isikan masing masing 0 maka rectangle akan dibentuk diawali dari pojok kiri atas canvas. w adalah width (panjang) dari rectangle yang akan kita buat, h adalah height (tinggi) dari rectangle yang akan kita buat. Di awal sudah saya jelaskan jika kita membuat objek canvas dengan width 460px dan height 320px. Jadi untuk menutupi seluruh objek canvas menjadi hijau kita gunakan fillRect(0, 0, 460, 320).

Didalam method renderImage kita menggunakan fungsi drawImage dari canvas context. Syntaxnya drawImage(image_object, x, y, w, h). image_object adalah objek image/dom image yang akan kita lakukan drawing (disini kita menggunakan this.image yang sudah kita definisikan di awal. lalu x,y,w,h sama artinya seperti fungsi fillReact.

Didalam method renderImage kita juga melakukan rendering filter (didalam blok if(this.filter) ). Untuk melakukan filter darker kita gunakan warna rgba(0, 0, 0, 0.6) berarti read = 0, green = 0, blue = 0, alpha = 0.6 atau warna hitam dengan opacity 0.6. Sedangkan filter lighter kita gunakan warna rgba(255, 255, 255, 0.6) atau putih dengan opacity 0.6.

Jika belum faham logic dari kode-kode diatas cobalah untuk mempraktekkannya sambil memahaminya.

Selanjutnya silahkan ubah file html yang kita buat di awal tadi, source canvas-filter.js tadi di dalam tag head:

<head>
 	<!-- KODE LAIN YANG SUDAH ADA SEBELUMNYA -->
 	<script src="canvas-filter.js"></script>
 </head>

Buat juga sebuah kode javascript dibawah (sebelum tag body ditutup) untuk memanggil class CanvasFilter yang sudah kita source:

<script>
	const canvasFilter = new CanvasFilter(document.getElementById('canvas'));
	canvasFilter.setImage('assets/athena.jpg');
	document.getElementById('image').addEventListener('change',function(event){
		canvasFilter.setImage('assets/'+event.target.value);
	});
	document.getElementById('filter').addEventListener('change',function(event){
		canvasFilter.setFilter(event.target.value);
	})
</script>

Lihat, di script tersebut kita panggil class CanvasFilter ke dalam variable canvasFilter, kita masukan dom dari canvas (menggunakan getElementById) sebagai constructor class tersebut.

Lalu kita init gambar awal yang dipilih (sesuai requirements dari soal) assets/athena,jpg

Disitu juga kita membuat event listener untuk select dengan id #image, ketika opsi selectnya berubah yang dipilih kita memanggil method setImage dari class CanvasFilter yang sudah kita init, kita tambahkan prefix assets/ karena semua image sudah kita masukan kedalam folder assets tersebut.

Ada juga event listener untuk select dengan id #filter, yang akan memanggil method setFilter dari class yang sudah kita init tadi.

Hasil Akhir Kode

index.html:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Simple Image Filter</title>
	<script src="canvas-filter.js"></script>
</head>
<body>
	<main>
		<canvas id="canvas" height="320px" width="460px"></canvas>
		<div>
			Image: 
			<select id="image">
				<option value="athena.jpg">Image:</option>
				<option value="athena.jpg">Athena.jpg</option>
				<option value="mona-lisa.jpg">mona-lisa.jpg</option>
				<option value="theKiss.jpg">theKiss.jpg</option>
				<option value="young-pearl.jpg">young-pearl.jpg</option>
			</select>
			Filter: 
			<select id="filter">
				<option value="">filter</option>
				<option value="darker">Darker</option>
				<option value="lighter">Lighter</option>
			</select>
		</div>
	</main>
	<script>
		const canvasFilter = new CanvasFilter(document.getElementById('canvas'));
		canvasFilter.setImage('assets/athena.jpg');
		document.getElementById('image').addEventListener('change',function(event){
			canvasFilter.setImage('assets/'+event.target.value);
		});
		document.getElementById('filter').addEventListener('change',function(event){
			canvasFilter.setFilter(event.target.value);
		})
	</script>
</body>
</html>

canvas-filter.js:

class CanvasFilter {
	constructor(dom){
		this.ctx = dom.getContext('2d');
		this.image = new Image();
		
		var self = this;
		this.image.onload=function(){
			self.renderImage()
		}
		this.filter = '';
	}
	setImage(url){
		this.image.src = url;
		this.render();
	}
	setFilter(filter){
		this.filter = filter;
		this.render();
		this.renderImage();
	}
	render(){
		this.ctx.fillStyle = "#42c78b";
		this.ctx.fillRect(0, 0, 460, 320);
	}
	renderImage(){
		this.ctx.drawImage(this.image, 10, 10, 210, 300);
		
		if(this.filter){
			this.ctx.drawImage(this.image, 240, 10, 210, 300);

			switch(this.filter){
				case 'darker':
					this.ctx.fillStyle = "rgba(0, 0, 0, 0.6)";
					break;

				case 'lighter':
					this.ctx.fillStyle="rgba(255, 255, 255, 0.6)";
					break;
			}
			this.ctx.fillRect(240, 10, 210, 300);
		}
	}
}

Silahkan coba lihat hasilnya di browser, kurang lebih akan berjalan sesuai dengan requirements soal yang ada.

Kesimpulan

Pembuatan filter image ini bisa dibilang cukup mudah bagi mereka yang sudah cukup memahami javascript dan sedikit memahami penggunaan canvas dengan javascript. Karena kita hanya menggunakan fungsi drawImage dan fillRect pada tutorial diatas.

Mungkin ada cara lain yang dapat digunakan untuk mencapai target dari soal ini, yang lebih cepat maupun lebih mudah.

Jangan ragu untuk menghubungi saya untuk berdiskusi maupun bertanya!

Terima kasih telah membaca seri ke 3 dari Pembahasan Soal LKS Nasional 2021 Web Technologies!