Come creare un plug-in di schizzo con JavaScript, HTML e CSS (parte 2)

Pubblicato: 2022-03-10
Riassunto rapido ↬ In questa seconda parte del nostro tutorial sulla creazione di plug-in di Sketch, riprenderemo da dove eravamo rimasti con la creazione della nostra interfaccia utente, quindi passeremo alla caratteristica chiave di generare effettivamente i nostri mosaici di livello e ottimizzando il codice del plugin finale.

Come accennato nella parte 1, questo tutorial è destinato a persone che conoscono e utilizzano l'app Sketch e non hanno paura di dilettarsi con il codice. Per trarne il massimo profitto, dovrai avere almeno un'esperienza di base nella scrittura di JavaScript (e, facoltativamente, HTML/CSS).

Nella parte precedente di questo tutorial, abbiamo appreso i file di base che compongono un plug-in e come creare l'interfaccia utente del plug-in. In questa seconda e ultima parte, impareremo come collegare l'interfaccia utente al codice del plugin di base e come implementare le caratteristiche principali del plugin. Ultimo ma non meno importante, impareremo anche come ottimizzare il codice e il modo in cui funziona il plugin.

Costruire l'interfaccia utente del plug-in: fare in modo che la nostra interfaccia web e il codice del plug-in di Sketch "parlino" tra loro

La prossima cosa che dobbiamo fare è impostare la comunicazione tra la nostra interfaccia web e il plug-in Sketch.

Dobbiamo essere in grado di inviare un messaggio dalla nostra interfaccia web al plug-in Sketch quando si fa clic sul pulsante "Applica" nella nostra interfaccia web. Questo messaggio deve indicarci quali impostazioni ha immesso l'utente, ad esempio il numero di passaggi, la quantità di rotazione, il numero di duplicati da creare e così via.

WKWebView rende questo compito un po' più semplice per noi: possiamo inviare messaggi al nostro plug-in Sketch dal codice JavaScript della nostra interfaccia web utilizzando l'API window.webkit.messageHandlers .

Dal lato del nostro codice Sketch, possiamo usare un altro metodo, addScriptMessageHandler:name: (o addScriptMessageHandler_name ) per registrare un gestore di messaggi che verrà chiamato ogni volta che riceve un messaggio inviato dalla nostra interfaccia web del plugin.

Iniziamo assicurandoci di poter ricevere messaggi dalla nostra interfaccia utente web. Vai alla funzione createWebView ui.js aggiungi quanto segue:

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

Qui utilizziamo la proprietà userContentController della visualizzazione Web per aggiungere un gestore di messaggi che abbiamo chiamato "sketchPlugin". Questo "controller dei contenuti utente" è il ponte che assicura che i messaggi arrivino dalla nostra visualizzazione web.

Altro dopo il salto! Continua a leggere sotto ↓

Potresti aver notato qualcosa di strano nel codice sopra: l'oggetto che stiamo aggiungendo come gestore del messaggio, ourMessageHandler , non esiste ancora! Sfortunatamente, non possiamo semplicemente usare un normale oggetto JavaScript o una funzione come gestore, poiché questo metodo si aspetta un certo tipo di oggetto nativo.

Fortunatamente per noi, possiamo aggirare questa limitazione usando MochaJSDelegate , una mini-libreria che ho scritto che rende possibile creare il tipo di oggetto nativo di cui abbiamo bisogno usando il normale vecchio JavaScript. Dovrai scaricarlo e salvarlo manualmente nel pacchetto di plug-in in Sketch/MochaJSDelegate.js .

Per usarlo, dovremo prima importarlo in ui.js . Aggiungi quanto segue nella parte superiore del file:

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

Ora possiamo usare MochaJSDelegate per creare il tipo di gestore di messaggi addScriptMessageHandler:name: si aspetta:

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

Il codice che abbiamo appena aggiunto crea l'oggetto nativo di cui abbiamo bisogno. Definisce anche un metodo su quell'oggetto chiamato userContentController:didReceiveScriptMessage: — questo metodo viene quindi chiamato con il messaggio che vogliamo come secondo argomento. Dal momento che in realtà non stiamo ancora inviando alcun messaggio, dovremo tornare qui in un secondo momento e aggiungere del codice per analizzare e gestire effettivamente i messaggi che riceviamo.

Successivamente, dobbiamo aggiungere del codice alla nostra interfaccia web per inviarci quei messaggi. Vai a /Resources/web-ui/script.js . Scoprirai che ho già scritto la maggior parte del codice che gestisce il recupero dei valori dell'HTML <inputs /> in cui l'utente inserirà le proprie opzioni.

Quello che ci resta da fare è aggiungere il codice che effettivamente invia i valori al nostro codice Sketch:

Trova la funzione apply e aggiungi quanto segue alla fine di essa:

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

Qui utilizziamo l'API window.webkit.messageHandlers menzionata in precedenza per accedere al gestore di messaggi che abbiamo registrato in precedenza come sketchPlugin . Quindi inviagli un messaggio con una stringa JSON contenente gli input dell'utente.

Assicuriamoci che tutto sia impostato correttamente. Torna a /Sketch/ui.js . Per assicurarci di ricevere i messaggi come previsto, modificheremo il metodo che abbiamo definito in precedenza in modo che visualizzi una finestra di dialogo quando riceviamo un messaggio:

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

Ora esegui il plug-in (potrebbe essere necessario prima chiudere qualsiasi finestra di Mosaic esistente che hai aperto), inserisci alcuni valori, quindi fai clic su "Applica". Dovresti vedere un avviso come quello qui sotto: significa che tutto è cablato correttamente e il nostro messaggio è andato a buon fine! In caso contrario, torna ai passaggi precedenti e assicurati che tutto sia stato eseguito come descritto.

Immagine che mostra la finestra di dialogo che dovresti vedere dopo aver fatto clic sul pulsante "applica" nell'interfaccia utente del plug-in.
La finestra di dialogo che dovresti vedere appare dopo aver fatto clic su Applica. (Grande anteprima)

Ora che siamo in grado di inviare messaggi dalla nostra interfaccia al nostro plugin, possiamo passare alla scrittura del codice che effettivamente fa qualcosa di utile con quelle informazioni: generare i nostri mosaici di livello.

Generazione dei mosaici di livello

Facciamo il punto su ciò che è necessario affinché ciò avvenga. Semplificando un po' le cose, ciò che il nostro codice deve fare è:

  1. Trova il documento corrente.
  2. Trova il livello selezionato del documento corrente.
  3. Duplica il livello selezionato (lo chiameremo livello modello ) x numero di volte.
  4. Per ogni duplicato, modifica la sua posizione, rotazione, opacità, ecc. in base ai valori (quantità) specifici impostati dall'utente.

Ora che abbiamo un piano ragionevole, continuiamo a scrivere. Attenendoci al nostro modello di modularizzazione del codice, creiamo un nuovo file, mosaic.js nella cartella Sketch/ , e aggiungiamo ad esso il seguente codice:

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

Useremo questa funzione come l'unica esportazione di questo modulo poiché rende l'API più semplice da usare una volta importata: possiamo semplicemente chiamare mosaic() con qualsiasi opzione otteniamo dall'interfaccia web.

I primi due passaggi che dobbiamo compiere sono ottenere il documento corrente e quindi il livello selezionato. L'API Sketch ha una libreria integrata per la manipolazione dei documenti a cui possiamo accedere importando il modulo sketch/dom . Abbiamo solo bisogno dell'oggetto Document in questo momento, quindi lo estrarremo esplicitamente. Nella parte superiore del file, aggiungi:

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

L'oggetto Document ha un metodo specifico per accedere al documento corrente che possiamo usare, chiamato getSelectedDocument() . Una volta che abbiamo l'istanza del documento corrente, possiamo accedere a qualsiasi livello l'utente abbia selezionato tramite la proprietà selectedLayers del documento. Nel nostro caso, tuttavia, ci preoccupiamo solo delle selezioni a livello singolo, quindi prenderemo solo il primo livello selezionato dall'utente:

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

Nota: potresti aspettarti che selectedLayers stesso sia un array, ma non lo è. Invece, è un'istanza della classe Selection . C'è una ragione per questo: la classe Selection contiene una serie di utili metodi di supporto per manipolare la selezione come clear, map, reduce e forEach. Espone l'array di livelli effettivo tramite la proprietà del layer .

Aggiungiamo anche alcuni feedback di avviso nel caso in cui l'utente si dimentichi di aprire un documento o di selezionare qualcosa:

 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;

Ora che abbiamo scritto il codice per i passaggi 1 e 2 (trovare il documento corrente e il livello selezionato), dobbiamo affrontare i passaggi 3 e 4:

  • Duplica il livello del modello x il numero di volte.
  • Per ogni duplicato, modifica la sua posizione, rotazione, opacità, ecc. in base ai valori specifici impostati dall'utente.

Iniziamo estraendo dalle options tutte le informazioni rilevanti di cui abbiamo bisogno: il numero di volte da duplicare, le opzioni di avvio e le opzioni di passaggio. Possiamo ancora una volta usare la destrutturazione (come abbiamo fatto in precedenza con Document ) per estrarre quelle proprietà dalle options :

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

Quindi, sanifichiamo i nostri input e assicuriamo che il conteggio dei passaggi sia sempre almeno 1:

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

Ora dobbiamo assicurarci che l'opacità, la rotazione, ecc. del livello del modello corrispondano ai valori iniziali desiderati dall'utente. Poiché applicare le opzioni dell'utente a un livello sarà qualcosa che faremo molto, sposteremo questo lavoro nel suo metodo:

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

E poiché la spaziatura deve essere applicata solo tra i duplicati e non il livello modello, abbiamo aggiunto un flag specifico, shouldAdjustSpacing , che possiamo impostare su true o false a seconda che stiamo applicando opzioni a un livello modello o non. In questo modo possiamo garantire che la rotazione e l'opacità vengano applicate al modello, ma non la spaziatura.

Tornando al metodo del mosaic , assicuriamoci ora che le opzioni di partenza siano applicate al livello del modello:

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

Successivamente, dobbiamo creare i nostri duplicati. Per prima cosa, creiamo una variabile che possiamo usare per tenere traccia di quali sono le opzioni per il duplicato corrente:

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

Poiché abbiamo già applicato le opzioni di partenza al livello del modello, dobbiamo prendere quelle opzioni che abbiamo appena applicato e aggiungere i valori relativi di stepOptions per ottenere le opzioni da applicare al livello successivo. Dal momento che lo faremo anche molte altre volte nel nostro ciclo, sposteremo anche questo lavoro in un metodo specifico, stepOptionsBy :

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

Dopodiché, dobbiamo scrivere un ciclo che duplichi il livello precedente, gli applichi le opzioni correnti, quindi sfalsi (o "passi") le opzioni correnti per ottenere le opzioni per il duplicato successivo:

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

Tutto fatto: abbiamo scritto con successo il nucleo di ciò che il nostro plugin dovrebbe fare! Ora, dobbiamo cablare le cose in modo che quando l'utente fa effettivamente clic sul pulsante "Applica" il nostro codice mosaico viene invocato.

Torniamo a ui.js e modifichiamo il nostro codice di gestione dei messaggi. Quello che dovremo fare è analizzare la stringa JSON di opzioni che stiamo ottenendo in modo che vengano trasformate in un oggetto che possiamo effettivamente utilizzare. Una volta che abbiamo queste opzioni, possiamo quindi chiamare la funzione mosaic con esse.

Innanzitutto, l'analisi. Avremo bisogno di aggiornare la nostra funzione di gestione dei messaggi per analizzare il messaggio JSON che otteniamo:

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

Successivamente, dovremo passare questo alla nostra funzione di mosaic . Tuttavia, questo non è davvero qualcosa che il nostro codice in ui.js dovrebbe fare - dovrebbe riguardare principalmente ciò che è necessario per visualizzare le cose relative all'interfaccia sullo schermo - non creare mosaici stessi. Per mantenere separate queste responsabilità, aggiungeremo un secondo argomento a createWebView che accetta una funzione e chiameremo quella funzione ogni volta che riceviamo opzioni dall'interfaccia web.

Diamo un nome a questo argomento onApplyMessage :

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

Dovremo anche modificare il nostro metodo esportato, loadAndShow , per accettare anche questo argomento onApplyMessage e passarlo a createWebView :

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

Infine, vai su main.js . Ora dobbiamo importare la nostra funzione mosaic e chiamarla con le opzioni che riceviamo dall'interfaccia utente del plugin:

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

Abbiamo quasi finito!

Tuttavia, se eseguissimo ora il nostro codice e facessimo clic sul pulsante "Applica" nell'interfaccia del plug-in, non accadrebbe nulla. Come mai? Il motivo è dovuto al modo in cui vengono eseguiti gli script di Sketch: per impostazione predefinita, "vivono" solo fino al raggiungimento della fine dello script, dopodiché Sketch lo distrugge e libera tutte le risorse che stava utilizzando.

Questo è un problema per noi poiché significa che tutto ciò di cui abbiamo bisogno che avvenga in modo asincrono (in questo caso, dopo che è stato raggiunto il fondo del nostro codice), come la ricezione di messaggi, non può, perché il nostro script è stato distrutto. Ciò significa che non riceveremo nessuno dei nostri messaggi dall'interfaccia web poiché non siamo in giro per riceverli e rispondere a loro!

C'è un modo per segnalare a Sketch che abbiamo bisogno del nostro script per rimanere in vita oltre questo punto, usando Fibers . Creando una fibra, diciamo a Sketch che sta accadendo qualcosa di asincrono e che deve mantenere il nostro script in giro. Sketch distruggerà quindi il nostro script solo quando assolutamente necessario (come l'utente che chiude Sketch o quando il plug-in Mosaic deve essere aggiornato):

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

Ecco! Proviamo ora il nostro plugin. Con un livello selezionato in Schizzo, inserisci alcune impostazioni, quindi fai clic su applica:

Proviamo ora il nostro plug-in: con un livello selezionato in Sketch, inserisci alcune impostazioni, quindi fai clic su "Applica".

Miglioramenti finali

Ora che abbiamo implementato la maggior parte delle funzionalità del nostro plug-in, possiamo provare a "rimpicciolire" un po' e dare un'occhiata al quadro generale.

Migliorare l'esperienza dell'utente

Se hai giocato con il plugin nel suo stato attuale, potresti aver notato che uno dei maggiori punti di attrito appare quando provi a modificare un Mosaic. Una volta creato uno, devi premere Annulla, regolare le opzioni, quindi fare clic su "Applica" (o premere Invio). Inoltre, rende più difficile modificare un Mosaico dopo aver lasciato il documento e tornarvi in ​​seguito, poiché la cronologia di annullamento/ripristino sarà stata cancellata, lasciandoti eliminare manualmente i livelli duplicati.

In un flusso più ideale, l'utente potrebbe semplicemente selezionare un gruppo Mosaic, regolare le opzioni e guardare l'aggiornamento di Mosaic fino a quando non ottiene la disposizione esatta che sta cercando. Per implementarlo, abbiamo due problemi da risolvere:

  1. Innanzitutto, avremo bisogno di un modo per raggruppare insieme i duplicati che compongono un Mosaico. Sketch fornisce il concetto di Gruppi, che possiamo usare per risolvere questo problema.
  2. In secondo luogo, avremo bisogno di un modo per distinguere tra un normale gruppo creato dall'utente e un gruppo Mosaic. L'API di Sketch ci offre anche un modo per archiviare informazioni su un determinato livello, che possiamo utilizzare come tag e in seguito identificare un gruppo come uno dei nostri gruppi Mosaic "speciali".

Rivisitiamo la logica che abbiamo scritto nella sezione precedente per affrontare questo problema. Il nostro codice originale segue i seguenti passaggi:

  1. Trova il documento corrente.
  2. Trova il livello selezionato del documento corrente.
  3. Duplica il livello selezionato (lo chiameremo livello modello ) x numero di volte.
  4. Per ogni duplicato, modifica la sua posizione, rotazione, opacità, ecc. in base ai valori (quantità) specifici impostati dall'utente.

Per rendere possibile il nostro nuovo flusso di utenti, dobbiamo modificare questi passaggi in:

  1. Prendi il documento corrente.
  2. Prendi il livello selezionato del documento corrente.
  3. Determina se il livello selezionato è un gruppo Mosaico o meno.
    • Se si tratta di un altro livello, utilizzalo come livello modello e vai al passaggio 4.
    • Se si tratta di un gruppo Mosaico, considera il primo livello al suo interno come livello modello e vai al passaggio 5.
  4. Avvolgi il livello del modello all'interno di un gruppo e contrassegna quel gruppo come gruppo Mosaico.
  5. Rimuovi tutti i livelli dall'interno del gruppo tranne il livello del modello.
  6. Duplica il livello del modello x il numero di volte.
  7. Per ogni duplicato, modifica la sua posizione, rotazione, opacità, ecc. in base ai valori specifici impostati dall'utente.

Abbiamo tre nuovi passaggi. Per il primo nuovo passaggio, passaggio 3, creeremo una funzione denominata findOrMakeSpecialGroupIfNeeded che esaminerà il livello passato per determinare se si tratta o meno di un gruppo Mosaic. Se lo è, lo restituiremo. Poiché l'utente potrebbe potenzialmente selezionare un sottolivello annidato in profondità in un gruppo Mosaic, dovremo anche controllare i genitori del livello selezionato per sapere se sono anche uno dei nostri gruppi Mosaic:

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

Se non siamo riusciti a trovare un gruppo Mosaic, avvolgeremo semplicemente il livello che ci è stato passato all'interno di un Group , quindi lo taggheremo come un gruppo Mosaic.

Tornando all'inizio del file, ora dovremo estrarre anche la classe Group:

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

Ora dobbiamo colmare le lacune (cose da fare). Per cominciare, abbiamo bisogno di un mezzo per identificare se un gruppo è o meno uno dei gruppi speciali che ci appartengono o meno. Qui, il modulo Settings della libreria Sketch viene in nostro soccorso. Possiamo usarlo per memorizzare informazioni personalizzate su un particolare livello e anche per rileggerle.

Una volta importato il modulo nella parte superiore del file:

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

Possiamo quindi utilizzare due metodi chiave che fornisce, setLayerSettingForKey e layerSettingForKey , per impostare e leggere i dati da un livello:

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

Ora che abbiamo un metodo che gestisce l'avvolgimento di un livello in un gruppo di mosaici (o, se è già un gruppo di mosaici, lo restituisce semplicemente) ora possiamo inserirlo nel nostro metodo di mosaic principale subito dopo i nostri controlli di sicurezza:

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

Successivamente aggiungeremo un ciclo per rimuovere tutti i livelli dal gruppo tranne il livello modello (che è il primo):

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

Infine, ci assicureremo che la dimensione del gruppo sia adattata al suo nuovo contenuto poiché l'utente potrebbe aver originariamente selezionato un livello nidificato all'interno del vecchio gruppo (un livello che potremmo aver rimosso).

Dovremo anche assicurarci di impostare la selezione corrente sul nostro gruppo di mosaici stesso. Ciò assicurerà che se l'utente sta apportando una serie di modifiche rapide allo stesso gruppo di mosaici, non verrà deselezionato. Dopo il codice che abbiamo già scritto per duplicare un livello, aggiungi:

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

Prova di nuovo il plugin. Dovresti scoprire che la modifica di un mosaico ora è molto più agevole!

Migliorare l'interfaccia

Un'altra cosa che potresti notare è la mancanza di sincronizzazione tra la finestra del display e l'interfaccia al suo interno, in quanto entrambe diventano visibili contemporaneamente. Ciò è dovuto al fatto che quando visualizziamo la finestra, non è garantito che l'interfaccia web abbia terminato il caricamento, quindi a volte "pop" o "flash in" in seguito.

Un modo per risolvere questo problema è ascoltare quando l'interfaccia web ha terminato il caricamento e solo allora mostrare la nostra finestra. Esiste un metodo, webView:didFinishNavigation: , che WKWebView chiamerà al termine del caricamento della pagina corrente. Possiamo usarlo per ottenere esattamente la notifica che stiamo cercando.

Di nuovo in ui.js , estenderemo l'istanza MochaJSDelegate che abbiamo creato per implementare questo metodo, che a sua volta chiamerà l'argomento onLoadFinish che passeremo a 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; };

E di nuovo nel metodo loadAndShow , lo regoleremo in modo che mostri la finestra solo dopo che la visualizzazione Web è stata caricata:

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

Bingo! Ora la nostra finestra viene visualizzata solo quando la visualizzazione web ha terminato il caricamento, evitando quel fastidioso sfarfallio visivo.

Conclusione

Congratulazioni, hai creato il tuo primo plug-in Sketch!

Se desideri installare e giocare con Mosaic, puoi scaricare il plug-in completo da GitHub. E prima di partire, ecco alcune risorse che potrebbero esserti utili durante il resto del tuo viaggio:

  • developer.sketchapp.com La risorsa ufficiale relativa allo sviluppo del plugin di Sketch. Contiene diverse guide utili, oltre a un riferimento API per la libreria JavaScript di Sketch.
  • sketchplugins.com Una community fantastica e utile di sviluppatori di plugin di Sketch. Ottimo per ottenere risposte a tutte le tue domande scottanti.
  • github.com/sketchplugins/plugin-directory Repository GitHub centrale e ufficiale dei plugin di Sketch. Puoi inviare i tuoi plugin qui e condividerli con il resto della community di Sketch!