Cómo crear un complemento de boceto con JavaScript, HTML y CSS (Parte 2)

 

 

 

  • Implemente rápidamente. Implementar inteligentemente
  • Accesibilidad para diseñadores, con Stéphanie Walter

  • Índice
    1. Creación de la interfaz de usuario del complemento: hacer que nuestra interfaz web y el código del complemento Sketch “hablen” entre sí
    2. Generando los mosaicos de capas
      1. Improving The Interface
    3. Conclusion

    En esta segunda parte de nuestro tutorial sobre la creación de complementos de Sketch, continuaremos donde lo dejamos con la creación de nuestra interfaz de usuario y luego pasaremos a la característica clave de generar nuestros mosaicos de capas y optimizar el resultado final. código de complemento.

     

    Como se mencionó en la parte 1 , este tutorial está destinado a personas que conocen y utilizan la aplicación Sketch y que tampoco tienen miedo de incursionar con el código. Para aprovecharlo al máximo, necesitará tener al menos algo de experiencia básica escribiendo JavaScript (y, opcionalmente, HTML/CSS).

    En la parte anterior de este tutorial, aprendimos sobre los archivos básicos que componen un complemento y cómo crear la interfaz de usuario del complemento. En esta segunda y última parte, aprenderemos cómo conectar la interfaz de usuario al código principal del complemento y cómo implementar las características principales del complemento. Por último, pero no menos importante, también aprenderemos cómo optimizar el código y la forma en que funciona el complemento.

    Creación de la interfaz de usuario del complemento: hacer que nuestra interfaz web y el código del complemento Sketch “hablen” entre sí

    Lo siguiente que debemos hacer es configurar la comunicación entre nuestra interfaz web y el complemento Sketch.

    Necesitamos poder enviar un mensaje desde nuestra interfaz web al complemento Sketch cuando se hace clic en el botón "Aplicar" en nuestra interfaz web. Este mensaje debe informarnos qué configuraciones ingresó el usuario, como la cantidad de pasos, la cantidad de rotación, la cantidad de duplicados a crear, etc.

    WKWebViewnos facilita un poco esta tarea: podemos enviar mensajes a nuestro complemento Sketch desde el código JavaScript de nuestra interfaz web utilizando la window.webkit.messageHandlersAPI.

    Del lado de nuestro código Sketch, podemos usar otro método addScriptMessageHandler:name:(o addScriptMessageHandler_name) para registrar un controlador de mensajes que será llamado cada vez que reciba un mensaje enviado desde nuestra interfaz web del complemento.

    Comencemos asegurándonos de que podamos recibir mensajes desde nuestra interfaz de usuario web. Dirígete a la función ui.jsde nuestro archivo createWebViewy agrega lo siguiente:

     

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

    Aquí usamos la userContentControllerpropiedad de la vista web para agregar un controlador de mensajes al que llamamos "sketchPlugin". Este "controlador de contenido del usuario" es el puente que garantiza que los mensajes lleguen desde nuestra vista web.

    Es posible que hayas notado algo extraño en el código anterior: ¡el objeto que estamos agregando como controlador de mensajes ourMessageHandlerno existe todavía! Desafortunadamente, no podemos simplemente usar un objeto o función JavaScript normal como controlador, ya que este método espera un cierto tipo de objeto nativo.

    Por suerte para nosotros, podemos sortear esta limitación utilizando MochaJSDelegate, una minibiblioteca que escribí y que permite crear el tipo de objeto nativo que necesitamos utilizando JavaScript normal. Deberá descargarlo y guardarlo manualmente en su paquete de complementos en Sketch/MochaJSDelegate.js.

    Para usarlo, primero necesitaremos importarlo a ui.js. Agregue lo siguiente en la parte superior del archivo:

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

    Ahora podemos usar MochaJSDelegatepara crear el tipo de controlador de mensajes addScriptMessageHandler:name:que se espera:

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

    El código que acabamos de agregar crea el objeto nativo que necesitamos. También define un método en ese objeto llamado userContentController:didReceiveScriptMessage:; luego, este método se llama con el mensaje que queremos como segundo argumento. Dado que todavía no estamos enviando ningún mensaje, tendremos que volver aquí más adelante y agregar algo de código para analizar y manejar los mensajes que recibimos.

     

    A continuación, debemos agregar algo de código a nuestra interfaz web para enviarnos esos mensajes. Dirigirse a /Resources/web-ui/script.js. Descubrirá que ya escribí la mayor parte del código que maneja la recuperación de los valores del HTML en el que inputs /el usuario ingresará sus opciones.

    Lo que aún nos queda por hacer es agregar el código que realmente envía los valores a nuestro código Sketch:

    Busque la applyfunción y agregue lo siguiente al final:

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

    Aquí utilizamos window.webkit.messageHandlersla API que mencionamos anteriormente para acceder al controlador de mensajes que registramos anteriormente como sketchPlugin. Luego envíele un mensaje con una cadena JSON que contenga las entradas del usuario.

    Asegurémonos de que todo esté configurado correctamente. Regresar a /Sketch/ui.js. Para asegurarnos de recibir los mensajes como esperamos, modificaremos el método que definimos anteriormente para que muestre un cuadro de diálogo cuando recibamos un mensaje:

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

    Ahora ejecute el complemento (es posible que primero deba cerrar cualquier ventana de mosaico existente que haya abierto), ingrese algunos valores y luego haga clic en "Aplicar". Deberías ver una alerta como la que aparece a continuación: ¡esto significa que todo está conectado correctamente y nuestro mensaje se envió correctamente! De lo contrario, vuelva a los pasos anteriores y asegúrese de que todo se haya hecho como se describe.

    El cuadro de diálogo que debería ver aparecer una vez que haga clic en Aplicar. ( Vista previa grande )

    Ahora que podemos enviar mensajes desde nuestra interfaz a nuestro complemento, podemos pasar a escribir el código que realmente hace algo útil con esa información: generar nuestros mosaicos de capas.

    Generando los mosaicos de capas

    Hagamos un balance de lo que es necesario para que esto suceda. Simplificando un poco las cosas, lo que nuestro código debe hacer es:

    1. Encuentra el documento actual.
    2. Busque la capa seleccionada del documento actual.
    3. Duplica la capa seleccionada (la llamaremos capa de plantilla ) x número de veces.
    4. Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos (cantidades) establecidos por el usuario.

    Ahora que tenemos un plan razonable, sigamos escribiendo. Siguiendo con nuestro patrón de modularizar nuestro código, creemos un nuevo archivo, mosaic.jsen la Sketch/carpeta, y agreguemosle el siguiente código:

     

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

    Usaremos esta función como la única exportación de este módulo, ya que simplifica el uso de la API una vez que la importamos; podemos simplemente llamar mosaic()con cualquier opción que obtengamos de la interfaz web.

    Los primeros dos pasos que debemos seguir son obtener el documento actual y luego la capa seleccionada. La API de Sketch tiene una biblioteca incorporada para la manipulación de documentos a la que podemos acceder importando el sketch/dommódulo. Sólo necesitamos el Documentobjeto en este momento, así que lo sacaremos explícitamente. En la parte superior del archivo, agregue:

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

    El Documentobjeto tiene un método específico para acceder al documento actual que podemos usar, llamado getSelectedDocument(). Una vez que tenemos la instancia del documento actual, podemos acceder a las capas que el usuario haya seleccionado a través de la selectedLayerspropiedad del documento. Sin embargo, en nuestro caso, solo nos preocupamos por las selecciones de una sola capa, por lo que solo tomaremos la primera capa que el usuario haya seleccionado:

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

    Nota: Es posible que esperaba selectedLayersque fuera una matriz, pero no lo es. En cambio, es una instancia de la Selectionclase. Hay una razón para esto: la Selectionclase contiene un montón de métodos auxiliares útiles para manipular la selección como clear, map, reduce y forEach . Expone la matriz de capas real a través de la layerpropiedad.

    También agreguemos algunos comentarios de advertencia en caso de que el usuario olvide abrir un documento o seleccionar 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;

    Ahora que hemos escrito el código para los pasos 1 y 2 (buscar el documento actual y la capa seleccionada), debemos abordar los pasos 3 y 4:

    • Duplica la capa de la plantilla x número de veces.
    • Para cada duplicado, modifique su posición, rotación, opacidad, etc., según los valores específicos establecidos por el usuario.

    Comencemos por extraer toda la información relevante que necesitamos options: la cantidad de veces que se duplicará, las opciones de inicio y las opciones de pasos. Una vez más podemos usar la desestructuración (como hicimos antes con Document) para extraer esas propiedades de options:

     

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

    A continuación, desinfectemos nuestras entradas y asegurémonos de que el recuento de pasos sea siempre al menos 1:

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

    Ahora debemos asegurarnos de que la opacidad, rotación, etc. de la capa de la plantilla coincidan con los valores iniciales deseados por el usuario. Dado que aplicar las opciones del usuario a una capa será algo que haremos mucho, trasladaremos este trabajo a su propio 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; }};

    Y debido a que el espaciado solo debe aplicarse entre los duplicados y no entre la capa de plantilla, hemos agregado una marca específica, shouldAdjustSpacingque podemos configurar en trueo falsedependiendo de si estamos aplicando opciones a una capa de plantilla o no. De esa manera podemos asegurarnos de que se aplicarán rotación y opacidad a la plantilla, pero no espacio. Tes e infusiones

    De vuelta en el mosaicmétodo, asegurémonos de que las opciones iniciales se apliquen a la capa de plantilla:

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

    A continuación, necesitamos crear nuestros duplicados. Primero, creemos una variable que podamos usar para rastrear cuáles son las opciones para el duplicado actual:

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

    Como ya aplicamos las opciones iniciales a la capa de plantilla, debemos tomar esas opciones que acabamos de aplicar y agregar los valores relativos stepOptionspara obtener las opciones para aplicar a la siguiente capa. Dado que también haremos esto varias veces más en nuestro bucle, también trasladaremos este trabajo a un método específico stepOptionsBy:

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

    Después de eso, necesitamos escribir un bucle que duplique la capa anterior, le aplique las opciones actuales y luego desplace (o "escale") las opciones actuales para obtener las opciones para el siguiente duplicado:

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

    Todo listo: ¡hemos escrito con éxito el núcleo de lo que se supone que debe hacer nuestro complemento! Ahora, necesitamos conectar las cosas para que cuando el usuario haga clic en el botón "Aplicar", se invoque nuestro código de mosaico.

     

    Regresemos ui.jsy ajustemos nuestro código de manejo de mensajes. Lo que tendremos que hacer es analizar la cadena JSON de opciones que estamos obteniendo para que se conviertan en un objeto que realmente podamos usar. Una vez que tengamos estas opciones, podemos llamar a la mosaicfunción con ellas.

    Primero, analizando. Necesitaremos actualizar nuestra función de manejo de mensajes para analizar el mensaje JSON que recibimos:

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

    A continuación, necesitaremos pasar esto a nuestra mosaicfunción. Sin embargo, esto no es realmente algo que nuestro código ui.jsdeba hacer; se supone que se ocupa principalmente de lo que es necesario para mostrar elementos relacionados con la interfaz en la pantalla, no de crear mosaicos en sí. Para mantener estas responsabilidades separadas, agregaremos un segundo argumento que createWebViewrequiere una función y llamaremos a esa función cada vez que recibamos opciones de la interfaz web.

    Llamemos a este argumento onApplyMessage:

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

    También necesitaremos modificar nuestro método exportado, loadAndShowpara tomar este onApplyMessageargumento también y pasarlo a createWebView:

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

    Finalmente, dirígete a main.js. Ahora necesitamos importar nuestra mosaicfunción y llamarla con las opciones que recibimos de la interfaz de usuario del complemento:

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

    ¡Ya casi hemos terminado!

    Sin embargo, si ejecutamos nuestro código ahora y hacemos clic en el botón "Aplicar" en la interfaz del complemento, no sucederá nada. ¿Por qué? La razón se debe a cómo se ejecutan los scripts de Sketch: de forma predeterminada, "viven" sólo hasta que se llega al final del script, después de lo cual Sketch lo destruye y libera los recursos que estaba utilizando.

     

    Esto es un problema para nosotros ya que significa que cualquier cosa que necesitemos que suceda de forma asincrónica (en este caso, después de llegar al final de nuestro código), como recibir mensajes, no puede suceder porque nuestro script ha sido destruido. ¡Esto significa que no recibiríamos ninguno de nuestros mensajes desde la interfaz web ya que no estamos disponibles para recibirlos y responderlos!

    Hay una manera de indicarle a Sketch que necesitamos que nuestro script permanezca vivo más allá de este punto, usando Fibers. Al crear una Fibra, le decimos a Sketch que algo asincrónico está sucediendo y que necesita mantener nuestro script. Entonces, Sketch solo destruirá nuestro script cuando sea absolutamente necesario (como cuando el usuario cierra Sketch o cuando es necesario actualizar el complemento 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); });};

    ¡Voilá! Probemos nuestro complemento ahora. Con una capa seleccionada en Sketch, ingrese algunas configuraciones, luego haga clic en Aplicar:

    , then tag it as a Mosaic group.

    Back at the top of the file, we’ll need to pull out the Group class now too:

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

    Now we need to fill in the gaps (todo’s). To begin with, we need a means of identifying whether or not a group is one of the special groups belonging to us or not. Here, the Settings module of the Sketch library comes to our rescue. We can use it to store custom information on a particular layer, and also to read it back.

    Once we import the module at the top of the file:

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

    We can then use two key methods it provides, setLayerSettingForKey and layerSettingForKey, to set and read data off a layer:

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

    Now that we’ve got a method that handles wrapping a layer in a mosaic group (or, if already a mosaic group, just returns it) we can now plug it into our main mosaic method just after our safety checks:

     

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

    Next we’ll add a loop to remove all layers from the group except the template layer (which is the first):

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

    Lastly, we’ll make sure that the group’s size is fitted to its new contents since the user might have originally selected a layer nested within the old group (a layer that we might have removed).

    We’ll also need to make sure to set the current selection to our mosaic group itself. This will ensure that if the user is making a bunch of rapid changes to the same mosaic group it won’t become deselected. After the code we already wrote to duplicate a layer, add:

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

    Try out the plugin again. You should find that editing a mosaic is much smoother now!

    Improving The Interface

    One other thing you might notice is the lack of synchronization between the display window and the interface inside it, in terms of them both becoming visible at the same time. This is due to the fact that when we display the window, the web interface isn’t guaranteed to have finished loading, so sometimes it’ll “pop” or “flash in” afterwards.

    One way to fix this is by listening for when the web interface has finished loading, and only then show our window. There is a method, webView:didFinishNavigation:, that WKWebView will call when the current page has finished loading. We can use it to get exactly the notification we’re looking for.

    Back in ui.js, we’ll extend the MochaJSDelegate instance we created to implement this method, which will in turn call the onLoadFinish argument we’ll pass to 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;};

    And back in the loadAndShow method, we’ll adjust it so that it only shows the window once the web view has loaded:

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

    Bingo! Now our window displays only when the web view has finished loading, avoiding that annoying visual flicker.

    Conclusion

    Congratulations, you’ve built your first Sketch plugin!

    If you’d like to install and play around with Mosaic, you can download the complete plugin from GitHub. And before you go, here are a few resources that might be handy during the rest of your journey:

    • developer.sketchapp.comThe official resource regarding Sketch plugin development. Contains several useful guides, as well as an API reference for the Sketch JavaScript library.
    • sketchplugins.comA fantastic and helpful community of Sketch plugin developers. Great for getting all your burning questions answered.
    • github.com/sketchplugins/plugin-directoryOfficial, central GitHub repository of Sketch plugins. You can submit your plugins here and share them with the rest of the Sketch community!

    (mb, yk, il)Explore more on

    • Sketch
    • JavaScript
    • HTML
    • CSS





    Tal vez te puede interesar:

    1. ¿Deberían abrirse los enlaces en ventanas nuevas?
    2. 24 excelentes tutoriales de AJAX
    3. 70 técnicas nuevas y útiles de AJAX y JavaScript
    4. Más de 45 excelentes recursos y repositorios de fragmentos de código

    Cómo crear un complemento de boceto con JavaScript, HTML y CSS (Parte 2)

    Cómo crear un complemento de boceto con JavaScript, HTML y CSS (Parte 2)

    Implemente rápidamente. Implementar inteligentemente Accesibilidad para diseñadores, con Stéphanie Walter Índice

    programar

    es

    https://aprendeprogramando.es/static/images/programar-como-crear-un-complemento-de-boceto-con-javascript-990-0.jpg

    2024-05-20

     

    Cómo crear un complemento de boceto con JavaScript, HTML y CSS (Parte 2)
    Cómo crear un complemento de boceto con JavaScript, HTML y CSS (Parte 2)

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20