So erstellen Sie ein Sketch-Plugin mit JavaScript, HTML und CSS (Teil 1)
Veröffentlicht: 2022-03-10Dieses Tutorial richtet sich an Personen, die die Sketch-App kennen und verwenden und keine Angst haben, sich mit Code zu beschäftigen. Um am meisten davon zu profitieren, müssen Sie zumindest einige grundlegende Erfahrungen mit dem Schreiben von JavaScript (und optional HTML/CSS) haben.
Das Plugin, das wir erstellen werden, heißt „Mosaic“. In Teil eins lernen wir die grundlegenden Dateien kennen, aus denen ein Sketch-Plugin besteht. Wir schreiben JavaScript und erstellen mit Hilfe von HTML und CSS eine Benutzeroberfläche für unser Plugin. Im nächsten Artikel geht es darum, wie Sie die Benutzeroberfläche mit dem Kern-Plugin-Code verbinden, wie Sie die Hauptfunktionen des Plugins implementieren, und am Ende erfahren Sie auch, wie Sie den Code und die Funktionsweise des Plugins optimieren.
Ich werde auch den Code des Plugins (JS, HTML, CSS) und Dateien teilen, die Sie untersuchen und für Lernzwecke verwenden können.
Was sind Sketch-Plugins und wie funktionieren sie?
In Sketch sind Plugins eine Möglichkeit, Features und Funktionen hinzuzufügen, die in Sketch „out of the box“ nicht vorhanden sind. Wenn man bedenkt, dass in jedem beliebigen Programm fast immer eine Funktion oder Integration fehlen wird (insbesondere angesichts der enormen Anzahl von Anforderungen, die ein einzelner Designer haben könnte!), kann man sich vorstellen, wie nützlich und leistungsfähig Plugins sein könnten. Sketch-Plugins können so ziemlich alles, was Sie erwarten, wie das Manipulieren von Farbe, Form, Größe, Reihenfolge, Stil, Gruppierung und Effekten von Ebenen, aber auch Dinge wie Anfragen an Internetressourcen stellen, einem Benutzer präsentieren Benutzeroberfläche und vieles mehr!
Auf der Programmierseite sind alle Sketch-Plugins in JavaScript-Code geschrieben. Nun, eigentlich stimmt das nicht ganz . Genauer gesagt sind die meisten Sketch-Plug-ins in JavaScript geschrieben, da es auch möglich ist, ein Sketch-Plug-in in einer der Programmiersprachen von Apple, Objective-C und Swift, zu schreiben, obwohl selbst sie ein wenig JavaScript-Kenntnisse erfordern.
Aber keine Sorge. In diesem Artikel konzentrieren wir uns darauf, wie Sie Sketch-Plug-ins allein mit JavaScript, HTML und CSS erstellen. Wir werden nicht auf die Grundlagen von HTML, CSS oder JavaScript eingehen – dieser Artikel setzt zumindest einige Kenntnisse und Erfahrungen mit all diesen dreien voraus. Die MDN-Entwickler-Website bietet einen großartigen Ort, um mehr über die Webentwicklung zu erfahren.
Lass uns anfangen!
Erstens, was machen wir?
In diesem Tutorial zeige ich Ihnen, wie Sie ein einfaches, anfängerfreundliches Plugin erstellen, mit dem Ebenen erstellt, dupliziert und geändert werden können und dem Benutzer eine schöne Benutzeroberfläche präsentiert wird. Mein Ziel ist es dabei, ein grundlegendes Wissen aufzubauen, auf dem Sie aufbauen und Ihre eigenen Plugins erstellen können.
Das Plugin, das wir bauen werden, heißt Mosaic und ist praktisch ein „Mustergenerator“. Füttere es mit deinen Ebenen, passe ein paar Einstellungen an und es wird ein Muster erstellt:
Wenn Sie Mosaic installieren und damit herumspielen möchten, können Sie das fertige Plugin von GitHub herunterladen.
Ein bisschen Geschichte: Mosaic ist zu einem großen Teil von einem Adobe Fireworks-Plug-in der alten Schule namens Twist-and-Fade inspiriert. Twist-and-Fade war ziemlich leistungsfähig und konnte eine Ebene beliebig oft duplizieren, während der Farbton, die Position, die Drehung, die Größe und die Deckkraft angepasst wurden. Das Plugin war sogar in der Lage, animierte GIFs zu generieren, wie dieses hier, wo es die Rahmen für die beiden rotierenden Elemente im Kassettenband erstellte:
(Hier ist ein Video, das Twist and Fade vorführt, wenn Sie daran interessiert sind, genau zu sehen, wie es funktioniert hat.)
Für die Zwecke dieses Tutorials werden wir ein etwas ähnliches Plugin für Sketch erstellen, wenn auch absichtlich vereinfacht, um das Tutorial so zugänglich wie möglich zu halten. Insbesondere wird unser Plugin in der Lage sein:
- Duplizieren Sie eine beliebige Skizzenebene (Bitmap oder Vektor) und optimieren Sie die Position, Drehung und Deckkraft der Ebene der Duplikate. Dies gibt uns eine Einführung in die Manipulation von Ebenen mit den JavaScript-APIs von Sketch.
- Zeigen Sie eine mit HTML, CSS und JS erstellte Benutzeroberfläche an, die Ihnen beibringt, wie Sie mithilfe von Webtechnologien, mit denen Sie möglicherweise bereits vertraut sind, auf einfache Weise eine Benutzeroberfläche für das Plugin erstellen. Die Plugin-Schnittstelle ist ziemlich wichtig, da wir die Eingaben des Benutzers dazu sammeln, wie der Benutzer das resultierende Mosaikbild aussehen lassen möchte.
Erstellung unseres Basis-Plugins in zehn Sekunden
Zuerst erstellen wir die „Basis“ (oder Vorlage) für das Plugin, das wir erstellen möchten. Wir könnten alle notwendigen Dateien und Ordner, aus denen ein Plugin besteht, manuell erstellen, aber zum Glück müssen wir das nicht – denn Sketch kann das für uns erledigen. Nachdem wir das Vorlagen-Plugin generiert haben, können wir es nach Belieben anpassen.
Es gibt eine wirklich schnelle und einfache Technik, mit der wir das Vorlagen-Plugin erstellen können, was so ziemlich meine bevorzugte Methode ist, wenn ich ein Plugin zusammenstellen muss, um ein beliebiges Problem zu lösen, mit dem ich gerade zu tun habe. So funktioniert das:
Überprüfen Sie bei geöffnetem Sketch die Menüleiste oben auf dem Bildschirm und klicken Sie auf Plugins -> Run Script
. Dadurch wird ein Dialogfeld geöffnet, mit dem wir den Code testen und ausführen können. Wir können auch jeden Code, den wir darin eingeben, als Plugin speichern, was der Teil ist, an dem wir gerade besonders interessiert sind.
Löschen Sie den Code, der sich bereits in diesem Dialogfeld befindet, und ersetzen Sie ihn durch den folgenden Democode:
const UI = require("sketch/ui"); UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");
Klicken Sie als Nächstes unten links im Fenster auf Save Script as Plugin
, geben Sie den gewünschten Namen für dieses Plugin ein (in unserem Fall ist dies „Mosaic“) und Save Script as Plugin
.
Ob Sie es glauben oder nicht, wir sind schon fertig – Sie müssen nur noch den Kuchen essen, den wir gerade gebacken haben. Hier kommt der lustige Teil. Wenn Sie das Plugins-Menü noch einmal öffnen, sollten Sie so etwas sehen: Ihr brandneues Plugin wird als „Mosaic“ aufgeführt! Klick es an!
Herzlichen Glückwunsch, Sie haben gerade Ihr erstes Sketch-Plugin geschrieben!
Was Sie sehen sollten, nachdem Sie auf „Mosaik“ geklickt haben, sollte wie im obigen kurzen Video aussehen, mit einer unauffälligen QuickInfo-Nachricht, die am unteren Rand des Bildschirms erscheint und mit den Worten „Hey there…“ beginnt – genau das sagt der Code, den wir eingefügt haben machen. Das macht diese Technik so großartig: Sie macht es einfach, Code einzufügen, zu ändern und zu testen, ohne ein Plugin von Grund auf neu erstellen zu müssen. Wenn Sie mit der Webkonsole Ihres Browsers vertraut sind oder jemals damit gespielt haben, ist dies im Grunde das Richtige. Dieses Tool in der Gesäßtasche zu haben, wenn Sie Code erstellen und testen, ist ein Muss.
Lassen Sie uns einen kurzen Überblick darüber geben, was der von Ihnen hinzugefügte Code bewirkt:
Zuerst importiert es das sketch/ui
-Modul der integrierten JS-Bibliothek von Sketch und weist es der UI
-Variablen zu. Dieses Modul enthält einige nützliche schnittstellenbezogene Methoden, von denen wir eine verwenden werden:
const UI = require("sketch/ui");
Als nächstes ruft es die message
-Methode (die Teil des sketch/ui
-Moduls ist) mit der Textzeichenfolge auf, die wir in der QuickInfo anzeigen möchten, die wir gesehen haben:
UI.message(" Hey there, you fantastic plugin developer you! This is your plugin! Talking to you from the digital computer screen! In Sketch! Simply stupendous!");
Die Methode message()
bietet eine großartige Möglichkeit, dem Benutzer eine unaufdringliche Nachricht zu präsentieren; Es ist großartig für Fälle, in denen Sie den Fokus nicht stehlen müssen (nicht modal) und keine ausgefallenen Schaltflächen oder Textfelder benötigen. Es gibt auch andere Möglichkeiten, allgemeine UI-Elemente wie Warnungen, Eingabeaufforderungen und dergleichen darzustellen, von denen wir einige beim Erstellen von Mosaic verwenden werden.
Anpassen der Metadaten unseres Plugins
Wir haben jetzt ein einfaches Plugin, mit dem wir beginnen können, aber wir müssen es noch weiter optimieren und es wirklich zu unserem machen. Unser nächster Schritt wird sein, die Metadaten des Plugins zu ändern.
Für diesen Schritt müssen wir einen Blick auf das sogenannte Plugin-Bundle werfen . Wenn Sie im Fenster „Skript ausführen“ auf „Speichern“ klicken, speichert Sketch Ihr Plugin als Ordner namens Mosaic.sketchplugin
, den Sie im Verzeichnis „ ~/Library/Application Support/com.bohemiancoding.sketch3/Plugins
finden. Das ist ein bisschen lang und nervig, sich daran zu erinnern; Als Abkürzung können Sie es auch über Plugins -> Manage Plugins -> (right-click your plugin) -> Reveal Plugins Folder
. Obwohl es im Finder als einzelne Datei angezeigt wird, ist es eigentlich ein Ordner, der alles enthält, was unser Plugin benötigt, damit Sketch es ausführen kann. Der Grund, warum es als einzelne Datei angezeigt wird, obwohl es sich um einen Ordner handelt, liegt darin, dass Sketch bei der ersten Installation von Sketch die Erweiterung .sketchplugin
als „Bundle“ (eine spezielle Art von Ordner, der als Datei angezeigt wird) registriert und darum gebeten hat, dass es automatisch geöffnet wird in Sketch, wenn geöffnet.
Werfen wir einen Blick hinein. Klicken Sie mit der rechten Maustaste auf Mosaic.sketchplugin
und dann auf „Paketinhalt anzeigen“. Darin sollten Sie die folgende Verzeichnisstruktur sehen:
Contents/ └ Resources/ └ Sketch/ └ manifest.json └ script.cocoascript
Sie fragen sich vielleicht, warum es dort eine Datei mit der Erweiterung .cocoascript
gibt. Keine Sorge – es ist nur eine normale JavaScript-Datei und enthält nur den Code, den wir zuvor eingegeben haben. Fahren Sie fort und benennen Sie diese Datei in index.js
um, wodurch die Verzeichnisstruktur so geändert wird, dass sie wie folgt aussieht:
Contents/ └ Resources/ └ Sketch/ └ manifest.json └ index.js
Die gebräuchlichste Art, die Dateien in einem Plugin-Bundle zu organisieren, ist wie folgt: Ihr Code (JavaScript) und manifest.json
gehören in Sketch/
und Ressourcen (denken Sie an Bilder, Audiodateien, Textdateien usw.) gehören in Resources/
.
Beginnen wir damit, die Datei mit dem Namen manifest.json
zu optimieren. Öffnen Sie es in Ihrem bevorzugten Code-Editor, z. B. Visual Studio Code oder Atom.
Sie werden sehen, dass hier im Moment relativ wenig drin ist, aber wir werden bald mehr hinzufügen. Das Plugin-Manifest dient hauptsächlich zwei Zwecken:
- Erstens stellt es Metadaten bereit, die das Plugin für den Benutzer beschreiben – Dinge wie Name, Version, Name des Autors und so weiter. Sketch verwendet diese Informationen im Dialog
Sketch -> Preferences -> Plugins
, um eine Auflistung und Beschreibung für Ihr Plugin zu erstellen. - Zweitens teilt es Sketch auch mit, wie Sie zu Ihrem Geschäft kommen können. Das heißt, es teilt Sketch mit, wie das Menü Ihres Plugins aussehen soll, welche Hotkeys Ihrem Plugin zugewiesen werden sollen und wo sich der Code Ihres Plugins befindet (damit Sketch es ausführen kann).
In Anbetracht von Zweck Nr. 1, der Beschreibung des Plugins für den Benutzer, werden Sie wahrscheinlich feststellen, dass im Moment keine Beschreibung oder ein Autor angegeben ist, was für den Benutzer verwirrend wäre und die Identifizierung des Plugins erschweren würde. Lassen Sie uns das beheben, indem wir die Werte der relevanten Schlüssel anpassen auf:
{ "description": "Generate awesome designs and repeating patterns from your layers!", "author": "=> Your name here <=" }
Als nächstes passen wir die Kennung des Plugins an. Diese Kennung verwendet eine sogenannte „umgekehrte Domain-Notation“, was eine wirklich prägnante (oder langweilige, wählen Sie selbst) Art ist, um zu sagen: „Nehmen Sie die Domain Ihrer Website, kehren Sie die Reihenfolge um und setzen Sie dann den Namen Ihres Produkts an das Ende.“ Dies wird in etwa so aussehen: com.your-company-or-your-name-its-not-that-big-a-deal.yourproduct
.
Sie müssen sich nicht an diese Namenskonvention halten – Sie können hier alles einfügen, was Sie wollen, solange es einzigartig genug ist, um Konflikte mit anderen Plugins zu vermeiden (obwohl es wahrscheinlich eine gute Idee ist, sich an das RDN-Format zu halten, besonders da es ein einfaches, wiederverwendbares System für Ihre Plugin-Identifikatoren).
Ändern Sie dazu Ihre Kennung in com.your-name.mosaic
:
{ "identifier": "com.your-name.mosaic" }
Ich persönlich nehme gerne alle metadatenbezogenen Schlüssel (Titel, Autor, Kennung usw.) und gruppiere sie am oberen Rand des Manifests, damit sie nicht überall verstreut sind und helfen, meinen Verstand zu bewahren, wenn ich sie finden muss .
Als nächstes werfen wir einen Blick auf die menu
und commands
. Diese beiden sind dafür verantwortlich, Sketch mitzuteilen, welcher Code aufgerufen und worauf reagiert werden soll.
Wenn Sie sich die menu
ansehen, sehen Sie, dass sie eine title
enthält, deren Wert der Name ist, mit dem unser Plugin im Plugins
-Menü angezeigt wird. Es hat auch einen items
-Schlüssel, der eine Liste von Befehlskennungen ist :
{ "menu": { "title": "Mosaic", "items": [ "com.bohemiancoding.sketch.runscriptidentifier" ] } }
Im Moment gibt es nur eine Befehlskennung in dieser Liste, "com.bohemiancoding.sketch.runscriptidentifier"
. Befehlsbezeichner zeigen immer auf einen Befehl in der commands
. Im Moment hat unser Plugin nur einen Befehl, und zwar den mit dieser Kennung:
{ "commands": [ { "script" : "script.cocoascript", "name" : "Mosaic", "handlers" : { "run" : "onRun" }, "identifier" : "com.bohemiancoding.sketch.runscriptidentifier" } ] }
Immer wenn Sie einem menu
eine Befehlskennung hinzufügen, sucht Sketch nach dem Befehlseintrag mit dieser Kennung und zeigt den Wert seines name
(in diesem Fall „Mosaik“) an und zeigt ihn stattdessen im Menü Ihres Plugins an der Kennung.
Was die Rolle von Befehlen betrifft, so können wir uns einen Befehlseintrag vorstellen, um Sketch mitzuteilen, welche Funktion im JavaScript-Code unseres Plugins wir ausführen möchten, wenn dieser Befehl aufgerufen wird, wobei der „Aufruf“ normalerweise der Klick des Benutzers auf das zugehörige Menü ist Artikel. Der Befehlseintrag selbst macht nichts, es ist nur JSON – er stellt einfach eine Beschreibung für Sketch bereit, wo nach dem JavaScript gesucht werden soll, das es ausführen muss, wenn der Befehl aufgerufen wird.
Bisher haben wir darüber gesprochen, was die name
und identifier
eines Befehls bewirken, aber es gibt zwei weitere Schlüssel in einem Befehl, die angesprochen werden müssen: script
und handlers
.
Der script
teilt Sketch mit, wo sich die auszuführende JavaScript-Datei befindet. Beachten Sie, dass Sketch davon ausgeht, dass sich die betreffende Skriptdatei im Ordner Sketch/
befindet. Aus diesem Grund sollten Sie der Einfachheit halber sicherstellen, dass sich Ihr gesamter JavaScript-Code irgendwo im Ordner Sketch/
befindet. Bevor wir von diesem Schlüssel fortfahren, ist es wichtig , dass Sie den Wert dieses Schlüssels in index.js
, genau wie wir die Datei zuvor umbenannt haben. Andernfalls kann Sketch Ihre JavaScript-Datei nicht finden und ausführen.
Der Wert des handlers
-Schlüssels ist das, was Sketch betrachtet, um zu bestimmen, welche Funktion in Ihrem JavaScript aufgerufen werden soll. Hier haben wir nur einen Handler-Satz: run
mit dem Wert onRun
. run
ist der Name einer vordefinierten, integrierten Sketch- Aktion . Diese run
wird immer aufgerufen, wenn ein Benutzer auf ein Menüelement klickt, das auf diesen Befehl verweist. onRun
ist der Name einer Funktion in der automatisch generierten script.cocoascript
-Datei (die wir in index.js
umbenannt haben) und die Funktion, die aufgerufen werden soll, wenn das run
-Ereignis eintritt, dh wenn der Benutzer auf das Menüelement klickt.
In dem Beispiel, das wir bisher haben, spielt sich dieser Prozess ungefähr so ab:
- Der Benutzer klickt auf unseren Menüpunkt.
- Sketch findet den Befehl, der diesem Menüelement zugeordnet ist.
- Sketch findet die Skriptdatei, auf die sich der Befehl bezieht, und führt sie aus (was in diesem Fall bedeutet, dass das JavaScript in
index.js
). - Da dieser Befehl durch Klicken auf ein Menüelement aufgerufen wurde, wird er als
run
betrachtet. Das bedeutet, dass Sketch denhandlers.run
-Wert des Befehls nach der Funktion sucht, die als nächstes aufgerufen werden soll, was in diesem FallonRun
ist. - Sketch ruft die
onRun
Funktion auf.
Befehle werden am häufigsten als Reaktion darauf aufgerufen, dass ein Benutzer auf eines Ihrer Menüelemente klickt, aber sie können auch als Reaktion auf andere Benutzeraktionen aufgerufen werden, z. B. wenn der Benutzer die Auswahl oder eine Eigenschaft auf einer Ebene ändert. Für dieses Plugin verwenden wir jedoch keine dieser anderen Aktionen. (Weitere Informationen zu Aktionen und ihrer Funktionsweise finden Sie auf der Hilfeseite der Aktions-API.)
Bevor wir von diesem Manifest fortfahren, wollen wir zwei weitere Anpassungen vornehmen. Im Moment hat unsere Speisekarte die Struktur:
Mosaic └ Mosaic
…was etwas überflüssig ist, da unser Plugin nur einen Menüpunkt hat. Es fügt unseren Benutzern auch ein wenig unnötige Reibung hinzu, da unser Plugin jetzt zwei Klicks benötigt, um aufgerufen zu werden, anstatt nur einen. Wir können dies beheben, indem isRoot: true
zu unserem menu
hinzufügen:
{ "menu": { "title" : "Mosaic", "items" : [ "com.bohemiancoding.sketch.runscriptidentifier" ], "isRoot": true } }
Dies weist Sketch an, die erste Ebene der Menüelemente direkt unter dem Plugins
-Menü zu platzieren, anstatt sie unter dem title
des Menüs zu verschachteln.
Klicken Sie auf Speichern und kehren Sie zu Sketch zurück. Sie sollten sehen, dass jetzt Mosaic -> Mosaic
durch Mosaic
ersetzt wurde – perfekt!
Was unsere zweite Optimierung betrifft, lassen Sie uns fortfahren und diese Befehlskennung in etwas weniger Unhandliches umbenennen. Da Befehlsbezeichner nur im Kontext eines einzelnen Plugins eindeutig sein müssen, können wir es getrost in etwas Prägnanteres und Offensichtlicheres umbenennen, wie "open"
:
{ "commands": [ { ... "identifier" : "open" } ], "menu": { ... "items" : [ "open" ] } }
Bevor wir fortfahren, ist es nützlich zu beachten, dass Menüs auch andere Menüs enthalten können. Sie könnten ganz einfach ein Untermenü erstellen, indem Sie einen weiteren { title: ..., items: ... }
items
in der Elementliste eines anderen Menüs verschachteln:
{ "menu": { "title" : "Mosaic", "items" : [ "open", { "title" : "I'm a sub-menu!", "items" : [ "another-command-identifier" ] } ] } }
Erstellen der Benutzeroberfläche des Plugins
Bisher haben wir Democode geschrieben und das Manifest unseres Plugins angepasst. Wir werden nun mit der Erstellung seiner Benutzeroberfläche fortfahren, die im Wesentlichen eine Webseite ist, die in ein Fenster eingebettet ist (ähnlich wie bei den Browsern, mit denen Sie vertraut sind):
Das Fenster
Das Design der Benutzeroberfläche von Mosaic hat ein eigenes Fenster, das wir als grundlegendste Komponente betrachten können. wir fangen damit an. Um ein Fenster zu erstellen und anzuzeigen, müssen wir eine Klasse verwenden, die standardmäßig in macOS integriert ist und NSWindow
heißt. Im weiteren Verlauf dieses Tutorials werden wir dies tatsächlich ziemlich oft tun (unter Verwendung integrierter APIs wie NSWindow
), was ein wenig entmutigend erscheinen mag, wenn Sie damit nicht vertraut sind, aber keine Sorge – ich werde es erklären alles auf dem Weg!
Hinweis: Während wir über integrierte APIs sprechen, ist der Grund, warum wir diese Klasse überhaupt verwenden können, eine Brücke, die in der JavaScript-Laufzeit vorhanden ist, die von Sketch-Plug-ins verwendet wird. Diese Bridge importiert automatisch diese integrierten Klassen, Methoden und Funktionen, die normalerweise nur nativen Anwendungen zur Verfügung stehen.
Öffnen Sie Sketch/index.js
in Ihrem Code-Editor, löschen Sie, was bereits vorhanden ist, und fügen Sie Folgendes ein:
function onRun(context){ const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; window.makeKeyAndOrderFront(nil); };
Schauen wir uns an, was dieses erste Stück Code bewirkt:
function onRun(context){
Erinnern Sie sich noch, als wir über Befehle und ihre Funktionsweise gesprochen haben und wir Sketch angewiesen haben, als Antwort auf einen Menüklick aufzurufen, hieß onRun
? (Wenn Sie eine Auffrischung benötigen, sehen Sie sich den obigen Teil noch einmal an und kommen Sie dann zurück.) Alles, was dieses Bit tut, ist, diese Funktion zu erstellen. Sie werden auch feststellen, dass unsere onRun
Funktion ein context
akzeptiert. Dies ist ein Argument, mit dem Sketch Ihre Befehlshandler aufruft, die uns bestimmte Informationen liefern können. Später werden wir es verwenden, um die URL unseres Plugin-Pakets auf dem Computer des Benutzers abzurufen.
const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false );
Hier machen wir tatsächlich ein paar Dinge:
- Zuerst rufen wir
alloc()
aufNSWindow
auf; dies bedeutet im Grunde „etwas Speicher für eine Instanz von NSWindow reservieren“. Es genügt zu wissen, dass Sie dies für jede Instanz einer nativen Klasse tun müssen, die Sie erstellen möchten. Diealloc
Methode ist in jeder nativen Klasse verfügbar. - Als Nächstes rufen wir die Initialisierungsmethode von
NSWindow
auf (d. h. die Methode, die tatsächlich eine Instanz vonNSWindow
), dieinitWithContentRect:styleMask:backing:defer:
. Sie werden feststellen, dass sich das von dem unterscheidet, was wir in unserem obigen Code nennen – es hat eine Reihe von Doppelpunkten (:
) zwischen jedem Argument. Da wir diese Syntax in JavaScript nicht verwenden können, benennt Sketch sie bequem in etwas um, das wir tatsächlich verwenden können, indem die Doppelpunkte durch Unterstriche ersetzt werden, wodurch wir ihren JS-Namen erhalten:initWithContentRect_styleMask_backing_defer
. - Als nächstes übergeben wir jedes der Argumente, die die Methode benötigt. Für das erste Argument,
contentRect
, liefern wir ein Rechteck, das groß genug für unsere Benutzeroberfläche ist. - Für
styleMask
verwenden wir eine Bitmaske, die besagt, dass unser Fenster eine Schaltfläche zum Schließen, eine Titelleiste und eine Größenänderung haben soll. - Die nächsten beiden Argumente,
backing
unddefer
, werden immer aufNSBackingStoreBuffered
undfalse
gesetzt, sodass wir uns nicht wirklich darum kümmern müssen. (Die Dokumentation für diese Methode geht näher darauf ein, warum dies so ist.)
window.releasedWhenClosed = false; window.makeKeyAndOrderFront(null);
Hier setzen wir die Eigenschaft releasedWhenClosed
von NSWindow
auf false
, was bedeutet: „Hey! Löschen Sie dieses Fenster nicht aus dem Speicher, nur weil der Benutzer es schließt.“ Dann rufen wir makeKeyAndOrderFront
(null)
auf, was bedeutet: „Bewege dieses Fenster in den Vordergrund und gib ihm den Tastaturfokus.“
Webansicht: Die Benutzeroberfläche
Zur Vereinfachung habe ich bereits den HTML- und CSS-Code der Web-Benutzeroberfläche des Plugins geschrieben, die wir verwenden werden; Der einzige verbleibende Code, den wir hinzufügen müssen, wird sicherstellen, dass wir zwischen ihm und unserem Sketch-Plug-in-Code kommunizieren können.
Laden Sie als Nächstes den HTML- und CSS-Code herunter. Nachdem Sie es heruntergeladen haben, extrahieren Sie es und verschieben Sie dann den Ordner mit dem Namen „web-ui“ in den Ressourcenordner unseres Plugins.
Hinweis : Das Schreiben und Optimieren des eigentlichen HTML/CSS-Codes liegt außerhalb des Rahmens dieses Tutorials, da der Schwerpunkt auf JavaScript liegt, das die Kernfunktionen des Plugins unterstützt. Aber es gibt eine Menge Tutorials im Internet zu diesem Thema, falls Sie mehr erfahren möchten.
Wenn Sie unser Plugin jetzt ausführen, werden Sie sehen, dass es ein Fenster anzeigt – yay, Fortschritt! Aber es ist leer, ohne Titel und noch nicht besonders nützlich. Wir müssen es dazu bringen, unsere Weboberfläche anzuzeigen. Dazu müssen wir eine andere native Klasse verwenden, WKWebView
, eine Ansicht, die speziell für die Anzeige von Webinhalten erstellt wurde.
Wir fügen den Code, der zum Erstellen unseres WKWebView
benötigt wird, unter dem Code hinzu, den wir für unser Fenster geschrieben haben:
function onRun(context){ // Create window const window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.releasedWhenClosed = false; // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the web view const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };
Wenn wir jetzt unser Plugin ausführen, sehen wir, dass wir jetzt ein Fenster geöffnet haben, das unsere Web-Benutzeroberfläche anzeigt. Erfolg!
Bevor wir fortfahren, wollen wir noch einmal untersuchen, was der von uns hinzugefügte Code bewirkt:
const webView = WKWebView.alloc().init();
Das sollte Ihnen bekannt vorkommen – es ist im Grunde das Gleiche wie bei der Erstellung unseres NSWindow
: Speicher für eine Webansicht zuweisen und dann initialisieren.
window.contentView = webView;
Diese Codezeile weist unser Fenster an, die gerade erstellte Webansicht anzuzeigen.
const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/");
Hier ist unser Ziel, eine URL zu erstellen, die auf den zuvor erstellten web-ui
Ordner verweist. Um diese URL zu erhalten, müssen wir herausfinden, wo sich das Paket unseres Plugins im Dateisystem des Benutzers befindet. Hier verwenden wir die Eigenschaft context.scriptURL
, die uns die URL des aktuell ausgeführten Skripts liefert. Dies gibt uns jedoch nicht wie erwartet einen JavaScript- String
, sondern eine Instanz einer nativen Klasse, NSURL
, die einige Methoden enthält, die die Bearbeitung von URL-Strings vereinfachen.
Wir müssen umdrehen, was uns context.scriptURL
gibt —
file://path-to-your-plugin/Contents/Sketch/index.js
- hinein:
file://path-to-your-plugin/Contents/Resources/web-ui/
Schritt für Schritt:
- Der erste Aufruf
URLByDeletingLastPathComponent()
gibt unsfile://path-to-your-plugin/Contents/Sketch/
- Der erneute Aufruf
URLByDeletingLastPathComponent()
gibt unsfile://path-to-your-plugin/Contents/
- Und schließlich ergibt das Hinzufügen von
Resources/web-ui/
am Ende mitURLByAppendingPathComponent
("Resources/web-ui/")
file://path-to-your-plugin/Contents/Resources/web-ui/
Wir müssen auch eine zweite URL erstellen, die direkt auf die Datei index.html
verweist:
const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html");
Schließlich weisen wir unsere Webansicht an, index.html
zu laden und ihr Zugriff auf den Inhalt des Ordners web-ui
zu gewähren:
webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL);
In Ordung. Bisher haben wir ein Fenster, das unsere Web-Benutzeroberfläche anzeigt, genau wie wir es wollten. Es ist jedoch noch nicht ganz fertig – unser ursprüngliches Design hat keine Titelleiste (oder „Chrom“), aber unser aktuelles Fenster hat eine. Es gibt auch die Tatsache, dass, wenn wir in ein Sketch-Dokument klicken, dieses Dokument vor unser Fenster verschoben wird, was nicht das ist, was wir wollen – wir möchten, dass der Benutzer mit dem Plugin-Fenster und dem Sketch-Dokument interagieren kann, ohne dies tun zu müssen ständig von einem Fenster zum anderen neu fokussieren.
Um dies zu beheben, müssen wir zuerst das Standardfensterchrom entfernen und nur die Schaltflächen behalten. Durch Hinzufügen der beiden folgenden Codezeilen wird die Titelleiste entfernt.
Hinweis: Wie zuvor sind alle unten verwendeten Eigenschaften und Methoden auf der Dokumentationsseite von NSWindow
dokumentiert.
window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden;
Diese nächsten beiden Codezeilen entfernen die Fensterschaltflächen (im MacOS-Jargon auch als „Ampeln“ bezeichnet), die wir nicht benötigen – „Zoom“ und „Minimieren“ – und lassen nur die Schaltfläche „Schließen“ übrig:
window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true;
Wenn wir schon dabei sind, ändern wir auch die Hintergrundfarbe des Fensters, damit sie mit der unserer Web-Benutzeroberfläche übereinstimmt:
window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1);
Als nächstes müssen wir etwas tun, um unser schwebendes Plugin-Fenster über anderen Fenstern zu halten, damit der Benutzer mit seinen Sketch-Dokumenten interagieren kann, ohne sich Sorgen machen zu müssen, dass das Fenster des Mosaiks verschwindet. Wir können dafür einen speziellen Typ von NSWindow
namens NSPanel
, der in der Lage ist, über anderen Fenstern „im Vordergrund zu bleiben“. Dazu muss lediglich NSWindow
in NSPanel
, was eine einzeilige Codeänderung ist:
const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer(
Jetzt weisen wir unser Panel-Fenster an, zu schweben (über allen anderen zu bleiben) und nur bei Bedarf den Tastatur-/Maus-Fokus zu nehmen:
window.floatingPanel = true; window.becomesKeyOnlyIfNeeded = true;
Wir können unser Fenster auch so optimieren, dass es automatisch an der letzten Position wieder geöffnet wird, an der es war:
window.frameAutosaveName = "mosaic-panel-frame";
Diese Zeile besagt im Grunde: „Merken Sie sich die Position dieses Fensters, indem Sie es mit den Einstellungen von Sketch unter dem Schlüssel mosaic-panel-frame
speichern“.
Insgesamt haben wir nun folgenden Code:
function onRun(context){ // Create window const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 145, 500), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); // Create web view, and set it as the view for our window to display const webView = WKWebView.alloc().init(); window.contentView = webView; // Load our UI into the webview const webUIFolderURL = context.scriptURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/"); const indexURL = webUIFolderURL.URLByAppendingPathComponent("index.html"); webView.loadFileURL_allowingReadAccessToURL(indexURL, webUIFolderURL); // Make window key and move to front window.makeKeyAndOrderFront(nil); };
Organisation des Kodex
Bevor wir zum nächsten Teil übergehen, ist es eine gute Idee, unseren Code so zu organisieren, dass er einfacher zu navigieren und zu optimieren ist. Da wir noch viel mehr Code hinzufügen müssen und wir vermeiden möchten, dass index.js
zu einem unordentlichen Abladeplatz für unseren gesamten Code wird, teilen wir die Dinge ein wenig auf und verschieben unseren UI-spezifischen Code in eine Datei namens ui.js
. unter dem Sketch
Ordner. Wir werden auch einige der UI-Aufgaben, die wir ausführen, wie das Erstellen der Webansicht und des Fensters, in ihre eigenen Funktionen extrahieren.
Erstellen Sie eine neue Datei namens ui.js
und fügen Sie den folgenden Code darin ein:
// Private var _window; function createWebView(pageURL){ const webView = WKWebView.alloc().init(); webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; }; function createWindow(){ const window = NSPanel.alloc().initWithContentRect_styleMask_backing_defer( NSMakeRect(0, 0, 420, 646), NSWindowStyleMaskClosable | NSWindowStyleMaskTitled | NSWindowStyleMaskResizable, NSBackingStoreBuffered, false ); window.becomesKeyOnlyIfNeeded = true; window.floatingPanel = true; window.frameAutosaveName = "mosaic-panel-frame"; window.releasedWhenClosed = false; window.standardWindowButton(NSWindowZoomButton).hidden = true; window.standardWindowButton(NSWindowMiniaturizeButton).hidden = true; window.titlebarAppearsTransparent = true; window.titleVisibility = NSWindowTitleHidden; window.backgroundColor = NSColor.colorWithRed_green_blue_alpha(1, 0.98, 0.98, 1); return window; }; function showWindow(window){ window.makeKeyAndOrderFront(nil); }; // Public function loadAndShow(baseURL){ if(_window){ showWindow(_window); return; } const pageURL = baseURL .URLByDeletingLastPathComponent() .URLByAppendingPathComponent("../Resources/web-ui/index.html"); const window = createWindow(); const webView = createWebView(pageURL); window.contentView = webView; _window = window; showWindow(_window); }; function cleanup(){ if(_window){ _window.orderOut(nil); _window = null; } }; // Export module.exports = { loadAndShow, cleanup };
Es gibt ein paar wichtige Änderungen, die wir hier vorgenommen haben und die es zu beachten gilt. Neben der Tatsache, dass wir spezifische Funktionen zum Erstellen, Verstecken und Anzeigen unseres Fensters und seiner Webansicht erstellt haben, haben wir auch unseren Benutzeroberflächencode modularisiert .
Beachten Sie die module.exports = { loadAndShow, cleanup }
unten? Auf diese Weise können wir genau angeben, welche Objekte und Funktionen Skripte verwenden können, die diesen UI-Code importieren (und diejenigen ausblenden, um die sie sich nicht kümmern sollen), was bedeutet, dass wir jetzt eine besser organisierte API für die Interaktion haben. Anzeigen und Zerstören unserer Benutzeroberfläche.
Empfohlene Lektüre : Das volle Potenzial von Symbolen in Skizzen freisetzen
Mal sehen, wie das in der Praxis aussieht. Zurück in index.js
entfernen Sie den alten Code und fügen Folgendes hinzu:
const UI = require("./ui"); function onRun(context){ UI.loadAndShow(context.scriptURL); };
Wir verwenden eine spezielle Funktion, die uns Sketch automatisch zur Verfügung stellt, require
, um unseren ui.js
-Code zu importieren und das zurückgegebene Modul der UI
-Variablen zuzuweisen. Dadurch erhalten wir Zugriff auf eine vereinfachte API zum Auslösen unserer Benutzeroberfläche. Die Dinge sind jetzt viel aufgeräumter und leichter zu finden!
Fazit
Gut gemacht – du bist weit gekommen! In the next part of this tutorial, we'll give our web UI the ability to send us a message when the “Apply” button is clicked, and we'll focus on the main plugin functionality: actually generating layer mosaics!