7 days of WordPress plugins, themes & templates - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Code
  2. Apps

Bangun Editor Gambar Dengan EaselJS, jQuery, dan API File HTML5

Read Time: 46 mins

Indonesian (Bahasa Indonesia) translation by Keti Pritania (you can also view the original English article)

Ketika HTML5 menjadi lebih populer, lebih banyak browser utama mulai mendukung API-nya. Hari ini, menggunakan Canvas and File APIs, kita dapat membuat editor gambar lengkap, dengan fitur yang setara dengan beberapa aplikasi desktop. Untuk ini, kita akan menggunakan pustaka EaselJS. Ia menggunakan sintaks yang mirip dengan AS3, sehingga akan mudah dipahami baik untuk pemrogram Flash dan JavaScript.


Pratinjau Hasil Akhir

Mari kita lihat hasil akhir yang akan kita kerjakan:




Klik untuk mencoba demo

Bermain-main dengannya untuk merasakan apa yang bisa dilakukannya. Anda bahkan mungkin ingin mengunduh kode sumber lengkap dan melihat-lihat sebelum menggali ke dalam tutorial ini.


Pengenalan

Karena jumlah kode dalam tutorial ini, aku akan pergi melalui masing-masing file dan menjelaskan setiap bagian pada gilirannya, daripada membimbing Anda melalui pembangunan kembali dari awal. Saya akan mencoba untuk komentar semuanya sebanyak yang saya bisa, dan saya percaya Anda akan mengerti segala sesuatu.


Langkah 1: Gaya

Saya akan mulai dalam cara yang tidak biasa, dari file CSS. Pertama membuat style.css file:

Pada baris pertama kami mengubah font dan menonaktifkan menguraikan elemen. Selanjutnya ada hanya gaya definisi: ul #mainmenu adalah elemen menu utama, div #overlay naungan di bawah semua dialog, dan ul #layers adalah panel lapisan yang akan ditampilkan di sisi kanan kanvas. Selanjutnya kita mendefinisikan gaya untuk tombol alat, dan akhirnya kita memiliki sebuah fragmen dari gaya jQuery-UI, karena kita akan membutuhkan bagian ini untuk dialog pemangkasan layer.

Selanjutnya muncul file print.css yang hanya berisi dua baris untuk menyembunyikan semuanya terpisah dari kanvas saat mencetak gambar (gaya ini hanya diterapkan ketika Anda mencetak halaman, karena deklarasinya dalam file HTML).

Baris pertama menyembunyikan semua elemen dalam bagian tubuh, dan baris kedua membuat hanya kanvas terlihat (dan juga selaras dengan sudut kiri atas). Hal ini karena ketika seseorang ingin mencetak foto mereka biasanya tidak ingin mencetak antarmuka.


Langkah 2: Struktur HTML

Anda harus memiliki ide dasar dari antarmuka dari melihat CSS file di atas. Sekarang membuat file index.html tersebut dan masukkan baris berikut:

Perhatikan spesifikasi doctype HTML5. Tidak ada gunanya, panjang DTD spesifikasi; hanya kata html.

Di atas, kami link perpustakaan diperlukan dan JS file. Ini semua termasuk dalam download sumber; Perpustakaan Khusus yang EaselJS, jQuery, dan jQuery UI.

(Untuk pengantar EaselJS, lihat tutorial ini.)

Selanjutnya kita membangun seluruh struktur UI. Hanya ingat: setiap div dengan kelas dialog hanyalah sebuah dialog bagi pengguna untuk memasukkan data yang dibutuhkan untuk melakukan beberapa operasi pada gambar. Jika Anda menjalankan kode ini di browser Anda sekarang, Anda akan melihat beberapa kesalahan 404 di konsol dan bahwa menu itu tidak akan berfungsi, tetapi kami akan memperbaikinya ketika kami membuat file ui.js.


Langkah 3: Objek Aplikasi Utama

Ini adalah praktik yang baik untuk membungkus semua aplikasi Anda terkait fungsi dan variabel di dalam satu objek, untuk mencegah mereka dari overriden oleh pustaka eksternal atau bahkan skrip Anda sendiri. Objek kami akan terlihat seperti ini:

(Saya memulai semua nama variabel dan fungsi dengan huruf kecil berdasarkan pada Konvensi Kode Douglas Crockford untuk Bahasa Pemrograman JavaScript.)

Di app.stage kita akan mengadakan referensi ke objek Stage untuk aplikasi kita. Jika Anda telah mengkodekan apa pun di ActionScript, pikirkan Panggung ini seperti AS3. Ini memiliki daftar tampilan yang ditarik ke elemen kanvas pada setiap pembaruan. App.canvas bervariasi dengan mengacu pada elemen kanvas di dalam dokumen html kami. Kami akan menggunakannya untuk membuat Panggung dan untuk mengubah ukurannya bersama dengan jendela.

Array app.layers akan menampung semua layer gambar, dan app.tool berisi nilai alat yang sebenarnya dipilih. App.callbacks akan menahan setiap panggilan balik acara yang perlu kami tentukan (misalnya mengklik tombol menu), app.renameLayer memegang nomor dari lapisan yang benar-benar diganti namanya, dan app.undoBuffer dan app.redoBuffer adalah larik untuk menahan aplikasi yang dicadangkan. lapisan negara untuk membuat fungsi undo dan redo berfungsi.

Anda juga perlu menambahkan empat baris ini sebelum definisi app (mereka hanya konstanta ID alat):


Langkah 4: Metode Berguna

Sekarang, kita akan mendefinisikan metode objek ini. Pertama tambahkan metode refreshLayers () dan sortLayers () berikut:

Harap dicatat bahwa di dalam objek Anda perlu mendeklarasikan variabel dan fungsi dengan ':' bukannya '='.

Metode sortLayers() dipanggil ketika pengguna menyeret lapisan dalam panel Lapisan; refreshLayers() disebut sangat sering, karena menciptakan ulang app.stage dan menambahkan semua layer ke panggung sekali lagi, mengatur properti mereka dan menerapkan callback peristiwa. Callback ini memungkinkan Anda untuk memindahkan lapisan dan mengedit teks pada lapisan teks. Ini adalah fungsi yang sangat penting karena juga menambahkan semua lapisan ke panel Lapisan di UI dan menonaktifkan tombol alat di menu (jika tidak ada lapisan) dan memungkinkan mereka juga (bila ada setidaknya satu lapisan).

Sebelum refreshLayers (), masukkan fungsi helper lainnya (ingat untuk menambahkan koma setelah yang terakhir!):

Lapisan aktif adalah yang mana Anda menerapkan semua operasi (tranformations, menambahkan filter, dll.). Anda dapat mengaktifkan lapisan dengan mengkliknya pada panel Lapisan atau menggunakan alat Pilih di kanvas.

Seperti yang Anda lihat parameter parameter activateLayer () dapat berupa Bitmap atau angka. Jika itu adalah Bitmap - EaselJS objek untuk gambar - maka aktif properti diatur ke true, dan jika beberapa maka lapisan pada posisi ini dalam app.layers array diaktifkan. getActiveLayer() hanya kembali lapisan yang aktif dan getActiveLayerN() kembali posisi lapisan aktif dalam app.layers array.

Sekelompok terakhir metode di objek ini harus insterted langsung setelah Deklarasi app.redoBuffer dan sebelum orang-orang yang Anda menempatkan di sana sebelumnya:

Seperti yang Anda perhatikan ketika membaca app.refreshLayers (), toString () metode app.layers dikesampingkan oleh kode yang menyiapkan versi stringified dari semua lapisan di dalamnya. Tentu itu akan membuang-buang memori untuk menyimpan semua informasi lapisan di sana, jadi hanya nilai-nilai yang dapat diubah dengan aplikasi yang didukung.

Metode addUndo() mendorong keadaan sebenarnya lapisan ke app.undoBuffer array dan membersihkan app.redoBuffer - karena ketika Anda melakukan tindakan yang dapat dibatalkan maka Anda tidak dapat mengulang apa pun yang dibatalkan sebelum tindakan itu. loadLayers() mengambil dua argumen (array yang kita harus pop negara app.layers dan array yang kita harus mendorong keadaan yang sebenarnya variabel ini), dan melakukan parsing terhadap app.layers didukung.

Sebagai EaselJS's filter contoh mengatakan:

"... filter hanya ditampilkan ketika tampilan objek cache..."

(Dari contoh-contoh EaselJS: filter.)

Ini berarti bahwa Anda perlu untuk memanggil method cache() Bitmap untuk menerapkan filter. Caching dilakukan untuk meningkatkan kinerja - filter diterapkan hanya sekali dan hanya bitmap disaring ditarik. EaselJS adalah caching konten dalam cara yang sangat pintar - itu hanya menyalinnya ke kanvas elemen lain yang tidak ditambahkan ke dokumen (tersembunyi). Saya menyebutkan hal ini karena pada akhir loadLayers() metode ada jika blok yang memeriksa apakah ada penyaring apapun yang harus diupdate pada lapisan ini - dan jika ada, pembaruan cache atau cache elemen.


Langkah 5: inisialisasi

Inisialisasi aplikasi yang utuh sederhana; hanya masukkan ini setelah Deklarasi app:

Ticker ini dilaksanakan EaselJS timer yang memanggil fungsi tick() pendengar untuk mempertahankan FPS stabil yang ditetapkan sebelumnya. Dengan cara ini kita dapat secara otomatis memanggil app.stage.update() untuk redraw panggung.

Pada awal (setelah dokumen telah dimuat) kami menetapkan kanvas elemen pertama pada halaman yang menemukan $ (jQuery) fungsi untuk app.canvas, maka kami menonaktifkan memilih apa dokumen (karena sebaliknya ketika Anda tarik mouse di kanvas ada Efek seperti jika Anda memilih teks).

Kami menetapkan Ticker di FPS-30 (Anda perlu hanya 24 frame per detik untuk menipu mata manusia berpikir itu adalah melihat gerakan) dan mengatur jendela sebagai pendengar Ticker.


Langkah 6: UI penolong fungsi

Sekarang saatnya untuk membawa menu kami dan seluruh user interface untuk hidup. Ui.js file akan terdiri hampir seluruhnya dari fungsi jQuery, sehingga benar-benar mudah untuk memahami. Mari kita mulai dengan fungsi pembantu:

Variabel importFile akan menginformasikan kepada kami tentang apakah kita membuka sebuah file atau mengimpor.

Nama-nama showDialog() dan hideDialog() fungsi berbicara sendiri - meskipun satu hal interesing dalam fungsi hideDialog() adalah bagaimana memeriksa apakah semua dialog yang tersembunyi dengan jQuery ': terlihat ' kelas pseudo, hanya untuk kemudian menyembunyikan hamparan. Pada akhirnya itu terbukti menjadi tidak berguna karena tidak ada situasi bahwa lebih dari satu dialog di layar, tapi aku meninggalkannya untuk penggunaan masa depan Anda; mungkin itu akan datang berguna.


Langkah 7: Mengubah ukuran panggung

Sekarang kita harus melakukan sesuatu ketika pengguna mengubah ukuran jendela browser. Ini adalah ketika peristiwa yang mengubah ukuran jendela datang ke dalam bermain. Dipecat setiap kali pengguna mengubah ukuran jendela browser:

Pertama kami pusat semua dialog menggunakan jQuery's each() fungsi. Ini panggilan callback untuk setiap item yang cocok dengan pemilih dalam fungsi $.

Kemudian, kita harus menetapkan kanvas lebar dan tinggi - tetapi tidak dalam CSS karena ini akan strech gambar dalam kanvas, dan kita tidak ingin yang. Ketinggian menu adalah 37px sehingga kita menetapkan kanvas tinggi jendela tinggi minus 37px. Sama untuk lebar, tapi kali ini kami punya untuk mengurangi lebar panel lapisan yang 232px. Kami juga Resize menu dan panel lapisan untuk menyesuaikan jendela (di sini kita dapat menggunakan CSS).

Setelah itu kita perlu menyegarkan lapisan untuk memastikan mereka selalu up-to-date ketika jendela diubah ukurannya. Hal terakhir adalah untuk memindahkan dialog tanaman dalam kasus pengguna diubah ukurannya jendela ketika tanam lapisan.


Langkah 8: Mengikat semua bersama-sama

Tombol menu's harus diikat ke callback yang ditentukan dalam app.callbacks, dan juga kita perlu untuk mengikat keydown event untuk input dan klik untuk tombol dialog. Kalimat terakhir mungkin kedengarannya rumit, tetapi ketika Anda melihat kode ini akan menjadi jelas:

Satu hal yang perlu diingat: ketika Anda melakukan apa-apa dengan jQuery dan setiap elemen HTML, melakukan itu di dalam document.ready callback, karena hanya maka Anda dapat yakin bahwa semua elemen, Anda menggunakan yang sudah diberikan.

Kode di atas lama, tetapi karena ada banyak bagian yang sama tetapi berbeda dalam bagian-bagian yang tidak memungkinkan kita untuk bungkus dalam setiap fungsi pembantu. Bagian yang disorot adalah tempat kami mengatur semua callback untuk tombol dan input dialog.

Selanjutnya Anda harus melihat pada fungsi live () yang kita gunakan untuk mengikat event klik ke tombol layer (pada panel Layer) - fungsi live () menambahkan callback untuk setiap elemen yang akan cocok dengan pemilih ini di masa depan, membuatnya sangat berguna karena kami membuat daftar layer baru di setiap app.refreshLayers () panggilan.

Fungsi terakhir di sini adalah $ (window) .resize () yang secara manual memunculkan peristiwa pengubahan ukuran jendela. Inilah sebabnya mengapa penting untuk menghubungkan skrip secara berurutan, karena jika ui.js ditambahkan ke HTML sebelum main.js, layer akan di-refresh sebelum definisi fungsi, yang terkadang dapat menyebabkan hasil yang tidak diharapkan, sehingga menemukan bug bahkan lebih keras.

Jika Anda menjalankan aplikasi sekarang, Anda akan melihat menu yang berfungsi baik dan mengubah ukuran UI dengan tepat, tetapi tetap tidak ada tombol yang akan melakukan apa pun selain melemparkan kesalahan ke konsol saat Anda mengekliknya.


Langkah 9: Membuka File

Sekarang kita akan menggunakan API lain dari spesifikasi HTML5: File API. Ini memungkinkan kita untuk membuka file dari komputer pengguna, tetapi hanya ketika ia memilihnya di kolom input file OS (untuk mencegah aplikasi web mencuri data pribadi Anda).

Harap perhatikan bahwa jika Anda akan menjalankan aplikasi ini di komputer lokal, Anda perlu mengatur server lokal atau menambahkan parameter --allow-file-access-from-files ketika menjalankan Chrome, karena membuka file dari dalam halaman web lokal dinonaktifkan oleh default.

Di file file.js kita juga akan meletakkan fungsi untuk menyimpan dan mencetak gambar, jadi mari kita mulai dengan keempat pembantu ini:

Fungsi openFile akan digunakan untuk membuka gambar dan menambahkannya ke layer. Jika kita memilih 'Buka File' dari menu maka konten lama akan dihapus, sedangkan 'Impor File' akan menambahkan layer baru ke gambar. (Dalam fungsi, jika parameter pertama benar maka kita membuka file, jika tidak kita mengimpornya).

openURL membuka file menggunakan fungsi yang sama, tetapi dari sumber eksternal (dan sejauh yang saya tahu Chrome menonaktifkan akses ke data pixel asal lintas domain yang membuat gambar mereka cukup berguna). Karena kami tidak dapat menyimpan file ke disk pengguna kami hanya membuka jendela lain yang mengandung hanya gambar yang mewakili tahap yang sebenarnya; pengguna dapat kemudian klik kanan untuk menyimpan gambar.

Percetakan dicapai dengan memanggil window.print(). Kita tidak bisa, tentu saja, mencetak apa pun tanpa membiarkan pengguna mengetahui, sehingga fungsi ini akan membuka dialog cetak default dimana pengguna dapat memilih preferensi pencetakan.

Sekarang kita akan menentukan beberapa callback untuk tombol dalam menu:

Seperti Anda dapat melihat kode sangat singkat namun sangat kuat. Pertama (app.callbacks.openFile) callback kita memeriksa apakah dibuka file gambar, dan berhenti jika tidak. Kemudian kita menciptakan FileReader baru, menetapkan yang onload callback untuk membuka file, dan memanggil metode readAsDataURL(file) yang load file dan output hasil sebagai URL data untuk kita untuk membaca.

(Juga perlu diketahui bahwa kami membersihkan undo dan mengulang array; kita harus melakukan ini karena kami tidak dapat mengembalikan gambar jika kami menghapus itu - pengguna harus secara manual memilih ulang file dari input.)

Simpan file, membuka app di browser Anda, dan Anda dapat akhirnya melakukan sesuatu! Tidak banyak, tetapi jika Anda melakukan ini untuk pertama kalinya mungkin menarik untuk dapat memuat beberapa gambar ke browser, bahkan jika Anda hanya dapat bergerak mereka.


Langkah 10: Teks lapisan

Sekarang bahwa Anda dapat membuka dan mengimpor gambar Anda bisa menambahkan beberapa teks. Ada satu hal yang benar-benar berguna dengan kanvas - Anda menentukan teks seperti dalam atribut font CSS. Dan EaselJS sepenuhnya menggunakan fitur itu.

Kami akan menentukan alat teks dalam tools.js file. Tambahkan baris berikut:

EditText variabel benar ketika kita mengubah sifat lapisan teks yang sudah ada agar tidak perlu membuat yang baru.

Fungsi pertama adalah, seperti biasa, fungsi pembantu. Ia memeriksa apakah kita mengedit lapisan teks yang ada atau menambahkan yang baru, kemudian menciptakan sebuah obyek teks baru dan menambahkannya ke lapisan aplikasi. Karena semua objek dalam EaselJS memperpanjang DisplayObject dasar, kita dapat menggunakan teks dan Bitmap dengan cara yang sama; hanya sifat mereka berbeda.

Fungsi kedua adalah callback. Hal pertama yang perlu kita lakukan adalah untuk memeriksa jenis penyelenggaraan apa kita menerima (karena kita menggunakan hanya satu callback yang baik untuk menangani klik tombol dan menekan masuk di dalam input). Kemudian kita sebut penolong sebelumnya.


Langkah 11: Lapisan transformasi

Transformasi lapisan sederhana memang sangat sederhana dengan EaselJS. Segala sesuatu yang aku akan menunjukkan di sini dapat dilakukan hanya dengan mengubah lapisan (Bitmap atau teks) properti.

Saya akan mulai dengan menjelaskan titik pendaftaran. Properti regX dan regy menentukan titik dari mana rotasi dan posisi dihitung - itu seperti pegangan. Dalam file main.js kami menetapkan titik ini ke pusat gambar, untuk membuat transformasi lapisan lebih mudah. Semua fungsi transformasi lapisan akan masuk ke file layer.js.

Dalam bagian ini - seperti biasa - ada penolong fungsi. Ada beberapa hal yang saya ingin mengatakan tentang ini potongan kode.

Pertama: kita perlu memanggil fungsi addUndo() sebelum kita mulai mengubah apa-apa. Mengapa? Karena kami ingin kembali ke keadaan sebelum beberapa operasi dilakukan ketika kami mengklik tombol undo.

Juga melihat affectImage variabel. Kami akan mengaturnya untuk benar ketika kita ingin mempengaruhi seluruh gambar; dalam fungsi hampir setiap ada if pernyataan yang memeriksa jika kita mempengaruhi seluruh gambar, dan (jika demikian) kembali hasil dari fungsi sesuai image*().

Sekarang meletakkan pemanggilan kode dalam file:

Lain sekelompok panggilan jQuery. Kami juga memeriksa jenis acara karena kita bisa mendapatkan fungsi-fungsi yang disebut oleh tombol atau input field. Setiap fungsi dalam kode di atas adalah sama: memeriksa jenis peristiwa, mendapatkan nilai input dari dialog dan panggilan fungsi.

Callback di atas ini bagian dari kode terikat input yang hanya harus mendapatkan angka-angka di dalam mereka.


Langkah 12: Transformasi: skala

Mari kita mulai dengan menambahkan kode sumber ini; Saya akan menjelaskan itu kemudian. Letakkan kode berikut ke dalam image.js file:

Kami hanya perulangan melalui semua lapisan pengaturan mereka scaleX dan properti scaleY. Tapi gambar akan tampak aneh jika kita hanya skala lapisan. Kita juga harus bergerak setiap lapisan untuk membuat fungsi ini bekerja dengan baik.


Langkah 13: Gambar transformasi: rotasi

Rotasi akan sedikit lebih sulit daripada scaling. Tapi pertama adalah kode etik ini; menempatkan ini juga ke dalam image.js file:

Kita tentu saja menambahkan lapisan rotasi, tetapi kode yang saya disorot baru. Hal ini didasarkan pada persamaan berputar titik dalam sistem koordinat Kartesius:

Mana Φ adalah sudut. Sehingga kode disorot hanya terjemahan dari persamaan di atas ke dalam kode JavaScript (ditambah konversi dari derajat ke radian, karena fungsi trigonometri Math perpustakaan mengambil radian sebagai parameter dan satu radian adalah persis pi/180 derajat).


Langkah 14: Transformasi: condong

Membuat ini sangat mirip dengan rotasi, karena pada dasarnya rotasi tapi dengan dua berbeda sudut untuk dua arah. Lihatlah kode:

Anda melihat perbedaan? Kami hanya menggunakan radx untuk x posisi dan rady untuk posisi y. Hal ini membuat gambar condong benar (yang memberikan efek yang cukup baik, saya harus mengatakan).


Langkah 15: Transformasi: Flip

Ini adalah modifikasi dari fungsi imageScale(). Ianya tidak terakhir karena itu adalah yang paling sulit fungsi transformasi gambar, hanya karena itu pada posisi terakhir di menu. Kode:

Tentu saja kita dapat mengabaikan callback - kita sudah tahu apa yang mereka lakukan. Kita harus fokus pada pertama dua fungsi. Mereka hanya perulangan melalui lapisan pengaturan mereka scaleX atau scaleY ke nilai yang sebaliknya - ini adalah apa yang kita kenal sebagai membalik: skala yang negatif. Juga x atau y harus terbalik untuk membuat gambar yang benar-benar melihat membalik.

Ini adalah transformasi gambar terakhir. Sekarang kita akan membuat sesuatu yang lebih maju - filter!


Langkah 16: Sederhana filter: Pendahuluan

Saya menyebutnya sederhana karena kami menggunakan filter yang dibangun EaselJS: ColorFilter dan ColorMatrixFilter. Ini mengubah gambar piksel dengan piksel, sehingga dengan gambar besar dan rumit filter, Anda dapat membuat browser lag untuk sementara atau bahkan menghentikan sepenuhnya.

Sebelum kita menerapkan mereka saya akan menjelaskan apa setiap filter.

ColorFilter mengambil parameter delapan penciptaan:

Bila penyaring diterapkan membagi gambar ke empat saluran (merah, hijau, biru dan alpha) dan untuk setiap saluran mengalikan setiap nilai dengan kelipatan sesuai dan menambahkan offset sesuai. (Sebenarnya, gambar tidak benar-benar dibagi; ini adalah metafora yang berguna.)

ColorMatrixFilter mengambil satu-satunya parameter penciptaan:

Matriks memiliki format berikut:

Bila penyaring ini diterapkan, itu juga (kiasan) perpecahan gambar ke dalam saluran, dan kemudian itu mengalikan setiap nilai oleh satu sama lain. Sebagai contoh, persamaan untuk nilai sebuah pixel dalam saluran merah setelah melewati filter adalah:

Ini adalah sama untuk hijau, biru dan alpha juga, hanya dengan variabel yang berbeda dari matriks (gr, gg, gb, ga hijau, dan seterusnya). Filter ini sedikit lebih maju dari ColorFilter, karena masing-masing warna tergantung pada warna lainnya dari pixel.

Untuk keterangan lebih lanjut, lihat tutorial ini.


Langkah 17: Sederhana filter: fungsi pembantu

Ini adalah salah satu dua fungsi pembantu yang akan digunakan di sini, tapi kami akan menggunakannya untuk setiap filter, juga untuk yang canggih.

Letakkan kode ini di awal filters.js file:

Tidak semua pekerjaan untuk kita: meraih lapisan aktif; Jika tidak ada penyaring kemudian menciptakan array filter; dan menambahkan filter.

Setelah itu kita harus meng-cache layer agar efek filter terlihat, jadi kita periksa apakah kita sudah meng-cache layer ini (misalnya ketika memotongnya) dan memanggil updateCache () atau cache () sebagaimana mestinya.

Berikut gambar yang akan saya gunakan untuk menampilkan efek filter:



Langkah 18: Sederhana filter: kecerahan

Untuk efek ini kita akan menggunakan ColorFilter, karena perubahan kecerahan hanya berubah semua saluran nilai (merah, hijau, biru) dalam lapisan dengan nilai yang sama.

Ini kode (masukkan ke file filters.js):

Seperti yang saya sebutkan sebelumnya, fungsi pembantu kami adalah melakukan segala sesuatu bagi kita, kita hanya perlu membuat penyaring baru. Di sini kami menciptakan ColorFilter dengan pengganda merah, hijau, biru yang diatur ke nilai dan alpha set 1.0 (kita tidak ingin alpha untuk disentuh oleh filter ini).

Berikut adalah contoh hasil dari filter ini:



Langkah 19: Filter Sederhana: Mewarnai

Colorify akan membuat beberapa saluran bernilai lebih besar atau lebih kecil untuk mengubah warna keseluruhan gambar, jadi kita akan kembali menggunakan ColorFilter.

Lihatlah kode:

Sekali lagi, pekerjaan kotor ditangani oleh applyFilter, dan kami hanya fokus pada pembuatan objek filter. Di sini kita akan menggunakan empat parameter terakhir dari konstruktor ColorFilter. Mereka ditambahkan ke saluran, sehingga mereka sangat sesuai dengan kebutuhan kita.

Di bawah ini adalah contoh hasil dari filter ini:



Langkah 20: Filter Sederhana: Desaturasi

Desaturasi adalah proses menghilangkan kejenuhan - dengan kata-kata sederhana, membuat gambar hitam dan putih. Untuk melakukan itu kita perlu menghitung luminositas setiap piksel dan mengatur semua warna ke nilai ini. Persamaan Luminositas simpliest melibatkan hanya menambahkan sama jumlah dari semua warna, dan untuk bahwa kita dapat menggunakan ColorMatrixFilter:

Seperti yang saya katakan sebelumnya - ambil jumlah yang sama dari tiga warna dan tambahkan. Kita lagi jangan sentuh alpha seperti itu tidak mengandung nilai warna apapun.

Tidak ada contoh gambar hasil karena sudah hitam dan putih; filter tidak berpengaruh.


Langkah 21: Lilitan filter

Filter lilitan sedikit lebih maju dari ColorMatrixFilter. Ia juga menggunakan matriks, tetapi matriks konvolusi mewakili pengganda piksel di sekitar piksel sebenarnya.

Katakanlah kita memiliki contoh matriks 3x3 konvolusi ini (sudah diwakili sebagai susunan JavaScript):

Dan (misalnya) kita melihat pada bagian dari gambar mana pixel terlihat seperti ini (setiap nomor mewakili kekuatan saluran warna merah; kita mengabaikan sisanya untuk kesederhanaan):

Dengan lilitan filter, kami adalah memodifikasi pixel di tengah (nilai saat ini: 56). Jadi kita mulai dengan mengalikan setiap nilai warna di sekitar pixel itu dengan yang pengganda dari lilitan array, dan kemudian kita menambahkan mereka bersama-sama. Kami mendapatkan persamaan berikut:

Jadi sekarang kami menetapkan nilai kanal merah baru piksel ke 44 - tetapi dalam larik data baru, karena kita masih perlu mempertahankan nilai lama 56 untuk memodifikasi piksel lain dalam gambar. Ini berarti bahwa, ketika menerapkan filter, kita benar-benar membuat salinan gambar daripada memodifikasi ada satu di-tempat.

Dengan sebuah matriks yang lebih besar, Anda dapat melihat bagaimana rumus akan mendapatkan lebih kompleks, seperti setiap pixel tergantung pada data dari sekitarnya piksel lebih; untuk alasan ini, sebuah matriks yang lebih besar memerlukan waktu berjalan lebih lama. Ketika menjalankan filter lilitan, browser biasanya akan membeku untuk sementara.

Semua filter lanjutan yang akan kami buat akan bergantung pada filter ini, jadi akan lebih baik jika Anda memahami cara kerjanya. Sekali lagi, informasi lebih lanjut tersedia di sini.

Juga Anda harus ingat bahwa jumlah semua nilai di dalam matriks konvolusi harus sama dengan nol atau satu - jika tidak, Anda akan mendapatkan hasil yang aneh. Untuk membuat ini lebih sederhana kita dapat menggunakan faktor dan variabel offset. Setelah menghitung nilai piksel (dengan persamaan di atas), kita mengalikan seluruh nilai dengan faktor dan menambahkan offset. Ini menyederhanakan pembuatan, misalnya, filter blur (yang akan kita dapatkan dalam satu menit), di mana semua nilai matriks konvolusi adalah sama.

Sayangnya ada tidak ada implementasi ConvolutionFilter di EaselJS, jadi kita harus menulis satu. Mengikuti contoh ColorFilter saya membuat kode ini:

Anda dapat melewati semua metode kecuali untuk applyFilter, karena mereka digunakan oleh EaselJS untuk menginisialisasi filter.

applyFilter() dipanggil ketika kami menerapkan filter ke gambar. Pertama kita harus mendapatkan data gambar dari kanvas, maka saya menggunakan trik dengan JSON.parse(JSON.stringify(imageData.data)) - karena kita ingin salinan data gambar dan imageData.data objek tidak memiliki metode clone() atau slice() untuk mencapai hal ini, jadi kami menggunakan ini sulit untuk benar-benar menyalin objek dan semua sifat-sifatnya.

Informasi warna disimpan dalam data ini seperti ini:

Jadi setiap piksel mengambil empat item array - satu untuk setiap saluran. Akhirnya, setelah iterasi melalui semua data pixel, kita memanggil putImageData() pada target untuk menyimpan hasilnya.


Langkah 22: Lilitan filter: kabur

Ini adalah efek konvolusi yang paling sederhana, jadi saya memutuskan untuk membiarkan pengguna mengatur radius filter (yang akan menghasilkan pengaturan ukuran array) untuk membuatnya lebih kompleks. Berikut adalah fungsi kita akan menggunakan:

Ini menghasilkan matriks kabur, kemudian menerapkan filter.

Mengapa kita mengatur faktor untuk Math.pow (radius * 2, 2)? Karena seperti saya katakan sebelumnya: jumlah dari semua bidang array harus sama dengan nol atau satu; Jika kita membagi mereka semua dengan jumlah mereka kita akan selalu mendapatkan 1.

Di bawah ini adalah hasil dari filter ini:



Langkah 23: Gaussian Blur


Filter lilitan ini apa yang disebut karena menggunakan nilai-nilai dari standar distribusi Gaussian (digambarkan di atas) dimasukkan ke dalam matriks lilitan. Untuk menyederhanakan tugas kita membiarkan pengguna memilih hanya tiga nilai-nilai radius, karena menerapkan filter untuk radius lebih besar akan mengambil terlalu banyak waktu (3px radius sudah matriks 7 oleh 7).

Berikut adalah fungsi:

Kita menetapkan matriks di luar fungsi sehingga tidak untuk membuang-buang waktu menetapkan setiap kali pengguna memilih itu dari menu. Dalam fungsi ini kami hanya memilih matriks tertentu dan menerapkan filter. Anda tentu saja dapat menambahkan lebih banyak nilai radius jika Anda mau.

Di bawah ini adalah hasil dari filter ini:



Langkah 24: Deteksi Tepi


Deteksi tepi adalah teknik yang sering digunakan dalam AI robot untuk membantu mereka bergerak, karena deteksi tepi hanya menyisakan tepi gambar. Hal ini juga efek yang benar-benar bagus untuk digunakan dalam seni.

Untuk mencapai ini kita menggunakan perkiraan nilai-nilai pertama dari distribusi Laplace (gambar di atas) dengan b = 1/4. Semua fungsi dari titik ini hanya akan memiliki matriks yang berbeda:

Di bawah ini adalah hasil dari filter ini:



Langkah 25: Edge meningkatkan

Filter ini memiliki efek yang sama dengan sebelumnya, tetapi meningkatkan tepi tanpa menghilangkan sisa gambar - membuatnya sempurna untuk penggunaan artistik. Ini adalah benar-benar matriks saya digunakan untuk menjelaskan kepada Anda bagaimana lilitan Filter bekerja:

Di bawah ini adalah hasil dari filter ini:



Langkah 26: Emboss

Emboss filter menambah sedikit efek 3D gambar dengan menyoroti sudut kiri-bawah tepi (jadi itu juga penyaring deteksi tepi).

Fungsi:

Di bawah ini adalah hasil dari filter ini:



Langkah 27: Pertajam

Kita semua tahu apa mengasah. Ini dicapai dengan sedikit modifikasi deteksi tepi:

Di bawah ini adalah hasil dari filter ini:


Anda lihat perbedaannya? Hal ini sangat kecil dalam lilitan matriks, tapi gambar dihasilkan lebih tajam.

Itu adalah filter terakhir, tetapi Anda dapat menambahkan lebih banyak jika Anda mau. Hanya mencari beberapa di Internet atau percobaan untuk membuat Anda sendiri yang unik.


Langkah 28: Filter Callback

Ini akan menjadi sekelompok panggilan jQuery berikutnya, tetapi sebelum itu kita memerlukan fungsi pembantu kedua kita di file ini:

Dibutuhkan tiga parameter:

  • e - objek acara,
  • Val - nilai untuk lulus ke fungsi, dan
  • func - fungsi untuk panggilan dengan nilai sebelumnya.
  • Ini menciptakan singkatan yang bisa kita gunakan dalam kode panggilan balik berikut; tempelkan saja di bawah helper:


Langkah 29: Scripting: Pendahuluan

Mengizinkan pengguna untuk menggunakan beberapa bahasa scripting dalam aplikasi Anda adalah fitur yang sangat berguna. Ini memungkinkan pengguna untuk mengotomatisasi pekerjaannya, atau ketika dia mencapai beberapa efek yang bagus dia dapat membaginya dengan orang lain, dan orang ini juga akan mendapatkan efek yang sama. Dan karena kami menulis seluruh aplikasi dalam JavaScript - yang merupakan bahasa scripting itu sendiri - sangat mudah untuk membuat fitur semacam itu.

Menggunakan fungsi eval() kita kita dapat menjalankan beberapa JavaScript dari string, dan string ini akan menjadi pengguna script.

Anda mungkin pernah membaca bahwa menggunakan fungsi eval() adalah praktik yang sangat buruk. Tentu saja jika Anda harus menggunakannya di dalam kode Anda, maka kalimat ini benar, karena ini menonaktifkan semua cache atau kompilasi yang menggunakan mesin JavaScript modern untuk mempercepat kode. Ini juga menciptakan instance dari parser JavaScript, yang memboroskan memori sampai selesai dengan kode. Jadi hindari menggunakan eval() seperti ini:

Kode di atas sangat buruk. Anda tidak harus menggunakan eval() seperti ini. Contoh di atas dapat diperbaiki menggunakan tanda kurung siku:

Dalam kasus kami semuanya baik-baik saja, karena menulis juru bahasa Anda sendiri akan sangat membuang waktu dan sumber daya (artinya lebih banyak atau lebih besar file untuk diunduh oleh pengguna).


Langkah 30: Skrip: aman Eval

Karena kami mengeksekusi skrip pengguna, kami harus memastikan bahwa dia tidak secara tidak sengaja merusak hasil yang dia kerjakan karena dia salah ketik nama fungsi atau nomor lapisan. Itulah mengapa ia harus memastikan bahwa pengguna tidak dapat mengakses setiap jendela atau app metode langsung.

Untuk alasan ini, kami membuat fungsi kami terlihat seperti ini:

(Meletakkan kode ini ke dalam scripts.js file.)

Kita menyembunyikan dialog pertama karena itu tidak hanya akan bertahan tidak sampai script selesai, dan mungkin pengguna ingin melihat ia script di tempat kerja. Kemudian kita sebut kode yang tersedia, tapi kami mengganti semua jendela dan app terkait panggilan, sehingga pengguna tidak dapat menghapus semua lapisan atau menutup jendela oleh kesalahan.

Sedikit peringatan di sini: pengguna masih bisa melakukan sesuatu dengan variabel ini jika ia menggunakan fungsi eval() dalam kode - tapi kemudian kita bertanya kepadanya Apakah dia benar-benar ingin melakukan ini.

Sekarang kita harus menambahkan fungsi callback kecil ini:

Itu adalah sistem script lengkap. Pergi ke depan dan check it out dengan melewati beberapa kode yang bagus untuk dialog 'Jalankan Script'. Coba gunakan eval() di sana untuk melihat bahwa itu menanyakan Anda apakah Anda benar-benar ingin melakukan ini.


Kesimpulan

Seperti yang Anda lihat, HTML5 Canvas adalah hal yang sangat kuat. Tapi kita hanya menggores permukaan apa yang bisa dilakukan dengan itu. Kami telah membuat aplikasi yang sangat canggih - yang memungkinkan pengguna memuat foto mereka dan membuat beberapa modifikasi pada mereka, lalu menyimpan dan mencetak foto yang diedit - menggunakan JavaScript murni. Beberapa tahun yang lalu itu akan menjadi lelucon.

Juga Anda dipersilakan untuk memperluas aplikasi yang baru Anda buat! Tambahkan lebih banyak filter, ubah antarmuka, tambahkan fungsi yang lebih bermanfaat (misalnya Anda dapat menambahkan lebih banyak properti ke lapisan, mungkin daftar semua filter dengan kemungkinan menghapus dan mengeditnya, atau tombol untuk mengonversi buffer undo menjadi skrip siap untuk pengguna untuk berbagi). Jadilah kreatif dan mungkin Anda akan membuat beberapa hal yang sangat berguna!

Terima kasih untuk membaca tutorial ini, saya berharap saya benar-benar mengajarkan Anda sesuatu yang Anda akan menggunakan beberapa proyek yang besar. Jika Anda memerlukan bantuan dalam pembuatan sesuatu yang canggih dengan HTML5, jangan ragu untuk bertanya pada email kontak saya atau dengan menambahkan komentar ke tutorial ini. Saya akan menjawab Anda segera setelah saya mendapatkan pesan Anda.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Scroll to top
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.