Cara Membuat Plugin Sketch Dengan JavaScript, HTML, dan CSS (Bagian 2)
Diterbitkan: 2022-03-10Seperti yang disebutkan di bagian 1, tutorial ini ditujukan untuk orang yang mengetahui dan menggunakan aplikasi Sketch dan tidak takut mencoba kode juga. Untuk mendapatkan keuntungan maksimal, Anda harus memiliki setidaknya beberapa pengalaman dasar menulis JavaScript (dan, opsional, HTML/CSS).
Di bagian sebelumnya dari tutorial ini, kita belajar tentang file dasar yang membentuk sebuah plugin, dan cara membuat antarmuka pengguna plugin. Di bagian kedua dan terakhir ini, kita akan mempelajari cara menghubungkan antarmuka pengguna ke kode plugin inti dan cara mengimplementasikan fitur utama plugin. Terakhir, kita juga akan mempelajari cara mengoptimalkan kode dan cara kerja plugin.
Membangun Antarmuka Pengguna Plugin: Membuat Antarmuka Web Kami Dan Kode Plugin Sketsa "Berbicara" Satu Sama Lain
Hal berikutnya yang perlu kita lakukan adalah mengatur komunikasi antara antarmuka web kita dan plugin Sketch.
Kami harus dapat mengirim pesan dari antarmuka web kami ke plugin Sketch ketika tombol "Terapkan" di antarmuka web kami diklik. Pesan ini perlu memberi tahu kami tentang pengaturan apa yang telah dimasukkan pengguna — seperti jumlah langkah, jumlah rotasi, jumlah duplikat yang harus dibuat, dan sebagainya.
WKWebView
membuat tugas ini sedikit lebih mudah bagi kami: kami dapat mengirim pesan ke plugin Sketch kami dari kode JavaScript antarmuka web kami dengan menggunakan API window.webkit.messageHandlers
.
Di sisi kode Sketsa kami, kami dapat menggunakan metode lain, addScriptMessageHandler:name:
(atau addScriptMessageHandler_name
) untuk mendaftarkan pengendali pesan yang akan dipanggil setiap kali menerima pesan yang dikirim dari antarmuka web plugin kami.
Mari kita mulai dengan memastikan bahwa kita dapat menerima pesan dari UI web kita. Buka fungsi createWebView
file ui.js
kami, dan tambahkan yang berikut ini:
function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const ourMessageHandler = ... userContentController.addScriptMessageHandler_name( ourMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };
Di sini kita menggunakan properti userContentController
tampilan web untuk menambahkan pengendali pesan yang kita beri nama "sketchPlugin". "Pengendali konten pengguna" ini adalah jembatan yang memastikan pesan tersampaikan dari tampilan web kami.
Anda mungkin memperhatikan sesuatu yang aneh tentang kode di atas: objek yang kita tambahkan sebagai pengendali pesan, ourMessageHandler
, belum ada! Sayangnya, kita tidak bisa hanya menggunakan objek atau fungsi JavaScript biasa sebagai handler, karena metode ini mengharapkan jenis objek asli tertentu.
Beruntung bagi kami, kami dapat mengatasi batasan ini dengan menggunakan MochaJSDelegate
, perpustakaan mini yang saya tulis yang memungkinkan untuk membuat jenis objek asli yang kami butuhkan menggunakan JavaScript biasa. Anda harus mengunduh dan menyimpannya secara manual di bundel plugin Anda di bawah Sketch/MochaJSDelegate.js
.
Untuk menggunakannya, pertama-tama kita harus mengimpornya ke ui.js
. Tambahkan yang berikut ini di bagian atas file:
const MochaJSDelegate = require("./MochaJSDelegate");
Sekarang kita dapat menggunakan MochaJSDelegate
untuk membuat tipe penangan pesan yang addScriptMessageHandler:name:
::
function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { /* handle message here */ } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };
Kode yang baru saja kita tambahkan membuat objek asli yang kita butuhkan. Itu juga mendefinisikan metode pada objek yang bernama userContentController:didReceiveScriptMessage:
— metode ini kemudian dipanggil dengan pesan yang kita inginkan sebagai argumen kedua. Karena kita belum benar-benar mengirim pesan apa pun, kita harus kembali ke sini nanti dan menambahkan beberapa kode untuk benar-benar mengurai dan menangani pesan yang kita terima.
Selanjutnya, kita perlu menambahkan beberapa kode ke antarmuka web kita untuk mengirimkan pesan-pesan itu kepada kita. /Resources/web-ui/script.js
. Anda akan menemukan bahwa saya telah menulis sebagian besar kode yang menangani pengambilan nilai HTML <inputs />
yang akan digunakan pengguna untuk memasukkan opsi mereka.
Apa yang masih tersisa untuk kita lakukan adalah menambahkan kode yang sebenarnya mengirimkan nilai ke kode Sketch kita:
Temukan fungsi apply
dan tambahkan yang berikut di akhir:
// Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));
Di sini kami menggunakan window.webkit.messageHandlers
API yang kami sebutkan sebelumnya untuk mengakses pengendali pesan yang kami daftarkan di atas sebagai sketchPlugin
. Kemudian kirim pesan ke sana dengan string JSON yang berisi input pengguna.
Mari kita pastikan semuanya sudah diatur dengan benar. Kembali ke /Sketch/ui.js
. Untuk memastikan kita menerima pesan seperti yang diharapkan, kita akan memodifikasi metode yang kita definisikan sebelumnya sehingga menampilkan dialog saat kita menerima pesan:
function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const UI = require("sketch/ui"); UI.alert("Hey, a message!", wkMessage.body()); } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // ... };
Sekarang jalankan plugin (Anda mungkin perlu menutup jendela Mosaik yang ada yang telah Anda buka terlebih dahulu), masukkan beberapa nilai, lalu klik "Terapkan". Anda akan melihat peringatan seperti di bawah ini — ini berarti semuanya terhubung dengan benar dan pesan kami berhasil dikirim! Jika tidak, kembali ke langkah sebelumnya dan pastikan semuanya telah dilakukan seperti yang dijelaskan.
Sekarang setelah kita dapat mengirim pesan dari antarmuka kita ke plugin, kita dapat melanjutkan untuk menulis kode yang benar-benar melakukan sesuatu yang berguna dengan informasi itu: menghasilkan mosaik lapisan kita.
Menghasilkan Lapisan Mosaik
Mari kita lihat apa yang diperlukan untuk mewujudkannya. Menyederhanakan sedikit, apa yang perlu dilakukan kode kita adalah:
- Temukan dokumen saat ini.
- Temukan lapisan yang dipilih dokumen saat ini.
- Gandakan layer yang dipilih (kami akan menyebutnya layer template ) x beberapa kali.
- Untuk setiap duplikat, atur posisinya, rotasi, opacity, dll., dengan nilai (jumlah) tertentu yang ditetapkan oleh pengguna.
Sekarang kita punya rencana yang masuk akal, mari kita lanjutkan menulis. Berpegang pada pola kami memodulasi kode kami, mari buat file baru, mosaic.js
di folder Sketch/
, dan tambahkan kode berikut ke dalamnya:
function mosaic(options){ }; module.export = mosaic;
Kami akan menggunakan fungsi ini sebagai satu-satunya ekspor modul ini karena membuat API yang lebih sederhana untuk digunakan setelah kami mengimpornya — kami hanya dapat memanggil mosaic()
dengan opsi apa pun yang kami dapatkan dari antarmuka web.
Dua langkah pertama yang perlu kita ambil adalah mendapatkan dokumen saat ini, dan kemudian lapisan yang dipilih. Sketch API memiliki pustaka bawaan untuk manipulasi dokumen yang dapat kita akses dengan mengimpor modul sketch/dom
. Kami hanya membutuhkan objek Document
sekarang, jadi kami akan menariknya keluar secara eksplisit. Di bagian atas file, tambahkan:
const { Document } = require("sketch/dom");
Objek Document
memiliki metode khusus untuk mengakses dokumen saat ini yang dapat kita gunakan, yang disebut getSelectedDocument()
. Setelah kita memiliki instance dokumen saat ini, kita dapat mengakses layer apa pun yang telah dipilih pengguna melalui properti selectedLayers
dari dokumen. Namun, dalam kasus kami, kami hanya peduli dengan pilihan lapisan tunggal, jadi kami hanya akan mengambil lapisan pertama yang telah dipilih pengguna:
function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;
Catatan: Anda mungkin mengharapkan selectedLayers
itu sendiri untuk menjadi sebuah array, tapi ternyata tidak. Sebagai gantinya, ini adalah turunan dari kelas Selection
. Ada alasan untuk ini: kelas Selection
berisi banyak metode pembantu yang berguna untuk memanipulasi seleksi seperti clear, map, reduce, dan forEach. Ini memperlihatkan larik lapisan yang sebenarnya melalui properti layer
.
Mari tambahkan juga beberapa umpan balik peringatan jika pengguna lupa membuka dokumen atau memilih sesuatu:
const UI = require("sketch/ui"); function mosaic(options){ const document = Document.getSelectedDocument(); // Safety check: if(!document){ UI.alert("Mosaic", "️ Please select/focus a document."); return; } // Safety check: const selectedLayer = document.selectedLayers.layers[0]; if(!selectedLayer){ UI.alert("Mosaic", "️ Please select a layer to duplicate."); return; } }; module.export = mosaic;
Sekarang setelah kita menulis kode untuk langkah 1 dan 2 (menemukan dokumen saat ini dan lapisan yang dipilih), kita perlu menangani langkah 3 dan 4:
- Gandakan layer template x beberapa kali.
- Untuk setiap duplikat, atur posisinya, rotasi, opacity, dll., dengan nilai spesifik yang ditetapkan oleh pengguna.
Mari kita mulai dengan menarik semua informasi relevan yang kita butuhkan dari options
: berapa kali untuk menduplikasi, opsi awal, dan opsi langkah. Kita dapat sekali lagi menggunakan destructuring (seperti yang kita lakukan sebelumnya dengan Document
) untuk menarik properti tersebut keluar dari options
:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }
Selanjutnya, mari bersihkan input kita dan pastikan jumlah langkah selalu setidaknya 1:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }
Sekarang kita perlu memastikan bahwa opasitas, rotasi, dll., semua lapisan template sesuai dengan nilai awal yang diinginkan pengguna. Karena menerapkan opsi pengguna ke lapisan akan menjadi sesuatu yang akan sering kami lakukan, kami akan memindahkan pekerjaan ini ke metodenya sendiri:
function configureLayer(layer, options, shouldAdjustSpacing){ const { opacity, rotation, direction, spacing } = options; layer.style.opacity = opacity / 100; layer.transform.rotation = rotation; if(shouldAdjustSpacing){ const directionAsRadians = direction * (Math.PI / 180); const vector = { x: Math.cos(directionAsRadians), y: Math.sin(directionAsRadians) }; layer.frame.x += vector.x * spacing; layer.frame.y += vector.y * spacing; } };
Dan karena spasi hanya perlu diterapkan di antara duplikat dan bukan lapisan template, kami telah menambahkan tanda tertentu, shouldAdjustSpacing
, yang dapat kami atur ke true
atau false
tergantung pada apakah kami menerapkan opsi ke lapisan template atau bukan. Dengan cara itu kita dapat memastikan bahwa rotasi dan opacity akan diterapkan ke template, tetapi bukan spasi.
Kembali ke metode mosaic
, sekarang mari kita pastikan bahwa opsi awal diterapkan ke lapisan template:
function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }
Selanjutnya, kita perlu membuat duplikat kita. Pertama, mari buat variabel yang dapat kita gunakan untuk melacak opsi untuk duplikat saat ini:
function mosaic(options){ // ... var currentOptions; // ... }
Karena kita sudah menerapkan opsi awal ke lapisan template, kita perlu mengambil opsi yang baru saja kita terapkan dan menambahkan nilai relatif dari stepOptions
untuk mendapatkan opsi untuk diterapkan ke lapisan berikutnya. Karena kami juga akan melakukan ini beberapa kali lagi dalam loop kami, kami juga akan memindahkan pekerjaan ini ke metode tertentu, stepOptionsBy
:
function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };
Setelah itu, kita perlu menulis loop yang menduplikasi layer sebelumnya, menerapkan opsi saat ini padanya, lalu mengimbangi (atau "langkah") opsi saat ini untuk mendapatkan opsi untuk duplikat berikutnya:
function mosaic(options) { // ... var currentOptions = stepOptionsBy(startingOptions, stepOptions); for(let i = 0; i < (stepCount - 1); i++){ let duplicateLayer = layer.duplicate(); configureLayer(duplicateLayer, currentOptions, true); currentOptions = stepOptionsBy(currentOptions, stepOptions); layer = duplicateLayer; } }
Semua selesai — kami telah berhasil menulis inti dari apa yang seharusnya dilakukan plugin kami! Sekarang, kita perlu menghubungkan semuanya sehingga ketika pengguna benar-benar mengklik tombol "Terapkan" kode mosaik kita dipanggil.
Mari kembali ke ui.js
dan sesuaikan kode penanganan pesan kita. Yang perlu kita lakukan adalah mengurai string JSON dari opsi yang kita dapatkan sehingga mereka berubah menjadi objek yang benar-benar dapat kita gunakan. Setelah kami memiliki opsi ini, kami kemudian dapat memanggil fungsi mosaic
dengan mereka.
Pertama, penguraian. Kami perlu memperbarui fungsi penanganan pesan untuk mengurai pesan JSON yang kami dapatkan:
function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }
Selanjutnya, kita harus meneruskan ini ke fungsi mosaic
kita. Namun, ini sebenarnya bukan sesuatu yang harus dilakukan oleh kode kita di ui.js
— ini seharusnya terutama berkaitan dengan apa yang diperlukan untuk menampilkan hal-hal yang berhubungan dengan antarmuka di layar — bukan membuat mosaik itu sendiri. Untuk memisahkan tanggung jawab ini, kami akan menambahkan argumen kedua ke createWebView
yang mengambil fungsi, dan kami akan memanggil fungsi itu setiap kali kami menerima opsi dari antarmuka web.
Beri nama argumen ini onApplyMessage
:
function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }
Kita juga perlu memodifikasi metode yang diekspor, loadAndShow
, untuk mengambil argumen onApplyMessage
ini juga dan meneruskannya ke createWebView
:
function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }
Terakhir, main.js
. Sekarang kita perlu mengimpor fungsi mosaic
kita, dan memanggilnya dengan opsi yang kita terima dari antarmuka pengguna plugin:
const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };
Kami hampir selesai!
Namun, jika kami menjalankan kode kami sekarang dan mengklik tombol "Terapkan" di antarmuka plugin, tidak akan terjadi apa-apa. Mengapa? Alasannya adalah karena bagaimana skrip Sketch dijalankan: secara default, skrip tersebut "hidup" hanya sampai bagian bawah skrip Anda tercapai, setelah itu Sketch menghancurkannya dan membebaskan sumber daya apa pun yang digunakannya.
Ini adalah masalah bagi kami karena itu berarti bahwa apa pun yang kami perlukan terjadi secara tidak sinkron (dalam hal ini, setelah bagian bawah kode kami tercapai), seperti menerima pesan, tidak dapat, karena skrip kami telah dihancurkan. Ini berarti kami tidak akan mendapatkan pesan apa pun dari antarmuka web karena kami tidak ada untuk menerima dan menanggapinya!
Ada cara untuk memberi sinyal ke Sketch bahwa kita membutuhkan skrip kita untuk tetap hidup melampaui titik ini, menggunakan Fibers
. Dengan membuat Fiber, kami memberi tahu Sketch bahwa sesuatu yang tidak sinkron sedang terjadi dan itu perlu menjaga skrip kami tetap ada. Sketch hanya akan menghancurkan skrip kita saat benar-benar diperlukan (seperti pengguna menutup Sketch, atau saat plugin Mosaic perlu diperbarui):
// ... const Async = require("sketch/async"); var fiber; function onRun(context){ if(!fiber){ fiber = Async.createFiber(); fiber.onCleanup(() => { UI.cleanup(); }); } UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };
Voila! Mari kita coba plugin kita sekarang. Dengan lapisan yang dipilih di Sketch, masukkan beberapa pengaturan, lalu klik terapkan:
Perbaikan Akhir
Sekarang setelah sebagian besar fungsionalitas plugin kami diterapkan, kami dapat mencoba untuk "memperkecil" sedikit dan melihat gambaran besarnya.
Meningkatkan Pengalaman Pengguna
Jika Anda telah bermain-main dengan plugin dalam kondisi saat ini, Anda mungkin telah memperhatikan bahwa salah satu titik gesekan terbesar muncul ketika Anda mencoba mengedit Mosaic. Setelah Anda membuatnya, Anda harus menekan undo, menyesuaikan opsi, lalu klik 'Terapkan' (atau tekan Enter). Ini juga membuat lebih sulit untuk mengedit Mosaic setelah Anda meninggalkan dokumen Anda dan kembali lagi nanti, karena riwayat undo/redo Anda akan dihapus, sehingga Anda harus menghapus sendiri layer duplikat secara manual.
Dalam alur yang lebih ideal, pengguna cukup memilih grup Mosaic, menyesuaikan opsi dan menonton pembaruan Mosaic hingga mereka mendapatkan pengaturan yang tepat yang mereka cari. Untuk menerapkan ini, kami memiliki dua masalah untuk dipecahkan:
- Pertama, kita membutuhkan cara untuk mengelompokkan duplikat yang membentuk Mosaik menjadi satu. Sketch memberikan konsep Grup, yang dapat kita gunakan untuk menyelesaikan masalah ini.
- Kedua, kita membutuhkan cara untuk membedakan antara grup normal yang dibuat pengguna dan grup Mosaic. API Sketch juga memberi kita cara untuk menyimpan informasi pada setiap lapisan tertentu, yang dapat kita gunakan sebagai tag jalan dan kemudian mengidentifikasi grup sebagai salah satu grup Mosaik 'khusus' kami.
Mari kita tinjau kembali logika yang kita tulis di bagian sebelumnya untuk mengatasi hal ini. Kode asli kami mengikuti langkah-langkah berikut:
- Temukan dokumen saat ini.
- Temukan lapisan yang dipilih dokumen saat ini.
- Gandakan layer yang dipilih (kami akan menyebutnya layer template ) x beberapa kali.
- Untuk setiap duplikat, atur posisinya, rotasi, opacity, dll., dengan nilai (jumlah) tertentu yang ditetapkan oleh pengguna.
Untuk memungkinkan aliran pengguna baru kami, kami perlu mengubah langkah-langkah ini menjadi:
- Ambil dokumen saat ini.
- Ambil lapisan yang dipilih dokumen saat ini.
- Tentukan apakah layer yang dipilih adalah grup Mosaic atau bukan.
- Jika itu adalah lapisan lain, gunakan itu sebagai lapisan template dan lanjutkan ke langkah 4.
- Jika itu adalah grup Mosaik, pertimbangkan lapisan pertama di dalamnya sebagai lapisan template, dan lanjutkan ke langkah 5.
- Bungkus lapisan template di dalam grup, dan tandai grup itu sebagai grup Mosaik.
- Hapus semua lapisan dari dalam grup kecuali lapisan template.
- Gandakan layer template x beberapa kali.
- Untuk setiap duplikat, atur posisinya, rotasi, opacity, dll., dengan nilai spesifik yang ditetapkan oleh pengguna.
Kami punya tiga langkah baru. Untuk langkah baru pertama, langkah 3, kita akan membuat fungsi bernama findOrMakeSpecialGroupIfNeeded
yang akan melihat lapisan yang diteruskan ke sana untuk menentukan apakah itu grup Mosaik atau bukan. Jika ya, kami hanya akan mengembalikannya. Karena pengguna berpotensi memilih sublapisan yang bersarang jauh di dalam grup Mosaik, kami juga perlu memeriksa induk dari lapisan yang dipilih untuk mengetahui apakah mereka juga salah satu dari grup Mosaik kami:
function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } };
Jika kami tidak dapat menemukan grup Mosaic, kami cukup membungkus lapisan yang kami lewati di dalam Group
, lalu menandainya sebagai grup Mosaic.
Kembali ke bagian atas file, kita juga perlu mengeluarkan kelas Grup sekarang:
const { Document, Group } = require("sketch/dom");
function findOrMakeSpecialGroupIfNeeded(layer){ // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ if(/* TODO: is mosaic layer? */){ return layerToCheck; } layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); /* TODO: mark group as mosaic layer */ return group; };
Sekarang kita perlu mengisi celah (todo's). Untuk memulainya, kita membutuhkan suatu sarana untuk mengidentifikasi apakah suatu kelompok merupakan salah satu kelompok khusus milik kita atau bukan. Di sini, modul Settings
dari perpustakaan Sketsa datang untuk menyelamatkan kami. Kita dapat menggunakannya untuk menyimpan informasi khusus pada lapisan tertentu, dan juga untuk membacanya kembali.
Setelah kami mengimpor modul di bagian atas file:
const Settings = require("sketch/settings");
Kami kemudian dapat menggunakan dua metode utama yang disediakannya, setLayerSettingForKey
dan layerSettingForKey
, untuk mengatur dan membaca data dari lapisan:
function findOrMakeSpecialGroupIfNeeded(layer){ const isSpecialGroupKey = "is-mosaic-group"; // Loop up through the parent hierarchy, looking for a special group var layerToCheck = layer; while(layerToCheck){ let isSpecialGroup = Settings.layerSettingForKey(layerToCheck, isSpecialGroupKey); if(isSpecialGroup) return layerToCheck; layerToCheck = layerToCheck.parent; } // Group const destinationParent = layer.parent; layer.remove(); // explicitly remove layer from it's existing parent before adding it to group const group = new Group({ name: "Mosaic Group", layers: [ layer ], parent: destinationParent }); Settings.setLayerSettingForKey(group, isSpecialGroupKey, true); return group; };
Sekarang kita memiliki metode yang menangani pembungkusan lapisan dalam grup mosaik (atau, jika sudah menjadi grup mosaik, kembalikan saja) sekarang kita dapat menghubungkannya ke metode mosaic
utama kita setelah pemeriksaan keamanan:
function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }
Selanjutnya kita akan menambahkan loop untuk menghapus semua layer dari grup kecuali layer template (yang pertama):
function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }
Terakhir, kami akan memastikan bahwa ukuran grup sesuai dengan konten barunya karena pengguna mungkin awalnya memilih lapisan yang bersarang di dalam grup lama (lapisan yang mungkin telah kami hapus).
Kita juga harus memastikan untuk menyetel pilihan saat ini ke grup mosaik kita sendiri. Ini akan memastikan bahwa jika pengguna membuat banyak perubahan cepat ke grup mosaik yang sama, itu tidak akan dibatalkan pilihannya. Setelah kode yang sudah kita tulis untuk menduplikasi layer, tambahkan:
function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }
Coba lagi pluginnya. Anda akan menemukan bahwa mengedit mosaik jauh lebih lancar sekarang!
Meningkatkan Antarmuka
Satu hal lain yang mungkin Anda perhatikan adalah kurangnya sinkronisasi antara jendela tampilan dan antarmuka di dalamnya, dalam hal keduanya menjadi terlihat pada saat yang bersamaan. Hal ini disebabkan karena ketika kita menampilkan jendela, antarmuka web tidak dijamin selesai memuat, sehingga kadang-kadang akan “pop” atau “flash in” setelahnya.
Salah satu cara untuk memperbaikinya adalah dengan mendengarkan ketika antarmuka web selesai dimuat, dan baru kemudian menunjukkan jendela kita. Ada metode, webView:didFinishNavigation:
, yang akan dipanggil oleh WKWebView ketika halaman saat ini selesai dimuat. Kita bisa menggunakannya untuk mendapatkan notifikasi yang kita cari.
Kembali ke ui.js
, kita akan memperluas instance MochaJSDelegate
yang kita buat untuk mengimplementasikan metode ini, yang pada gilirannya akan memanggil argumen onLoadFinish
yang akan kita teruskan ke createWebView
:
function createWebView(pageURL, onApplyMessage, onLoadFinish){ const webView = WKWebView.alloc().init(); // Create delegate const delegate = new MochaJSDelegate({ "webView:didFinishNavigation:": (webView, navigation) => { onLoadFinish(); }, "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }).getClassInstance(); // Set load complete handler webView.navigationDelegate = delegate; // Set handler for messages from script const userContentController = webView.configuration().userContentController(); userContentController.addScriptMessageHandler_name(delegate, "sketchPlugin"); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };
Dan kembali ke metode loadAndShow
, kami akan menyesuaikannya sehingga hanya menampilkan jendela setelah tampilan web dimuat:
function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };
Bingo! Sekarang jendela kita hanya ditampilkan ketika tampilan web telah selesai dimuat, menghindari kedipan visual yang mengganggu itu.
Kesimpulan
Selamat, Anda telah membuat plugin Sketch pertama Anda!
Jika Anda ingin menginstal dan bermain-main dengan Mosaic, Anda dapat mengunduh plugin lengkap dari GitHub. Dan sebelum Anda pergi, berikut adalah beberapa sumber yang mungkin berguna selama sisa perjalanan Anda:
- developer.sketchapp.com Sumber resmi tentang pengembangan plugin Sketch. Berisi beberapa panduan yang berguna, serta referensi API untuk perpustakaan Sketch JavaScript.
- sketchplugins.com Komunitas pengembang plugin Sketch yang fantastis dan bermanfaat. Bagus untuk mendapatkan semua pertanyaan Anda yang membara dijawab.
- github.com/sketchplugins/plugin-directory Resmi, repositori GitHub pusat untuk plugin Sketch. Anda dapat mengirimkan plugin Anda di sini dan membagikannya dengan komunitas Sketch lainnya!