Как создать плагин Sketch с помощью JavaScript, HTML и CSS (часть 2)
Опубликовано: 2022-03-10Как упоминалось в части 1, это руководство предназначено для людей, которые знают и используют приложение Sketch, а также не боятся возиться с кодом. Чтобы извлечь из этого максимальную пользу, вам потребуется хотя бы базовый опыт написания JavaScript (и, по желанию, HTML/CSS).
В предыдущей части этого руководства мы узнали об основных файлах, из которых состоит плагин, и о том, как создать пользовательский интерфейс плагина. Во второй и последней части мы узнаем, как связать пользовательский интерфейс с основным кодом плагина и как реализовать основные функции плагина. И последнее, но не менее важное: мы также узнаем, как оптимизировать код и как работает плагин.
Построение пользовательского интерфейса плагина: заставить наш веб-интерфейс и код плагина Sketch «разговаривать» друг с другом
Следующее, что нам нужно сделать, это настроить связь между нашим веб-интерфейсом и плагином Sketch.
Нам нужно иметь возможность отправлять сообщение из нашего веб-интерфейса в плагин Sketch при нажатии кнопки «Применить» в нашем веб-интерфейсе. Это сообщение должно сообщить нам о том, какие настройки ввел пользователь — например, количество шагов, количество поворотов, количество создаваемых дубликатов и т. д.
WKWebView
немного облегчает нам эту задачу: мы можем отправлять сообщения в наш плагин Sketch из кода JavaScript нашего веб-интерфейса, используя API window.webkit.messageHandlers
.
На стороне нашего кода Sketch мы можем использовать другой метод, addScriptMessageHandler:name:
(или addScriptMessageHandler_name
), чтобы зарегистрировать обработчик сообщений, который будет вызываться всякий раз, когда он получает сообщение, отправленное из веб-интерфейса нашего плагина.
Давайте начнем с того, что убедимся, что мы можем получать сообщения из нашего веб-интерфейса. Перейдите к функции ui.js
нашего файла createWebView
и добавьте следующее:
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; };
Здесь мы используем свойство userContentController
веб-представления, чтобы добавить обработчик сообщений, который мы назвали «sketchPlugin». Этот «контроллер пользовательского контента» является мостом, обеспечивающим передачу сообщений из нашего веб-представления.
Вы могли заметить что-то странное в приведенном выше коде: объект, который мы добавляем в качестве обработчика сообщений, ourMessageHandler
, еще не существует! К сожалению, мы не можем просто использовать обычный объект или функцию JavaScript в качестве обработчика, так как этот метод ожидает определенный тип нативного объекта.
К счастью для нас, мы можем обойти это ограничение, используя MochaJSDelegate
, мини-библиотеку, которую я написал, которая позволяет создавать нужный нам нативный объект с помощью обычного старого JavaScript. Вам нужно будет вручную загрузить и сохранить его в комплекте плагинов в Sketch/MochaJSDelegate.js
.
Чтобы использовать его, нам нужно сначала импортировать его в ui.js
Добавьте следующее в начало файла:
const MochaJSDelegate = require("./MochaJSDelegate");
Теперь мы можем использовать MochaJSDelegate
для создания типа обработчика сообщений, addScriptMessageHandler:name:
::
function createWebView(pageURL){ const webView = WKWebView.alloc().init(); // Set handler for messages from script const userContentController = webView.configuration().userContentController(); const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { /* handle message here */ } }).getClassInstance(); userContentController.addScriptMessageHandler_name( scriptMessageHandler, "sketchPlugin" ); // Load page into web view webView.loadFileURL_allowingReadAccessToURL(pageURL, pageURL.URLByDeletingLastPathComponent()); return webView; };
Код, который мы только что добавили, создает нужный нам нативный объект. Он также определяет метод для этого объекта с именем userContentController:didReceiveScriptMessage:
— этот метод затем вызывается с нужным нам сообщением в качестве второго аргумента. Поскольку на самом деле мы еще не отправляем никаких сообщений, нам придется вернуться сюда позже и добавить некоторый код для фактического анализа и обработки получаемых сообщений.
Затем нам нужно добавить код в наш веб-интерфейс, чтобы отправлять нам эти сообщения. Перейдите к /Resources/web-ui/script.js
. Вы обнаружите, что я уже написал большую часть кода, который обрабатывает получение значений HTML <inputs />
, в которые пользователь будет вводить свои параметры.
Что нам еще осталось сделать, так это добавить код, который фактически отправляет значения в наш код Sketch:
Найдите функцию apply
и добавьте в ее конец следующее:
// Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));
Здесь мы используем API window.webkit.messageHandlers
, о котором упоминалось ранее, для доступа к обработчику сообщений, который мы зарегистрировали выше как sketchPlugin
. Затем отправьте ему сообщение со строкой JSON, содержащей ввод пользователя.
Давайте удостоверимся, что все настроено правильно. Вернитесь к /Sketch/ui.js
. Чтобы убедиться, что мы получаем сообщения, как мы ожидаем, мы изменим метод, который мы определили ранее, чтобы он отображал диалоговое окно, когда мы получаем сообщение:
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" ); // ... };
Теперь запустите плагин (возможно, вам придется сначала закрыть любое существующее окно Mosaic, которое вы открыли), введите некоторые значения, затем нажмите «Применить». Вы должны увидеть предупреждение, подобное приведенному ниже — это означает, что все подключено правильно, и наше сообщение успешно прошло! Если нет, вернитесь к предыдущим шагам и убедитесь, что все сделано так, как описано.
Теперь, когда мы можем отправлять сообщения из нашего интерфейса в наш плагин, мы можем перейти к написанию кода, который на самом деле делает что-то полезное с этой информацией: создание нашей мозаики слоев.
Создание мозаики слоев
Давайте подведем итоги того, что необходимо для того, чтобы это произошло. Немного упрощая, наш код должен сделать следующее:
- Найдите текущий документ.
- Найдите выбранный слой текущего документа.
- Продублируйте выбранный слой (назовем его слоем- шаблоном ) x количество раз.
- Для каждого дубликата настройте его положение, поворот, непрозрачность и т. д. с помощью конкретных значений (количеств), установленных пользователем.
Теперь, когда у нас есть разумный план, давайте продолжим писать. Придерживаясь нашего шаблона модуляризации нашего кода, давайте создадим новый файл mosaic.js
в папке Sketch/
и добавим в него следующий код:
function mosaic(options){ }; module.export = mosaic;
Мы будем использовать эту функцию как единственный экспорт этого модуля, так как она упрощает использование API после его импорта — мы можем просто вызвать mosaic()
с любыми параметрами, которые мы получаем из веб-интерфейса.
Первые два шага, которые нам нужно сделать, это получить текущий документ, а затем его выбранный слой. Sketch API имеет встроенную библиотеку для работы с документами, к которой мы можем получить доступ, импортировав модуль sketch/dom
. Сейчас нам нужен только объект Document
, поэтому мы вытащим его явно. В верхней части файла добавьте:
const { Document } = require("sketch/dom");
Объект Document
имеет метод, специально предназначенный для доступа к текущему документу, который мы можем использовать, называемый getSelectedDocument()
. Получив текущий экземпляр документа, мы можем получить доступ к любым слоям, выбранным пользователем, через свойство selectedLayers
документа. Однако в нашем случае нас интересуют только однослойные выделения, поэтому мы возьмем только первый выбранный пользователем слой:
function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;
Примечание. Возможно, вы ожидали, что selectedLayers
будет массивом, но это не так. Вместо этого это экземпляр класса Selection
. Для этого есть причина: класс Selection
содержит кучу полезных вспомогательных методов для управления выделением, таких как clear, map, reduce и forEach. Он предоставляет фактический массив слоев через свойство layer
.
Давайте также добавим предупреждение на случай, если пользователь забудет открыть документ или что-то выбрать:
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;
Теперь, когда мы написали код для шагов 1 и 2 (поиск текущего документа и выбранного слоя), нам нужно обратиться к шагам 3 и 4:
- Дублируйте слой шаблона x количество раз.
- Для каждого дубликата настройте его положение, поворот, непрозрачность и т. д. с помощью определенных значений, установленных пользователем.
Давайте начнем с извлечения всей необходимой информации из options
: количество раз дублирования, начальные параметры и параметры шага. Мы можем снова использовать деструктурирование (как мы делали ранее с Document
), чтобы извлечь эти свойства из options
:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }
Затем давайте продезинфицируем наши входные данные и обеспечим, чтобы количество шагов всегда было не менее 1:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }
Теперь нам нужно убедиться, что непрозрачность слоя шаблона, поворот и т. д. соответствуют желаемым пользователем начальным значениям. Поскольку применение пользовательских опций к слою будет чем-то, чем мы будем много заниматься, мы переместим эту работу в отдельный метод:
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; } };
А поскольку расстояние необходимо применять только между дубликатами, а не слоем шаблона, мы добавили специальный флаг, shouldAdjustSpacing
, для которого мы можем установить значение true
или false
в зависимости от того, применяем ли мы параметры к слою шаблона или нет. Таким образом, мы можем гарантировать, что к шаблону будут применены вращение и непрозрачность, но не интервал.
Вернувшись к методу mosaic
, давайте теперь удостоверимся, что начальные параметры применяются к слою шаблона:
function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }
Далее нам нужно создать дубликаты. Во-первых, давайте создадим переменную, которую мы можем использовать для отслеживания параметров текущего дубликата:
function mosaic(options){ // ... var currentOptions; // ... }
Поскольку мы уже применили начальные параметры к слою шаблона, нам нужно взять те параметры, которые мы только что применили, и добавить относительные значения stepOptions
, чтобы получить параметры для применения к следующему слою. Так как мы будем делать это еще несколько раз в нашем цикле, мы также перенесем эту работу в определенный метод stepOptionsBy
:
function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };
После этого нам нужно написать цикл, который дублирует предыдущий слой, применяет к нему текущие параметры, затем смещает (или «шагает») текущие параметры, чтобы получить параметры для следующего дубликата:
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; } }
Все готово — мы успешно написали ядро того, что должен делать наш плагин! Теперь нам нужно подключить все так, чтобы когда пользователь фактически нажимал кнопку «Применить», вызывался наш код мозаики.
Вернемся к ui.js
и настроим наш код обработки сообщений. Что нам нужно сделать, так это проанализировать полученную строку параметров JSON, чтобы они превратились в объект, который мы действительно можем использовать. Получив эти параметры, мы можем вызвать с ними функцию mosaic
.
Во-первых, разбор. Нам нужно обновить нашу функцию обработки сообщений, чтобы проанализировать полученное сообщение JSON:
function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }
Далее нам нужно передать это нашей функции mosaic
. Однако на самом деле это не то, что должен делать наш код в ui.js
— предполагается, что он в первую очередь связан с тем, что необходимо для отображения на экране вещей, связанных с интерфейсом, а не с созданием самой мозаики. Чтобы разделить эти обязанности, мы добавим в createWebView
второй аргумент, который принимает функцию, и будем вызывать эту функцию всякий раз, когда получаем параметры из веб-интерфейса.
Назовем этот аргумент onApplyMessage
:
function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }
Нам также нужно изменить наш экспортированный метод loadAndShow
, чтобы он также принимал этот аргумент onApplyMessage
и передал его в createWebView
:
function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }
Наконец, перейдите к main.js
Теперь нам нужно импортировать нашу функцию mosaic
и вызывать ее с параметрами, которые мы получаем из пользовательского интерфейса плагина:
const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };
Мы почти закончили!
Однако, если мы сейчас запустим наш код и нажмем кнопку «Применить» в интерфейсе плагина, ничего не произойдет. Почему? Причина в том, как запускаются сценарии Sketch: по умолчанию они «живут» только до тех пор, пока не будет достигнута нижняя часть вашего сценария, после чего Sketch уничтожает его и освобождает все ресурсы, которые он использовал.
Это проблема для нас, поскольку это означает, что все, что нам нужно, чтобы происходило асинхронно (в данном случае это происходит после того, как будет достигнута нижняя часть нашего кода), например, получение сообщений, не может быть, потому что наш сценарий был уничтожен. Это означает, что мы не получим ни одного из наших сообщений из веб-интерфейса, поскольку нас нет рядом, чтобы получать и отвечать на них!
Есть способ сообщить Sketch, что нам нужно, чтобы наш скрипт оставался в живых после этого момента, используя Fibers
. Создавая файбер, мы сообщаем Sketch, что происходит что-то асинхронное и что он должен поддерживать наш скрипт. Затем Sketch удалит наш скрипт только в случае крайней необходимости (например, когда пользователь закрывает Sketch или когда необходимо обновить плагин Mosaic):
// ... 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); }); };
Вуаля! Давайте попробуем наш плагин прямо сейчас. Выбрав слой в Sketch, введите некоторые настройки, затем нажмите «Применить»:
Окончательные улучшения
Теперь, когда у нас реализована большая часть функций нашего плагина, мы можем попытаться немного «уменьшить масштаб» и взглянуть на общую картину.
Улучшение пользовательского опыта
Если вы играли с плагином в его текущем состоянии, вы могли заметить, что одна из самых больших проблем возникает, когда вы пытаетесь редактировать мозаику. После того, как вы создадите его, вы должны нажать «Отменить», настроить параметры, а затем нажать «Применить» (или нажать «Ввод»). Это также усложняет редактирование мозаики после того, как вы оставили свой документ и вернулись к нему позже, так как ваша история отмен/повторов будет стерта, и вам придется вручную удалять повторяющиеся слои самостоятельно.
В более идеальном случае пользователь мог бы просто выбрать группу Mosaic, настроить параметры и наблюдать за обновлением Mosaic, пока не получит точное расположение, которое он ищет. Для реализации этого нам необходимо решить две задачи:
- Во-первых, нам понадобится способ сгруппировать дубликаты, составляющие мозаику. Sketch предоставляет концепцию групп, которую мы можем использовать для решения этой проблемы.
- Во-вторых, нам понадобится способ отличить обычную группу, созданную пользователем, от группы Mosaic. API Sketch также дает нам возможность хранить информацию о любом заданном слое, которую мы можем использовать в качестве путевого тега, а затем идентифицировать группу как одну из наших «особых» групп мозаики.
Давайте вернемся к логике, которую мы написали в предыдущем разделе, чтобы решить эту проблему. Наш исходный код следует следующим шагам:
- Найдите текущий документ.
- Найдите выбранный слой текущего документа.
- Продублируйте выбранный слой (назовем его слоем- шаблоном ) x количество раз.
- Для каждого дубликата настройте его положение, поворот, непрозрачность и т. д. с помощью конкретных значений (количеств), установленных пользователем.
Чтобы сделать наш новый пользовательский поток возможным, нам нужно изменить эти шаги на:
- Возьмите текущий документ.
- Захватите выбранный слой текущего документа.
- Определите, является ли выбранный слой группой мозаики или нет.
- Если это какой-то другой слой, используйте его в качестве слоя-шаблона и перейдите к шагу 4.
- Если это группа мозаики, считайте первый слой в ней шаблонным и переходите к шагу 5.
- Оберните слой шаблона внутри группы и отметьте эту группу как группу мозаики.
- Удалите все слои внутри группы, кроме слоя шаблона.
- Дублируйте слой шаблона x количество раз.
- Для каждого дубликата настройте его положение, поворот, непрозрачность и т. д. с помощью определенных значений, установленных пользователем.
У нас есть три новых шага. Для первого нового шага, шага 3, мы создадим функцию с именем findOrMakeSpecialGroupIfNeeded
, которая будет смотреть на переданный ей слой, чтобы определить, является ли он группой 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; } };
Если нам не удалось найти группу мозаики, мы просто обернем полученный слой внутри Group
, а затем пометим его как группу мозаики.
Вернувшись к началу файла, нам также нужно вытащить класс 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; };
Теперь нам нужно заполнить пробелы (todo's). Для начала нам нужны средства для определения того, является ли группа одной из особых групп, принадлежащих нам, или нет. Здесь нам на помощь приходит модуль Settings
библиотеки Sketch. Мы можем использовать его для хранения пользовательской информации на определенном слое, а также для ее чтения.
Как только мы импортируем модуль вверху файла:
const Settings = require("sketch/settings");
Затем мы можем использовать два ключевых метода, которые он предоставляет, setLayerSettingForKey
и layerSettingForKey
, чтобы устанавливать и считывать данные со слоя:
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; };
Теперь, когда у нас есть метод, который обрабатывает включение слоя в группу мозаики (или, если это уже группа мозаики, просто возвращает его), мы можем подключить его к нашему основному методу mosaic
сразу после проверки безопасности:
function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }
Далее мы добавим цикл для удаления всех слоев из группы, кроме слоя шаблона (который является первым):
function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }
Наконец, мы убедимся, что размер группы соответствует ее новому содержимому, поскольку пользователь мог изначально выбрать слой, вложенный в старую группу (слой, который мы могли удалить).
Нам также нужно убедиться, что текущий выбор установлен на саму нашу группу мозаики. Это гарантирует, что если пользователь вносит несколько быстрых изменений в одну и ту же группу мозаики, она не будет отменена. После кода, который мы уже написали для дублирования слоя, добавьте:
function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }
Попробуйте плагин еще раз. Вы обнаружите, что редактирование мозаики стало намного более плавным!
Улучшение интерфейса
Еще одна вещь, которую вы можете заметить, это отсутствие синхронизации между окном дисплея и интерфейсом внутри него, с точки зрения того, что они оба становятся видимыми одновременно. Это связано с тем, что когда мы отображаем окно, не гарантируется, что загрузка веб-интерфейса завершится, поэтому иногда он «выскакивает» или «вспыхивает» после этого.
Один из способов исправить это — прослушивать завершение загрузки веб-интерфейса и только после этого показывать наше окно. Существует метод webView:didFinishNavigation:
, который WKWebView будет вызывать после завершения загрузки текущей страницы. Мы можем использовать его, чтобы получить именно то уведомление, которое мы ищем.
Вернувшись в ui.js
, мы расширим созданный нами экземпляр MochaJSDelegate
для реализации этого метода, который, в свою очередь, вызовет аргумент onLoadFinish
, который мы передадим в 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; };
Вернувшись к методу loadAndShow
, мы настроим его так, чтобы он отображал окно только после загрузки веб-представления:
function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };
Бинго! Теперь наше окно отображается только после завершения загрузки веб-представления, что позволяет избежать раздражающего визуального мерцания.
Заключение
Поздравляем, вы создали свой первый плагин для Sketch!
Если вы хотите установить Mosaic и поэкспериментировать с ним, вы можете загрузить полный плагин с GitHub. И прежде чем вы отправитесь, вот несколько ресурсов, которые могут пригодиться во время остальной части вашего путешествия:
- developer.sketchapp.com Официальный ресурс, посвященный разработке плагинов для Sketch. Содержит несколько полезных руководств, а также справочник API для библиотеки Sketch JavaScript.
- sketchplugins.com Фантастическое и полезное сообщество разработчиков плагинов для Sketch. Отлично подходит для получения ответов на все ваши животрепещущие вопросы.
- github.com/sketchplugins/plugin-directory Официальный центральный репозиторий плагинов Sketch на GitHub. Вы можете отправить свои плагины здесь и поделиться ими с остальным сообществом Sketch!