Como construir um plugin de esboço com JavaScript, HTML e CSS (Parte 2)
Publicados: 2022-03-10Conforme mencionado na parte 1, este tutorial é destinado a pessoas que conhecem e usam o aplicativo Sketch e não têm medo de mexer com código também. Para lucrar mais com isso, você precisará ter pelo menos alguma experiência básica escrevendo JavaScript (e, opcionalmente, HTML/CSS).
Na parte anterior deste tutorial, aprendemos sobre os arquivos básicos que compõem um plug-in e como criar a interface do usuário do plug-in. Nesta segunda e última parte, aprenderemos como conectar a interface do usuário ao código principal do plug-in e como implementar os principais recursos do plug-in. Por último, mas não menos importante, também aprenderemos como otimizar o código e a forma como o plugin funciona.
Construindo a interface de usuário do plug-in: fazendo com que nossa interface da Web e o código do plug-in do Sketch “falem” um com o outro
A próxima coisa que precisamos fazer é configurar a comunicação entre nossa interface web e o plugin Sketch.
Precisamos ser capazes de enviar uma mensagem de nossa interface web para o plugin Sketch quando o botão “Aplicar” em nossa interface web for clicado. Essa mensagem precisa nos informar sobre quais configurações o usuário inseriu, como o número de etapas, a quantidade de rotação, o número de duplicatas a serem criadas e assim por diante.
WKWebView
torna essa tarefa um pouco mais fácil para nós: podemos enviar mensagens para o nosso plugin Sketch a partir do código JavaScript da nossa interface web usando a API window.webkit.messageHandlers
.
Do lado do nosso código Sketch, podemos usar outro método, addScriptMessageHandler:name:
(ou addScriptMessageHandler_name
) para registrar um manipulador de mensagens que será chamado sempre que receber uma mensagem enviada da interface web do nosso plugin.
Vamos começar certificando-nos de que podemos receber mensagens da nossa interface do usuário da web. Vá até a função ui.js
do nosso arquivo createWebView
e adicione o seguinte:
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; };
Aqui usamos a propriedade userContentController
da visualização da web para adicionar um manipulador de mensagens que chamamos de “sketchPlugin”. Esse “controlador de conteúdo do usuário” é a ponte que garante que as mensagens passem pela nossa visualização da web.
Você deve ter notado algo estranho no código acima: o objeto que estamos adicionando como manipulador de mensagens, ourMessageHandler
, ainda não existe! Infelizmente, não podemos simplesmente usar um objeto ou função JavaScript regular como manipulador, pois esse método espera um certo tipo de objeto nativo.
Felizmente para nós, podemos contornar essa limitação usando MochaJSDelegate
, uma mini-biblioteca que escrevi que torna possível criar o tipo de objeto nativo que precisamos usando JavaScript comum. Você precisará fazer o download e salvá-lo manualmente em seu pacote de plugins em Sketch/MochaJSDelegate.js
.
Para usá-lo, primeiro precisamos importá-lo para ui.js
. Adicione o seguinte na parte superior do arquivo:
const MochaJSDelegate = require("./MochaJSDelegate");
Agora podemos usar o MochaJSDelegate
para criar o tipo de manipulador de mensagens que addScriptMessageHandler:name:
está esperando:
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; };
O código que acabamos de adicionar cria o objeto nativo que precisamos. Ele também define um método nesse objeto chamado userContentController:didReceiveScriptMessage:
— esse método é então chamado com a mensagem que queremos como segundo argumento. Como ainda não estamos enviando nenhuma mensagem, teremos que voltar aqui mais tarde e adicionar algum código para analisar e manipular as mensagens que recebemos.
Em seguida, precisamos adicionar algum código à nossa interface web para nos enviar essas mensagens. Vá para /Resources/web-ui/script.js
. Você verá que já escrevi a maior parte do código que trata da recuperação dos valores do HTML <inputs />
no qual o usuário inserirá suas opções.
O que ainda nos resta fazer é adicionar o código que realmente envia os valores para o nosso código Sketch:
Encontre a função apply
e adicione o seguinte ao final dela:
// Send user inputs to sketch plugin window.webkit.messageHandlers.sketchPlugin.postMessage(JSON.stringify({ stepCount, startingOptions, stepOptions }));
Aqui usamos a API window.webkit.messageHandlers
que mencionamos anteriormente para acessar o manipulador de mensagens que registramos acima como sketchPlugin
. Em seguida, envie uma mensagem para ele com uma string JSON contendo as entradas do usuário.
Vamos garantir que tudo esteja configurado corretamente. Volte para /Sketch/ui.js
. Para garantir que estamos recebendo as mensagens como esperamos, modificaremos o método que definimos anteriormente para que ele exiba uma caixa de diálogo quando recebermos uma mensagem:
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" ); // ... };
Agora execute o plugin (você pode precisar primeiro fechar qualquer janela do Mosaic existente que você abriu), digite alguns valores e clique em “Aplicar”. Você deve ver um alerta como o abaixo - isso significa que tudo está conectado corretamente e nossa mensagem foi enviada com sucesso! Caso contrário, volte aos passos anteriores e certifique-se de que tudo foi feito conforme descrito.
Agora que podemos enviar mensagens de nossa interface para nosso plugin, podemos passar a escrever o código que realmente faz algo útil com essa informação: gerar nossos mosaicos de camadas.
Gerando os mosaicos de camadas
Vamos fazer um balanço do que é necessário para que isso aconteça. Simplificando um pouco as coisas, o que nosso código precisa fazer é:
- Encontre o documento atual.
- Encontre a camada selecionada do documento atual.
- Duplique a camada selecionada (vamos chamá-la de camada de modelo ) x número de vezes.
- Para cada duplicata, ajuste sua posição, rotação, opacidade, etc., pelos valores específicos (quantidades) definidos pelo usuário.
Agora que temos um plano razoável, vamos continuar escrevendo. Mantendo nosso padrão de modularização de nosso código, vamos criar um novo arquivo, mosaic.js
na pasta Sketch/
, e adicionar a ele o seguinte código:
function mosaic(options){ }; module.export = mosaic;
Usaremos esta função como a única exportação deste módulo, já que ela torna a API mais simples de usar assim que a importamos — podemos simplesmente chamar mosaic()
com quaisquer opções que obtivermos da interface web.
As duas primeiras etapas que precisamos seguir são obter o documento atual e, em seguida, sua camada selecionada. A API do Sketch possui uma biblioteca interna para manipulação de documentos que podemos acessar importando o módulo sketch/dom
. Nós só precisamos do objeto Document
agora, então vamos retirá-lo explicitamente. No topo do arquivo, adicione:
const { Document } = require("sketch/dom");
O objeto Document
tem um método específico para acessar o documento atual que podemos usar, chamado getSelectedDocument()
. Uma vez que temos a instância do documento atual, podemos acessar quaisquer camadas que o usuário tenha selecionado através da propriedade selectedLayers
do documento. No nosso caso, porém, nos importamos apenas com seleções de camada única, então pegaremos apenas a primeira camada que o usuário selecionou:
function mosaic(options){ const document = Document.getSelectedDocument(); const selectedLayer = document.selectedLayers.layers[0]; }; module.export = mosaic;
Nota: Você pode estar esperando que selectedLayers
seja um array, mas não é. Em vez disso, é uma instância da classe Selection
. Há uma razão para isso: a classe Selection
contém vários métodos auxiliares úteis para manipular a seleção como clear, map, reduce e forEach. Ele expõe a matriz de camada real por meio da propriedade de layer
.
Vamos também adicionar alguns comentários de aviso caso o usuário esqueça de abrir um documento ou selecionar algo:
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;
Agora que escrevemos o código para as etapas 1 e 2 (encontrar o documento atual e a camada selecionada), precisamos abordar as etapas 3 e 4:
- Duplique a camada do modelo x número de vezes.
- Para cada duplicata, ajuste sua posição, rotação, opacidade, etc., pelos valores específicos definidos pelo usuário.
Vamos começar extraindo todas as informações relevantes que precisamos das options
: o número de vezes para duplicar, opções iniciais e opções de etapas. Podemos mais uma vez usar a desestruturação (como fizemos anteriormente com Document
) para extrair essas propriedades das options
:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; }
Em seguida, vamos higienizar nossas entradas e garantir que a contagem de etapas seja sempre pelo menos 1:
function mosaic(options) { // ... // Destructure options: var { stepCount, startingOptions, stepOptions } = options; stepCount = Math.max(1, stepCount); }
Agora precisamos ter certeza de que a opacidade da camada do modelo, rotação, etc., correspondem aos valores iniciais desejados pelo usuário. Como aplicar as opções do usuário a uma camada será algo que faremos muito, moveremos este trabalho para seu próprio método:
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 como o espaçamento só precisa ser aplicado entre as duplicatas e não a camada de modelo, adicionamos um sinalizador específico, shouldAdjustSpacing
, que podemos definir como true
ou false
dependendo se estamos aplicando opções a uma camada de modelo ou não. Dessa forma, podemos garantir que a rotação e a opacidade sejam aplicadas ao modelo, mas não o espaçamento.
De volta ao método mosaic
, vamos agora garantir que as opções iniciais sejam aplicadas à camada do modelo:
function mosaic(options){ // ... // Configure template layer var layer = group.layers[0]; configureLayer(layer, startingOptions, false); }
Em seguida, precisamos criar nossas duplicatas. Primeiro, vamos criar uma variável que podemos usar para rastrear quais são as opções para a duplicata atual:
function mosaic(options){ // ... var currentOptions; // ... }
Como já aplicamos as opções iniciais à camada de modelo, precisamos pegar as opções que acabamos de aplicar e adicionar os valores relativos de stepOptions
para obter as opções a serem aplicadas à próxima camada. Como também faremos isso várias vezes em nosso loop, também moveremos esse trabalho para um método específico, stepOptionsBy
:
function stepOptionsBy(start, step){ const newOptions = {}; for(let key in start){ newOptions[key] = start[key] + step[key]; } return newOptions; };
Depois disso, precisamos escrever um loop que duplique a camada anterior, aplique as opções atuais a ela e, em seguida, desloque (ou “passos”) as opções atuais para obter as opções para a próxima duplicata:
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; } }
Tudo pronto — escrevemos com sucesso o núcleo do que nosso plugin deve fazer! Agora, precisamos conectar as coisas para que, quando o usuário realmente clicar no botão “Aplicar”, nosso código de mosaico seja invocado.
Vamos voltar para ui.js
e ajustar nosso código de manipulação de mensagens. O que precisamos fazer é analisar a string JSON de opções que estamos obtendo para que sejam transformadas em um objeto que possamos realmente usar. Assim que tivermos essas opções, podemos chamar a função mosaic
com elas.
Primeiro, analisando. Precisaremos atualizar nossa função de manipulação de mensagens para analisar a mensagem JSON que recebemos:
function createWebView(pageURL){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); } }); }
Em seguida, precisaremos passar isso para nossa função de mosaic
. No entanto, isso não é realmente algo que nosso código em ui.js
deveria estar fazendo - deveria estar principalmente preocupado com o que é necessário para exibir coisas relacionadas à interface na tela - não criando mosaicos em si. Para manter essas responsabilidades separadas, adicionaremos um segundo argumento a createWebView
que recebe uma função e chamaremos essa função sempre que recebermos opções da interface da web.
Vamos nomear este argumento onApplyMessage
:
function createWebView(pageURL, onApplyMessage){ // ... const scriptMessageHandler = new MochaJSDelegate({ "userContentController:didReceiveScriptMessage:": (_, wkMessage) => { const message = JSON.parse(wkMessage.body()); onApplyMessage(message); } }); }
Também precisaremos modificar nosso método exportado, loadAndShow
, para receber este argumento onApplyMessage
e passá-lo para createWebView
:
function loadAndShow(baseURL, onApplyMessage){ // ... const webView = createWebView(pageURL, onApplyMessage); }
Por fim, vá para main.js
. Agora precisamos importar nossa função de mosaic
e chamá-la com as opções que recebemos da interface de usuário do plugin:
const mosaic = require("./mosaic"); function onRun(context){ UI.loadAndShow(context.scriptURL, options => { mosaic(options); }); };
Estamos quase terminando!
No entanto, se executarmos nosso código agora e clicarmos no botão “Aplicar” na interface do plugin, nada aconteceria. Por quê? A razão é devido à forma como os scripts do Sketch são executados: por padrão, eles “vivem” apenas até que o final do script seja alcançado, após o que o Sketch o destrói e libera quaisquer recursos que estava usando.
Isso é um problema para nós, pois significa que qualquer coisa que precisemos acontecer de forma assíncrona (neste caso, isso é depois que o fundo do nosso código for atingido), como receber mensagens, não pode, porque nosso script foi destruído. Isso significa que não receberíamos nenhuma de nossas mensagens da interface da web, pois não estamos por perto para recebê-las e respondê-las!
Existe uma maneira de sinalizar ao Sketch que precisamos que nosso script permaneça ativo além desse ponto, usando Fibers
. Ao criar um Fiber, informamos ao Sketch que algo assíncrono está acontecendo e que ele precisa manter nosso script por perto. O Sketch só destruirá nosso script quando for absolutamente necessário (como o usuário fechando o Sketch, ou quando o plugin Mosaic precisa ser atualizado):
// ... 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); }); };
Voilá! Vamos experimentar nosso plugin agora. Com uma camada selecionada no Sketch, insira algumas configurações e clique em aplicar:
Melhorias finais
Agora que implementamos a maioria das funcionalidades do nosso plugin, podemos tentar “reduzir” um pouco e dar uma olhada no quadro geral.
Melhorando a experiência do usuário
Se você já brincou com o plugin em seu estado atual, deve ter notado que um dos maiores pontos de atrito aparece quando você tenta editar um Mosaic. Depois de criar um, você deve clicar em desfazer, ajustar as opções e clicar em 'Aplicar' (ou pressionar Enter). Também torna mais difícil editar um Mosaico depois que você deixa seu documento e retorna a ele mais tarde, já que seu histórico de desfazer/refazer será apagado, deixando você mesmo excluir manualmente as camadas duplicadas.
Em um fluxo mais ideal, o usuário poderia simplesmente selecionar um grupo do Mosaic, ajustar as opções e assistir à atualização do Mosaic até obter o arranjo exato que procurava. Para implementar isso, temos dois problemas para resolver:
- Primeiro, precisaremos de uma maneira de agrupar as duplicatas que compõem um Mosaico. O Sketch fornece o conceito de Grupos, que podemos usar para resolver esse problema.
- Em segundo lugar, precisaremos de uma maneira de diferenciar um grupo normal criado pelo usuário e um grupo Mosaic. A API do Sketch também nos dá uma maneira de armazenar informações em qualquer camada, que podemos usar como uma etiqueta de caminho e depois identificar um grupo como um de nossos grupos Mosaic 'especiais'.
Vamos revisitar a lógica que escrevemos na seção anterior para resolver isso. Nosso código original segue os seguintes passos:
- Encontre o documento atual.
- Encontre a camada selecionada do documento atual.
- Duplique a camada selecionada (vamos chamá-la de camada de modelo ) x número de vezes.
- Para cada duplicata, ajuste sua posição, rotação, opacidade, etc., pelos valores específicos (quantidades) definidos pelo usuário.
Para possibilitar nosso novo fluxo de usuários, precisamos alterar estas etapas para:
- Pegue o documento atual.
- Pegue a camada selecionada do documento atual.
- Determine se a camada selecionada é um grupo Mosaico ou não.
- Se for alguma outra camada, use-a como camada de modelo e vá para a etapa 4.
- Se for um grupo Mosaic, considere a primeira camada como a camada de modelo e vá para a etapa 5.
- Enrole a camada do modelo dentro de um grupo e marque esse grupo como um grupo Mosaic.
- Remova todas as camadas de dentro do grupo, exceto a camada do modelo.
- Duplique a camada do modelo x número de vezes.
- Para cada duplicata, ajuste sua posição, rotação, opacidade, etc., pelos valores específicos definidos pelo usuário.
Temos três novos passos. Para a primeira nova etapa, a etapa 3, criaremos uma função chamada findOrMakeSpecialGroupIfNeeded
que examinará a camada passada para ela para determinar se é ou não um grupo Mosaic. Se for, vamos apenas devolvê-lo. Como o usuário pode selecionar uma subcamada aninhada profundamente em um grupo Mosaic, também precisaremos verificar os pais da camada selecionada para saber se eles também são um dos nossos grupos 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 não formos capazes de encontrar um grupo Mosaic, vamos simplesmente envolver a camada que nos foi passada dentro de um Group
, e então marcá-la como um grupo Mosaic.
De volta ao topo do arquivo, precisaremos retirar a classe Group agora também:
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; };
Agora precisamos preencher as lacunas (todos). Para começar, precisamos de um meio de identificar se um grupo é ou não um dos grupos especiais que nos pertencem ou não. Aqui, o módulo Settings
da biblioteca Sketch vem em nosso socorro. Podemos usá-lo para armazenar informações personalizadas em uma camada específica e também para lê-las de volta.
Depois de importar o módulo no topo do arquivo:
const Settings = require("sketch/settings");
Podemos então usar dois métodos principais que ele fornece, setLayerSettingForKey
e layerSettingForKey
, para definir e ler dados de uma camada:
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; };
Agora que temos um método que lida com o envolvimento de uma camada em um grupo de mosaico (ou, se já for um grupo de mosaico, apenas o retorna), agora podemos conectá-lo ao nosso método de mosaic
principal logo após nossas verificações de segurança:
function mosaic(options){ // ... safety checks ... // Group selection if needed: const group = findOrMakeSpecialGroupIfNeeded(selectedLayer); }
Em seguida, adicionaremos um loop para remover todas as camadas do grupo, exceto a camada de modelo (que é a primeira):
function mosaic(options) { // ... // Remove all layers except the first: while(group.layers.length > 1){ group.layers[group.layers.length - 1].remove(); } }
Por fim, garantiremos que o tamanho do grupo esteja ajustado ao seu novo conteúdo, pois o usuário pode ter selecionado originalmente uma camada aninhada no grupo antigo (uma camada que podemos ter removido).
Também precisamos ter certeza de definir a seleção atual para o nosso próprio grupo de mosaico. Isso garantirá que, se o usuário estiver fazendo várias alterações rápidas no mesmo grupo de mosaico, ele não será desmarcado. Após o código que já escrevemos para duplicar uma camada, adicione:
function mosaic(options) { // ... // Fit group to duplicates group.adjustToFit(); // Set selection to the group document.selectedLayers.clear(); group.selected = true; }
Experimente o plug-in novamente. Você deve achar que editar um mosaico é muito mais fácil agora!
Melhorando a interface
Uma outra coisa que você pode notar é a falta de sincronização entre a janela de exibição e a interface dentro dela, em termos de ambas ficarem visíveis ao mesmo tempo. Isso se deve ao fato de que, quando exibimos a janela, não é garantido que a interface da web tenha terminado de carregar, então às vezes ela “pop” ou “flash in” depois.
Uma maneira de corrigir isso é ouvir quando a interface da Web terminar de carregar e só então mostrar nossa janela. Existe um método, webView:didFinishNavigation:
, que o WKWebView chamará quando a página atual terminar de carregar. Podemos usá-lo para obter exatamente a notificação que estamos procurando.
De volta ao ui.js
, estenderemos a instância MochaJSDelegate
que criamos para implementar esse método, que por sua vez chamará o argumento onLoadFinish
que passaremos para 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 de volta ao método loadAndShow
, vamos ajustá-lo para que ele só mostre a janela depois que a visualização da web for carregada:
function loadAndShow(baseURL, onApplyMessage){ // ... const window = createWindow(); const webView = createWebView(pageURL, onApplyMessage, () => { showWindow(window); }); window.contentView = webView; _window = window; };
Bingo! Agora nossa janela é exibida apenas quando a visualização da web termina de carregar, evitando aquela cintilação visual irritante.
Conclusão
Parabéns, você construiu seu primeiro plugin do Sketch!
Se você quiser instalar e brincar com o Mosaic, você pode baixar o plugin completo do GitHub. E antes de ir, aqui estão alguns recursos que podem ser úteis durante o resto de sua jornada:
- developer.sketchapp.com O recurso oficial sobre o desenvolvimento de plugins do Sketch. Contém vários guias úteis, bem como uma referência de API para a biblioteca Sketch JavaScript.
- sketchplugins.com Uma comunidade fantástica e útil de desenvolvedores de plugins do Sketch. Ótimo para obter todas as suas perguntas ardentes respondidas.
- github.com/sketchplugins/plugin-directory Repositório oficial e central do GitHub de plugins do Sketch. Você pode enviar seus plugins aqui e compartilhá-los com o resto da comunidade Sketch!