Cum să construiți un plugin Sketch cu JavaScript, HTML și CSS (Partea 2)

Publicat: 2022-03-10
Rezumat rapid ↬ În această a doua parte a tutorialului nostru despre construirea pluginurilor Sketch, vom relua de unde am rămas cu construirea interfeței noastre cu utilizatorul, apoi vom trece la caracteristica cheie de a genera efectiv mozaicurile noastre de straturi și optimizarea codului de plugin final.

După cum s-a menționat în partea 1, acest tutorial este destinat persoanelor care cunosc și folosesc aplicația Sketch și nu se tem să se amestece și cu codul. Pentru a profita cel mai mult de pe urma, va trebui să aveți cel puțin o experiență de bază în scrierea JavaScript (și, opțional, HTML/CSS).

În partea anterioară a acestui tutorial, am aflat despre fișierele de bază care alcătuiesc un plugin și cum să creăm interfața de utilizator a pluginului. În această a doua și ultimă parte, vom învăța cum să conectăm interfața cu utilizatorul la codul principal al pluginului și cum să implementăm principalele caracteristici ale pluginului. Nu în ultimul rând, vom învăța și cum să optimizăm codul și cum funcționează pluginul.

Construirea interfeței de utilizator a pluginului: realizarea interfeței noastre web și codul pluginului Sketch „să vorbească” între ele

Următorul lucru pe care trebuie să-l facem este să stabilim comunicarea între interfața noastră web și pluginul Sketch.

Trebuie să putem trimite un mesaj din interfața noastră web către pluginul Sketch atunci când se face clic pe butonul „Aplicați” din interfața noastră web. Acest mesaj trebuie să ne spună despre setările introduse de utilizator, cum ar fi numărul de pași, cantitatea de rotație, numărul de duplicate de creat și așa mai departe.

WKWebView face această sarcină puțin mai ușoară: putem trimite mesaje către pluginul nostru Sketch din codul JavaScript al interfeței noastre web utilizând API-ul window.webkit.messageHandlers .

Pe partea codului nostru Sketch, putem folosi o altă metodă, addScriptMessageHandler:name: (sau addScriptMessageHandler_name ) pentru a înregistra un handler de mesaje care va fi apelat ori de câte ori primește un mesaj trimis de la interfața noastră web a pluginului.

Să începem prin a ne asigura că putem primi mesaje din interfața noastră web. Mergeți la funcția createWebView a fișierului nostru ui.js și adăugați următoarele:

 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; };

Aici folosim proprietatea userContentController a vizualizării web pentru a adăuga un handler de mesaje pe care l-am numit „sketchPlugin”. Acest „controller de conținut al utilizatorului” este puntea care asigură că mesajele ajung din vizualizarea noastră web.

Mai multe după săritură! Continuați să citiți mai jos ↓

S-ar putea să fi observat ceva ciudat la codul de mai sus: obiectul pe care îl adăugăm ca handler de mesaje, ourMessageHandler , nu există încă! Din păcate, nu putem folosi doar un obiect JavaScript obișnuit sau o funcție ca handler, deoarece această metodă se așteaptă la un anumit tip de obiect nativ.

Din fericire pentru noi, putem ocoli această limitare utilizând MochaJSDelegate , o mini-bibliotecă pe care am scris-o, care face posibilă crearea tipului de obiect nativ de care avem nevoie folosind JavaScript obișnuit. Va trebui să îl descărcați manual și să îl salvați în pachetul de pluginuri sub Sketch/MochaJSDelegate.js .

Pentru a-l folosi, va trebui mai întâi să îl importăm în ui.js . Adăugați următoarele în partea de sus a fișierului:

 const MochaJSDelegate = require("./MochaJSDelegate");

Acum putem folosi MochaJSDelegate pentru a crea tipul de handler de mesaje pe care addScriptMessageHandler:name: îl așteaptă:

 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; };

Codul pe care tocmai l-am adăugat creează obiectul nativ de care avem nevoie. De asemenea, definește o metodă pe acel obiect numit userContentController:didReceiveScriptMessage: — această metodă este apoi apelată cu mesajul pe care îl dorim ca al doilea argument. Deoarece încă nu trimitem niciun mesaj, va trebui să revenim aici mai târziu și să adăugăm un cod pentru a analiza și a gestiona efectiv mesajele pe care le primim.

Apoi, trebuie să adăugăm un cod pe interfața noastră web pentru a ne trimite acele mesaje. /Resources/web-ui/script.js . Veți descoperi că am scris deja cea mai mare parte a codului care se ocupă de preluarea valorilor HTML <inputs /> în care utilizatorul își va introduce opțiunile.

Ceea ce ne mai rămâne de făcut este să adăugăm codul care trimite de fapt valorile la codul nostru Sketch:

Găsiți funcția de apply și adăugați următoarele la sfârșitul acesteia:

 // Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));

Aici folosim window.webkit.messageHandlers API pe care l-am menționat mai devreme pentru a accesa handlerul de mesaje pe care l-am înregistrat mai sus ca sketchPlugin . Apoi trimiteți-i un mesaj cu un șir JSON care conține intrările utilizatorului.

Să ne asigurăm că totul este configurat corect. Reveniți la /Sketch/ui.js . Pentru a ne asigura că primim mesajele așa cum ne așteptăm, vom modifica metoda pe care am definit-o mai devreme, astfel încât să afișeze un dialog atunci când primim un mesaj:

 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" ); // ... };

Acum rulați pluginul (poate fi necesar să închideți mai întâi orice fereastră Mozaic existentă pe care ați deschis-o), introduceți câteva valori, apoi faceți clic pe „Aplicați”. Ar trebui să vedeți o alertă ca cea de mai jos - aceasta înseamnă că totul este conectat corect și mesajul nostru a trecut cu succes! Dacă nu, reveniți la pașii anteriori și asigurați-vă că totul a fost făcut așa cum este descris.

Imagine care arată dialogul pe care ar trebui să-l vedeți după ce faceți clic pe butonul „aplicați” din interfața de utilizare a pluginului.
Dialogul pe care ar trebui să-l vedeți să apară după ce faceți clic pe Aplicare. (Previzualizare mare)

Acum că suntem capabili să trimitem mesaje de la interfața noastră către pluginul nostru, putem trece la scrierea codului care face de fapt ceva util cu informațiile respective: generarea mozaicurilor noastre de straturi.

Generarea Mozaicului Stratului

Să facem un bilanț a ceea ce este necesar pentru ca acest lucru să se întâmple. Simplificând puțin lucrurile, ceea ce trebuie să facă codul nostru este:

  1. Găsiți documentul curent.
  2. Găsiți stratul selectat al documentului curent.
  3. Duplicați stratul selectat (îl vom numi stratul șablon ) x numărul de ori.
  4. Pentru fiecare duplicat, ajustați-i poziția, rotația, opacitatea etc., după valorile (cantitățile) specifice stabilite de utilizator.

Acum că avem un plan rezonabil, să continuăm să scriem. Respectând modelul nostru de modularizare a codului nostru, să creăm un nou fișier, mosaic.js în folderul Sketch/ , și să adăugăm la acesta următorul cod:

 function mosaic(options){ }; module.export = mosaic;

Vom folosi această funcție ca singurul export al acestui modul, deoarece face un API mai simplu de utilizat odată ce îl importăm - putem doar apela mosaic() cu orice opțiuni pe care le primim de la interfața web.

Primii doi pași pe care trebuie să-i facem sunt obținerea documentului curent și apoi stratul selectat. Sketch API are o bibliotecă încorporată pentru manipularea documentelor la care putem avea acces prin importul modulului sketch/dom . Avem nevoie doar de obiectul Document chiar acum, așa că îl vom scoate în mod explicit. În partea de sus a fișierului, adăugați:

 const { Document } = require("sketch/dom");

Obiectul Document are o metodă specifică pentru accesarea documentului curent pe care îl putem folosi, numită getSelectedDocument() . Odată ce avem instanța curentă a documentului, putem accesa orice straturi pe care utilizatorul le-a selectedLayers prin proprietatea selectLayers a documentului. În cazul nostru, totuși, ne pasă doar de selecțiile cu un singur strat, așa că vom lua doar primul strat selectat de utilizator:

 function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;

Notă: s-ar putea să vă așteptați ca însuși selectedLayers să fie o matrice, dar nu este. În schimb, este o instanță a clasei Selection . Există un motiv pentru acest lucru: clasa Selection conține o mulțime de metode de ajutor utile pentru manipularea selecției, cum ar fi clear, map, reduce și forEach. Acesta expune matricea stratului real prin proprietatea layer .

Să adăugăm, de asemenea, un feedback de avertizare în cazul în care utilizatorul uită să deschidă un document sau să selecteze ceva:

 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;

Acum că am scris codul pentru pașii 1 și 2 (găsirea documentului curent și a stratului selectat), trebuie să abordăm pașii 3 și 4:

  • Duplicați stratul șablon x de ori.
  • Pentru fiecare duplicat, ajustați-i poziția, rotația, opacitatea etc., după valorile specifice stabilite de utilizator.

Să începem prin a scoate toate informațiile relevante de care avem nevoie din options : numărul de ori de duplicat, opțiunile de pornire și opțiunile de pași. Putem folosi din nou destructurarea (cum am făcut mai devreme cu Document ) pentru a scoate aceste proprietăți din options :

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }

În continuare, să ne igienizăm intrările și să ne asigurăm că numărul de pași este întotdeauna de cel puțin 1:

 function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }

Acum trebuie să ne asigurăm că opacitatea stratului șablon, rotația etc., toate se potrivesc cu valorile de pornire dorite de utilizator. Deoarece aplicarea opțiunilor utilizatorului la un strat va fi ceva pentru care vom face o mulțime, vom muta această lucrare în propria sa metodă:

 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; } };

Și deoarece spațierea trebuie aplicată doar între duplicate și nu stratul șablon, am adăugat un semnal specific, shouldAdjustSpacing , pe care îl putem seta la true sau false , în funcție de dacă aplicăm opțiuni la un strat șablon sau nu. În acest fel, ne putem asigura că rotația și opacitatea vor fi aplicate șablonului, dar nu și spațierea.

Înapoi la metoda mosaic , să ne asigurăm că opțiunile de pornire sunt aplicate stratului șablon:

 function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }

În continuare, trebuie să ne creăm duplicatele. Mai întâi, să creăm o variabilă pe care o putem folosi pentru a urmări care sunt opțiunile pentru duplicatul curent:

 function mosaic(options){ // ... var currentOptions; // ... }

Deoarece am aplicat deja opțiunile de pornire layer-ului șablon, trebuie să luăm acele opțiuni pe care tocmai le-am aplicat și să adăugăm valorile relative ale stepOptions pentru a obține opțiunile de aplicat la stratul următor. Deoarece vom mai face acest lucru de mai multe ori în bucla noastră, vom muta și această lucrare într-o metodă specifică, stepOptionsBy :

 function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };

După aceea, trebuie să scriem o buclă care dublează stratul anterior, îi aplică opțiunile curente, apoi compensează (sau „pași”) opțiunile curente pentru a obține opțiunile pentru următorul duplicat:

 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; } }

Gata — am scris cu succes nucleul a ceea ce ar trebui să facă pluginul nostru! Acum, trebuie să conectăm lucrurile astfel încât atunci când utilizatorul face clic pe butonul „Aplicați” codul nostru mozaic să fie invocat.

Să ne întoarcem la ui.js și să ne adaptăm codul de gestionare a mesajelor. Ceea ce va trebui să facem este să analizăm șirul JSON de opțiuni pe care le primim, astfel încât acestea să fie transformate într-un obiect pe care îl putem folosi efectiv. Odată ce avem aceste opțiuni, putem apela apoi funcția mosaic cu ele.

În primul rând, analiza. Va trebui să ne actualizăm funcția de gestionare a mesajelor pentru a analiza mesajul JSON pe care îl primim:

 function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }

În continuare, va trebui să trecem acest lucru la funcția noastră de mosaic . Cu toate acestea, acest lucru nu este cu adevărat ceva ce ar trebui să facă codul nostru din ui.js - ar trebui să fie preocupat în primul rând de ceea ce este necesar pentru a afișa lucruri legate de interfață pe ecran - nu de a crea mozaicuri în sine. Pentru a păstra aceste responsabilități separate, vom adăuga un al doilea argument pentru createWebView care preia o funcție și vom apela această funcție ori de câte ori primim opțiuni de la interfața web.

Să numim acest argument onApplyMessage :

 function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }

De asemenea, va trebui să modificăm metoda noastră exportată, loadAndShow , pentru a lua și acest argument onApplyMessage și a-l transfera la createWebView :

 function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }

În cele din urmă, main.js . Acum trebuie să importam funcția mosaic și să o apelăm cu opțiunile pe care le primim din interfața de utilizator a pluginului:

 const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };

Aproape am terminat!

Cu toate acestea, dacă ne-am rula codul acum și am face clic pe butonul „Aplicați” din interfața pluginului, nu s-ar întâmpla nimic. De ce? Motivul se datorează modului în care sunt rulate scripturile Sketch: în mod implicit, ele „vii” doar până când se ajunge la partea de jos a scriptului tău, după care Sketch îl distruge și eliberează orice resurse pe care le folosea.

Aceasta este o problemă pentru noi, deoarece înseamnă că orice avem nevoie să se întâmple în mod asincron (în acest caz, asta după ce se ajunge la capătul codului nostru), cum ar fi primirea de mesaje, nu se poate, deoarece scriptul nostru a fost distrus. Aceasta înseamnă că nu vom primi niciunul dintre mesajele noastre de pe interfața web, deoarece nu suntem prin preajmă să le primim și să le răspundem!

Există o modalitate de a semnala lui Sketch că avem nevoie de scriptul nostru pentru a rămâne în viață dincolo de acest punct, folosind Fibers . Prin crearea unei fibre, îi spunem lui Sketch că se întâmplă ceva asincron și că trebuie să ne păstreze scriptul. Sketch ne va distruge scriptul numai atunci când este absolut necesar (cum ar fi utilizatorul care închide Sketch sau când pluginul Mosaic trebuie actualizat):

 // ... 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! Să încercăm acum pluginul nostru. Cu un strat selectat în Sketch, introduceți unele setări, apoi faceți clic pe Aplică:

Să încercăm acum pluginul nostru - cu un strat selectat în Sketch, introduceți unele setări, apoi faceți clic pe „Aplicați”.

Îmbunătățiri finale

Acum că am implementat majoritatea funcționalităților pluginului nostru, putem încerca să „micșorizăm” puțin și să aruncăm o privire la imaginea de ansamblu.

Îmbunătățirea experienței utilizatorului

Dacă v-ați jucat cu pluginul în starea sa actuală, este posibil să fi observat că unul dintre cele mai mari puncte de frecare apare atunci când încercați să editați un mozaic. După ce ați creat unul, trebuie să apăsați Undo, să ajustați opțiunile, apoi să faceți clic pe „Aplicați” (sau să apăsați Enter). De asemenea, este mai dificil să editați un mozaic după ce ați părăsit documentul și ați revenit la el mai târziu, deoarece istoricul dvs. de anulare/refacere va fi șters, lăsându-vă să ștergeți manual straturile duplicate.

Într-un flux mai ideal, utilizatorul ar putea doar să selecteze un grup de mozaic, să ajusteze opțiunile și să urmărească actualizarea mozaicului până când obține aranjamentul exact pe care îl caută. Pentru a implementa acest lucru, avem două probleme de rezolvat:

  1. În primul rând, vom avea nevoie de o modalitate de a grupa împreună duplicatele care alcătuiesc un mozaic. Sketch oferă conceptul de Grupuri, pe care îl putem folosi pentru a rezolva această problemă.
  2. În al doilea rând, vom avea nevoie de o modalitate de a face diferența dintre un grup normal, creat de utilizator și un grup Mozaic. API-ul lui Sketch ne oferă, de asemenea, o modalitate de a stoca informații pe orice strat dat, pe care le putem folosi ca etichetă de cale și mai târziu să identificăm un grup ca unul dintre grupurile noastre „speciale” de mozaic.

Să revedem logica pe care am scris-o în secțiunea anterioară pentru a aborda acest lucru. Codul nostru original urmează următorii pași:

  1. Găsiți documentul curent.
  2. Găsiți stratul selectat al documentului curent.
  3. Duplicați stratul selectat (îl vom numi stratul șablon ) x numărul de ori.
  4. Pentru fiecare duplicat, ajustați-i poziția, rotația, opacitatea etc., după valorile (cantitățile) specifice stabilite de utilizator.

Pentru a face posibil noul nostru flux de utilizatori, trebuie să modificăm acești pași în:

  1. Luați documentul curent.
  2. Luați stratul selectat al documentului curent.
  3. Determinați dacă stratul selectat este un grup Mozaic sau nu.
    • Dacă este un alt strat, utilizați-l ca strat șablon și treceți la pasul 4.
    • Dacă este un grup Mozaic, luați în considerare primul strat din acesta ca strat șablon și treceți la pasul 5.
  4. Înfășurați stratul șablon într-un grup și marcați acel grup ca grup Mozaic.
  5. Eliminați toate straturile din interiorul grupului, cu excepția stratului șablon.
  6. Duplicați stratul șablon x de ori.
  7. Pentru fiecare duplicat, ajustați-i poziția, rotația, opacitatea etc., după valorile specifice stabilite de utilizator.

Avem trei pași noi. Pentru primul pas nou, pasul 3, vom crea o funcție numită findOrMakeSpecialGroupIfNeeded , care va analiza stratul transmis pentru a determina dacă este sau nu un grup Mozaic. Dacă este, îl vom returna. Deoarece utilizatorul ar putea selecta un substrat imbricat adânc într-un grup de mozaic, va trebui, de asemenea, să verificăm părinții stratului selectat pentru a spune dacă sunt și unul dintre grupurile noastre de mozaic:

 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; } };

Dacă nu am reușit să găsim un grup de mozaic, vom împacheta pur și simplu stratul care ne-a trecut într-un Group , apoi îl vom eticheta ca grup de mozaic.

Înapoi în partea de sus a fișierului, va trebui să scoatem și clasa de grup acum:

 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; };

Acum trebuie să umplem golurile (todo's). Pentru început, avem nevoie de un mijloc de a identifica dacă un grup este sau nu unul dintre grupurile speciale care ne aparțin sau nu. Aici, modulul Settings al bibliotecii Sketch ne vine în ajutor. Îl putem folosi pentru a stoca informații personalizate pe un anumit strat și, de asemenea, pentru a le citi înapoi.

Odată ce importăm modulul din partea de sus a fișierului:

 const Settings = require("sketch/settings");

Apoi putem folosi două metode cheie pe care le oferă, setLayerSettingForKey și layerSettingForKey , pentru a seta și a citi datele dintr-un strat:

 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; };

Acum că avem o metodă care se ocupă de împachetarea unui strat într-un grup de mozaic (sau, dacă deja un grup de mozaic, doar îl returnează), îl putem conecta la metoda noastră principală mosaic imediat după verificările noastre de siguranță:

 function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }

Apoi vom adăuga o buclă pentru a elimina toate straturile din grup, cu excepția stratului șablon (care este primul):

 function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }

În cele din urmă, ne vom asigura că dimensiunea grupului este potrivită cu noul său conținut, deoarece utilizatorul ar fi putut selecta inițial un strat imbricat în grupul vechi (un strat pe care este posibil să l-am fi eliminat).

De asemenea, va trebui să ne asigurăm că setăm selecția curentă la grupul nostru de mozaic însuși. Acest lucru va asigura că, dacă utilizatorul face o grămadă de modificări rapide la același grup de mozaic, acesta nu va fi deselectat. După codul pe care l-am scris deja pentru a duplica un strat, adăugați:

 function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }

Încercați din nou pluginul. Ar trebui să descoperiți că editarea unui mozaic este mult mai lină acum!

Îmbunătățirea interfeței

Un alt lucru pe care l-ați putea observa este lipsa de sincronizare între fereastra de afișare și interfața din interiorul acesteia, în ceea ce privește ambele devenind vizibile în același timp. Acest lucru se datorează faptului că atunci când afișăm fereastra, nu se garantează că interfața web s-a terminat de încărcat, așa că uneori va apărea sau „flash-in” după aceea.

O modalitate de a remedia acest lucru este să ascultăm când interfața web s-a terminat de încărcat și abia apoi să ne arăți fereastra. Există o metodă, webView:didFinishNavigation: , pe care WKWebView o va apela când pagina curentă s-a terminat de încărcat. Îl putem folosi pentru a primi exact notificarea pe care o căutăm.

Înapoi în ui.js , vom extinde instanța MochaJSDelegate pe care am creat-o pentru a implementa această metodă, care la rândul său va apela argumentul onLoadFinish pe care îl vom transmite la 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; };

Și înapoi în metoda loadAndShow , o vom ajusta astfel încât să arate fereastra doar după ce vizualizarea web s-a încărcat:

 function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };

Bingo! Acum fereastra noastră se afișează numai când vizualizarea web s-a terminat de încărcat, evitând acea pâlpâire vizuală enervantă.

Concluzie

Felicitări, ai creat primul tău plugin Sketch!

Dacă doriți să instalați și să vă jucați cu Mosaic, puteți descărca pluginul complet de pe GitHub. Și înainte de a pleca, iată câteva resurse care ar putea fi la îndemână în restul călătoriei:

  • developer.sketchapp.com Resursa oficială privind dezvoltarea pluginului Sketch. Conține mai multe ghiduri utile, precum și o referință API pentru biblioteca Sketch JavaScript.
  • sketchplugins.com O comunitate fantastică și utilă de dezvoltatori de plugin-uri Sketch. Excelent pentru a primi răspuns la toate întrebările arzătoare.
  • github.com/sketchplugins/plugin-directory Depozitul oficial, central GitHub al pluginurilor Sketch. Puteți trimite pluginurile dvs. aici și le puteți partaja cu restul comunității Sketch!