Generando √≠conos multiplataforma de m√ļltiples marcas con Sketch y un script Node.js - Parte # 2



Esta es la segunda parte de una publicación sobre la creación de una tubería que puede tomar un archivo de boceto y exportar todos los íconos incluidos en el archivo, en diferentes formatos, para diferentes plataformas, con la posibilidad de que AB pruebe cada ícono.

Puedes leer la primera parte de la publicación aquí .



Los archivos de Sketch, con todos los íconos recopilados, con estilo y con el nombre apropiado, estaban listos. Ahora era el momento de comenzar a escribir el código.

Basta decir que el proceso fue en gran medida una prueba y error: despu√©s del importante n√ļcleo de c√≥digo inicial, desarrollado por el l√≠der de mi equipo Nikhil Verma (quien estableci√≥ los fundamentos del gui√≥n), pas√© por un proceso incremental que requiri√≥ al menos tres fases de refactorizaci√≥n y bastantes revisiones. Por esta raz√≥n, no entrar√© en demasiados detalles sobre c√≥mo se desarroll√≥ el script, sino que me centrar√© en c√≥mo funciona el script hoy, en su forma final.

El script de compilación


El script de compilación, escrito en Node.js, es relativamente sencillo en su flujo: una vez importadas las dependencias, declaró la lista de archivos de Sketch para procesar (como una lista de marcas y para cada marca una lista de archivos para esa marca) y comprobó que Sketch está instalado en el cliente, el script se repite en el conjunto de marcas y para cada una de ellas ejecuta estos pasos en secuencia:

  1. Obtenga los tokens de dise√Īo para la marca (necesitamos los valores de color)
  2. Clone los archivos Sketch asociados con la marca, descomprímalos para exponer los archivos JSON internos y manipule algunos de los valores internos de estos archivos JSON (más sobre esto más adelante)
  3. Lea los metadatos relevantes de los archivos de Sketch JSON ( document.json , meta.json y pages / pageUniqueID.json ); en particular, necesitamos la lista de estilos compartidos y la lista de activos / iconos contenidos en los archivos
  4. Después de algunas manipulaciones adicionales de los archivos Sketch JSON, vuelva a comprimirlos y, utilizando los archivos Sketch (clonados y actualizados), exporte y genere los archivos de salida finales para las tres plataformas (iOS, Android, Mobile Web)

Puede ver las partes relevantes del script de compilación principal aquí:

// ... modules imports here const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], }; const SKETCH_FOLDER_PATH = path.resolve(__dirname, '../src/'); const SKETCH_TEMP_PATH = path.resolve(SKETCH_FOLDER_PATH, 'tmp'); const DESTINATION_PATH = path.resolve(__dirname, '../dist'); console.log('Build started...'); if (sketchtool.check()) { console.log(`Processing Sketch file via ${sketchtool.version()}`); build(); } else { console.info('You need Sketch installed to run this script'); process.exit(1); } // ---------------------------------------- function build() { // be sure to start with a blank slate del.sync([SKETCH_TEMP_PATH, DESTINATION_PATH]); // process all the brands declared in the list of Sketch files Object.keys(SKETCH_FILES).forEach(async (brand) => { // get the design tokens for the brand const brandTokens = getDesignTokens(brand); // prepare the Sketch files (unzipped) and get a list of them const sketchUnzipFolders = await prepareSketchFiles({ brand, sketchFileNames: SKETCH_FILES[brand], sketchFolder: SKETCH_FOLDER_PATH, sketchTempFolder: SKETCH_TEMP_PATH }); // get the Sketch metadata const sketchMetadata = getSketchMetadata(sketchUnzipFolders); const sketchDataSharedStyles = sketchMetadata.sharedStyles; const sketchDataAssets = sketchMetadata.assetsMetadata; generateAssetsPDF({ platform: 'ios', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsSVGDynamicMobileWeb({ platform: 'mw', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); generateAssetsVectorDrawableDynamicAndroid({ platform: 'android', brand, brandTokens, sketchDataSharedStyles, sketchDataAssets }); }); } 

En realidad, todo el código de canalización es mucho más complejo que esto, y la complejidad radica en las funciones prepareSketchFiles , getSketchMetadata y generateAssets [formato] [plataforma] . Trataré de explicarlos con más detalle a continuación.

Preparando los archivos de croquis


El primer paso en el proceso de compilación es la preparación de los archivos de Sketch, de modo que puedan usarse más tarde para la exportación de los activos para las diferentes plataformas.

Los archivos asociados con la marca, por ejemplo, para Blendr , los archivos icons_common.sketch y icons_blendr.sketch , se clonan inicialmente en una carpeta temporal (m√°s precisamente, en una subcarpeta con el nombre de la marca que se est√° procesando) y se descomprimen.

Luego, se procesan los archivos JSON internos, a un prefijo agregado a los activos que se someter√°n a pruebas AB, de modo que cuando se exporten, se guardar√°n en una subcarpeta con un nombre predefinido (el nombre √ļnico del experimento). Para comprender qu√© activos se deben probar, simplemente verificamos si el nombre de la p√°gina en la que est√°n almacenados en Sketch tiene el prefijo "XP_" .


Una comparación de los nombres de las capas, dentro de los archivos de Sketch, antes y después de la actualización.

En el ejemplo anterior, cuando se exportan los activos se guardar√°n en la subcarpeta "this__is_an_experiment" , con un nombre de archivo "icon-name [variant-name] .ext" .

Leer los metadatos del boceto


El segundo paso importante en el proceso es sacar todos los metadatos relevantes de los archivos de Sketch, en particular de sus archivos JSON internos. Como se explicó anteriormente, estos archivos son los dos archivos principales ( document.json y meta.json ) y los archivos de páginas ( pages / pageUniqueId.json ).

El archivo document.json se usa para obtener la lista de los estilos compartidos, que aparecen debajo de la propiedad de objeto layerStyles :

 { "_class": "document", "do_objectID": "45D2DA82-B3F4-49D1-A886-9530678D71DC", "colorSpace": 1, ... "layerStyles": { "_class": "sharedStyleContainer", "objects": [ { "_class": "sharedStyle", "do_objectID": "9BC39AAD-CDE6-4698-8EA5-689C3C942DB4", "name": "features/feature-like", "value": { "_class": "style", "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.10588235408067703, "green": 0.4000000059604645, "red": 1 }, "fillType": 0, "noiseIndex": 0, "noiseIntensity": 0, "patternFillType": 1, "patternTileScale": 1 } ], "blur": {...}, "startMarkerType": 0, "endMarkerType": 0, "miterLimit": 10, "windingRule": 1 } }, ... 

Para cada estilo, almacenamos informaci√≥n b√°sica en un objeto clave-valor. Esto se usar√° m√°s adelante cuando necesitemos recuperar el nombre de un estilo basado en su ID √ļnica (en Sketch, la propiedad do_objectID ):

 const parsedSharedStyles = {}; parsedDocument.layerStyles.objects.forEach((object) => { parsedSharedStyles[object.do_objectID] = { name: object.name, isFill: _.get(object, 'value.fills[0].color') !== undefined, isBorder: _.get(object, 'value.borders[0].color') !== undefined, }; }); 


En este punto, nos movemos en el archivo meta.json para obtener la lista de p√°ginas, en particular necesitamos su id y nombre √ļnicos :

 { "commit": "623a23f2c4848acdbb1a38c2689e571eb73eb823", "pagesAndArtboards": { "EE6BE8D9-9FAD-4976-B0D8-AB33D2B5DBB7": { "name": "Icons", "artboards": { "3275987C-CE1B-4369-B789-06366EDA4C98": { "name": "badge-feature-like" }, "C6992142-8439-45E7-A346-FC35FA01440F": { "name": "badge-feature-crush" }, ... "7F58A1C4-D624-40E3-A8C6-6AF15FD0C32D": { "name": "tabbar-livestream" } ... } }, "ACF82F4E-4B92-4BE1-A31C-DDEB2E54D761": { "name": "XP_this__is_an_experiment", "artboards": { "31A812E8-D960-499F-A10F-C2006DDAEB65": { "name": "this__is_an_experiment/tabbar-livestream[variant1]" }, "20F03053-ED77-486B-9770-32E6BA73A0B8": { "name": "this__is_an_experiment/tabbar-livestream[variant2]" }, "801E65A4-3CC6-411B-B097-B1DBD33EC6CC": { "name": "this__is_an_experiment/tabbar-livestream[control]" } } }, 

Luego, para cada página leemos el archivo JSON correspondiente debajo de la carpeta de páginas (como ya se dijo, el nombre de archivo es [pageUniqueId] .json ), y revisamos los activos contenidos en esa página (aparecen como capas). De esta manera, para cada icono obtenemos su nombre, su ancho / alto, los metadatos de Sketch para ese icono de capa, y si está en una página de experimento, el nombre de la prueba AB en cuestión y el nombre de la variante para ese ícono.

Aviso : el objeto "page.json" es muy complejo, por lo que no voy a entrar aqu√≠. Si tiene curiosidad y quiere ver c√≥mo se ve, le sugiero que cree un nuevo archivo Sketch en blanco, agregue alg√ļn contenido y gu√°rdelo; luego cambie el nombre de su extensi√≥n en zip, descompr√≠malo y busque uno de los archivos que aparecen en la carpeta "p√°ginas".

Mientras procesamos las mesas de trabajo, también creamos una lista de experimentos (con sus activos correspondientes) que se utilizarán más adelante para determinar qué opciones de icono se usan y para qué experimento, asociando el nombre de las opciones de icono al objeto "base de icono".

Para cada archivo de boceto que se procesa asociado con la marca, producimos un objeto de metadatos de activos que se ve así:

 { "navigation-bar-edit": { "do_objectID": "86321895-37CE-4B3B-9AA6-6838BEDB0977", ...sketch_artboard_properties, "name": "navigation-bar-edit", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 "layers": [ { "do_objectID": "A15FA03C-DEA6-4732-9F85-CA0412A57DF4", "name": "Path", ...sketch_layer_properties, "sharedStyleID": "6A3C0FEE-C8A3-4629-AC48-4FC6005796F5", "style": { ... "fills": [ { "_class": "fill", "isEnabled": true, "color": { "_class": "color", "alpha": 1, "blue": 0.8784313725490196, "green": 0.8784313725490196, "red": 0.8784313725490196 }, } ], "miterLimit": 10, "startMarkerType": 0, "windingRule": 1 }, }, ], ... }, "experiment-name/navigation-bar-edit[variant]": { "do_objectID": "00C0A829-D8ED-4E62-8346-E7EFBC04A7C7", ...sketch_artboard_properties, "name": "experiment-name/navigation-bar-edit[variant]", "assetname": "navigation-bar-edit", "source": "icons_common", "width": 48, "height": 48 ... 

Como puede ver, el mismo "icono" (en este caso , barra de navegaci√≥n-edici√≥n ) puede tener m√ļltiples "activos" asociados, en t√©rminos de experimentos. Pero el mismo √≠cono puede aparecer con el mismo nombre en un segundo archivo de boceto asociado con la marca, y esto es muy √ļtil: es el truco que hemos usado para compilar un conjunto com√ļn de √≠conos y luego definir diferentes variantes de √≠conos espec√≠ficos dependiendo de marca

Es por eso que declaramos los archivos de Sketch asociados con cada marca en particular como una matriz:

 const SKETCH_FILES = { badoo: ['icons_common'], blendr: ['icons_common', 'icons_blendr'], fiesta: ['icons_common', 'icons_fiesta'], hotornot: ['icons_common', 'icons_hotornot'], }; 

Porque en este caso el orden importa. Y, de hecho, en la funci√≥n getSketchMetadata , llamada por el script de compilaci√≥n, no devolvemos los objetos assetsMetadata (uno por archivo) como una lista, sino que hacemos una fusi√≥n profunda de cada objeto, uno en el otro, y luego devuelve un √ļnico objeto activoMetadata combinado.

Esto no es más que la fusión "lógica" de los archivos de Sketch, y sus activos, en un solo archivo. Pero la lógica no es tan simple como parece. Aquí está el esquema que tuvimos que crear para descubrir qué sucede cuando hay íconos con el mismo nombre (posiblemente bajo pruebas AB) en diferentes archivos asociados con la misma marca:


El esquema l√≥gico de c√≥mo funciona la "anulaci√≥n" del mismo √≠cono, entre un conjunto com√ļn / compartido de √≠conos e √≠conos dise√Īados espec√≠ficamente para etiquetas blancas (tambi√©n considerando el caso de las pruebas AB)

Generando los archivos finales en diferentes formatos para diferentes plataformas


El √ļltimo paso del proceso es la generaci√≥n real de los archivos de iconos con diferentes formatos para las diferentes plataformas (PDF para iOS, SVG / JSX para Web y VectorDrawable para Android).

Como puede ver en la cantidad de par√°metros pasados ‚Äč‚Äča las funciones generateAssets [formato] [plataforma] esta es la parte m√°s compleja de la tuber√≠a. Aqu√≠ es donde el proceso comienza a dividirse y divergir para las diferentes plataformas. Vea a continuaci√≥n el flujo l√≥gico completo del script y c√≥mo la parte relacionada con la generaci√≥n de los activos se divide en tres flujos similares pero no id√©nticos:



Para generar los activos finales con los colores correctos asociados con la marca que se est√° procesando, necesitamos hacer otro conjunto de manipulaciones en los archivos Sketch JSON: iteramos iterativamente sobre cada capa que tiene un estilo compartido aplicado, y reemplazamos el valores de color con los colores de las fichas de dise√Īo de la marca.

Para la generación de Android, se requiere una manipulación adicional (más sobre esto más adelante): cambiamos la propiedad de regla de relleno de cada capa de par a impar a distinto de cero (esto está controlado por la propiedad "windingRule" en el objeto JSON, donde " 1 "significa" par-impar "y" 0 "significa" distinto de cero ").

Una vez completadas estas manipulaciones, comprimimos los archivos de Sketch JSON nuevamente en un archivo de Sketch est√°ndar, de modo que pueda procesarse para exportar los activos con las propiedades actualizadas (los archivos clonados y actualizados son archivos de Sketch absolutamente normales: se pueden abrir en Sketch , visto, editado, guardado, etc.).

En este punto, podemos usar sketchtool ( en un contenedor de nodo ) para exportar automáticamente todos los activos en formatos específicos para plataformas específicas. Para cada archivo asociado con una marca (más correctamente, su versión clonada y actualizada) ejecutamos este comando:

 sketchtool.run(`export slices ${cloneSketchFile} --formats=svg <i>--scales=1 </i>--output=${destinationFolder} --overwriting`); 

Como puede suponer, este comando exporta los activos en un formato espec√≠fico, aplicando una escala opcional (por ahora siempre mantenemos la escala original), en una carpeta de destino. La opci√≥n de sobrescritura es clave aqu√≠: de la misma manera que hacemos una "fusi√≥n profunda" de los objetos de metadatos de activos (que equivale a una "fusi√≥n l√≥gica" de los archivos de Sketch), cuando exportamos lo hacemos desde m√ļltiples archivos a la misma carpeta (√ļnica por marca / plataforma). Esto significa que si un activo, identificado por su nombre de capa, ya exist√≠a en un archivo Sketch anterior, se sobrescribir√° en la siguiente exportaci√≥n. Lo cual, de nuevo, no es m√°s que una operaci√≥n de "fusi√≥n".

Sin embargo, en este caso, podemos tener algunos activos que son "fantasmas". Esto sucede cuando un icono se prueba con AB en un archivo, pero se sobrescribe en un archivo posterior. En tales casos, los archivos de variantes se exportan a la carpeta de destino, a los que se hace referencia en el objeto assetsMetadata como activo (con su clave y propiedades), pero no se asocian a ning√ļn activo "base" (debido a la fusi√≥n profunda de los objetos activosMetadata ). Estos archivos se eliminar√°n en un paso posterior, antes de completar el proceso.



Como se mencionó anteriormente, necesitamos diferentes formatos finales para diferentes plataformas. Para iOS queremos archivos PDF, y podemos exportarlos directamente con el comando sketchtool . Mientras, para Mobile Web queremos archivos JSX, y para Android queremos archivos VectorDrawable; Por esta razón, exportamos los activos en formato SVG a una carpeta intermedia y luego los sometemos a un procesamiento adicional.

Archivos PDF para iOS


Curiosamente, PDF es el formato (¬Ņsolo?) Compatible con Xcode y OS / iOS para importar y representar activos vectoriales ( aqu√≠ hay una breve explicaci√≥n de las razones t√©cnicas detr√°s de esta elecci√≥n de Apple).

Como podemos exportar directamente en PDF a través de Sketchtool, no hay necesidad de pasos adicionales para esta plataforma: simplemente guardamos los archivos directamente en la carpeta de destino, y eso es todo.

Reaccionar / archivos JSX para web


En el caso de la Web, utilizamos una biblioteca de nodos llamada svgr que convierte archivos SVG simples en componentes React. Pero queremos hacer algo a√ļn m√°s poderoso: queremos "pintar din√°micamente" el √≠cono en tiempo de ejecuci√≥n, con los colores provenientes de los tokens de dise√Īo. Por esta raz√≥n, justo antes de la conversi√≥n, reemplazamos en el SVG los valores de relleno de las rutas que originalmente ten√≠an un estilo compartido aplicado, con el valor de token correspondiente asociado con ese estilo.

Entonces, si este es el archivo badge-feature-like.svg exportado desde Sketch:

 <?xml version="1.0" encoding="UTF-8"?> <svg width="128px" height="128px" viewBox="0 0 128 128" version="1.1" xmlns="<a href="http://www.w3.org/2000/svg">http://www.w3.org/2000/svg</a>" xmlns:xlink="<a href="http://www.w3.org/1999/xlink">http://www.w3.org/1999/xlink</a>"> <!-- Generator: sketchtool 52.2 (67145) - <a href="http://www.bohemiancoding.com/sketch">http://www.bohemiancoding.com/sketch</a> --> <title>badge-feature-like</title> <desc>Created with sketchtool.</desc> <g id="Icons" fill="none" fill-rule="evenodd"> <g id="badge-feature-like"> <circle id="circle" fill="#E71032" cx="64" cy="64" r="64"> <path id="Shape" fill="#FFFFFF" d="M80.4061668,..."></path> </g> </g> </svg> 

el activo / icono final de badge-feature-like.js se verá así:

 /* This file is generated automatically - DO NOT EDIT */ /* eslint-disable max-lines,max-len,camelcase */ const React = require('react'); module.exports = function badge_feature_like({ tokens }) { return ( <svg data-origin="pipeline" viewBox="0 0 128 128"> <g fill="none" fillRule="evenodd"> <circle fill={tokens.TOKEN_COLOR_FEATURE_LIKED_YOU} cx={64} cy={64} r={64} /> <path fill="#FFF" d="M80.4061668,..." /> </g> </svg> ); }; 

Como puede ver, hemos reemplazado el valor est√°tico para el color de relleno del c√≠rculo, por uno din√°mico, que toma su valor de los tokens de dise√Īo (estos estar√°n disponibles para el componente Reaccionar <Icono /> a trav√©s de la API de contexto, Pero esa es otra historia).

Este reemplazo es posible a trav√©s de los metadatos de Sketch para el activo almacenado en el objeto Metadata de los activos : recorriendo recursivamente las capas del activo, es posible crear un selector DOM (en el caso anterior, ser√≠a #Icons # badge-feature- como #circle ) y √ļselo para encontrar el nodo en el √°rbol SVG y reemplace el valor de su atributo de relleno (para esta operaci√≥n usamos la biblioteca cheerio ).

Archivos VectorDrawable para Android


Android admite gráficos vectoriales utilizando su formato vectorial personalizado, llamado VectorDrawable . Por lo general, los desarrolladores realizan la conversión de SVG a VectorDrawable directamente en Android Studio . Pero aquí queríamos automatizar todo el proceso, por lo que necesitábamos encontrar una forma de convertirlos mediante código.

Después de mirar diferentes bibliotecas y herramientas, decidimos usar una biblioteca llamada svg2vectordrawable . No solo se mantiene activamente (al menos, mejor que los otros que encontramos) sino que también es más completo.

El hecho es que VectorDrawable no está en paridad de características con SVG: algunas de las características avanzadas de SVG (p. Ej., Gradientes radiales, máscaras complejas, etc.) no son compatibles , y algunas de ellas han obtenido soporte recientemente (con Android API 24 y más alto). Una desventaja de esto es que en Android pre-24 la regla de relleno "par-impar" no es compatible . Pero en Badoo necesitamos ser compatibles con Android 5 y superior. Es por eso que, como se explicó anteriormente, para Android necesitamos convertir cada ruta en los archivos de Sketch a relleno "distinto de cero".

Potencialmente, los dise√Īadores podr√≠an hacer esto manualmente:



pero esto puede pasarse por alto f√°cilmente y, por lo tanto, ser propenso a errores humanos.

Por esta razón, hemos agregado un paso adicional en nuestro proceso para Android, donde convertimos automáticamente todas las rutas a no cero en el Sketch JSON. Esto es para que cuando exportamos los íconos a SVG, ya estén en este formato, y cada VectorDrawable generado también sea compatible con dispositivos Android 5.

El archivo final badge-feature-like.xml en este caso tiene este aspecto:

 <!-- This file is generated automatically - DO NOT EDIT --> <vector xmlns:android="<a href="http://schemas.android.com/apk/res/android">http://schemas.android.com/apk/res/android</a>" android:width="128dp" android:height="128dp" android:viewportWidth="128" android:viewportHeight="128"> <path android:fillColor="?color_feature_liked_you" android:pathData="M64 1a63 63 0 1 0 0 126A63 63 0 1 0 64 1z" /> <path android:fillColor="#FFFFFF" android:pathData="M80.406 ..." /> </vector> 

Como puede ver, tambi√©n en los archivos de VectorDrawable inyectamos nombres de variables para los colores de relleno , que est√°n asociados a los tokens de dise√Īo a trav√©s de estilos personalizados en las aplicaciones de Android.

Así es como se ve VectorDrawable una vez importado en Android Studio:


Un ejemplo de un icono de VectorDrawable importado a Android Studio

Una cosa a tener en cuenta en este caso: Android Studio tiene una forma muy estricta y prescriptiva de organizar los activos: ¬°no hay carpetas anidadas y todos los nombres en min√ļsculas! Esto significaba que ten√≠amos que encontrar un formato ligeramente diferente para los nombres de sus √≠conos: en el caso de un activo en experimentaci√≥n, su nombre ser√° algo as√≠ como ic_icon-name__experiment-name__variant-name .

Diccionario JSON como biblioteca de activos


Una vez que los archivos de activos se guardan en su formato final, lo √ļltimo que queda por hacer es guardar toda la metainformaci√≥n recopilada durante el proceso de compilaci√≥n y almacenarla en un "diccionario" para que pueda estar disponible m√°s adelante. cuando los activos son importados y consumidos por la base de c√≥digo de las diferentes plataformas.

Una vez extraída la lista plana de iconos del objeto assetsMetadata , la recorrimos y para cada elemento verificamos:

  • si es un activo normal (por ejemplo, tabbar-livestream ), y si lo es, simplemente lo conservamos;
  • si se trata de una variante en una prueba AB (p. ej. experimento / tabbar-livestream [variante] ) asociamos su nombre, ruta, prueba AB y nombres de variantes a las propiedades abtest del activo "base" (en este caso, tabbar- livestream ), y luego eliminamos la entrada variante de la lista / objeto (solo cuenta la "base");
  • si es una variante "fantasma", eliminamos el archivo y luego eliminamos la entrada de la lista / objeto.

Una vez que se completa el ciclo, el diccionario contendr√° la lista de todos y solo los √≠conos "base" (y sus pruebas AB, si est√°n bajo experimento). Para cada uno de estos, contendr√° su nombre, tama√Īo, ruta y, en caso de que un icono est√© bajo prueba AB, la informaci√≥n sobre las diferentes opciones del activo.

Este diccionario se guarda en formato JSON en la carpeta de destino de la marca y la plataforma . Aquí, por ejemplo, está el archivo assets.json generado para la aplicación "Blendr" en "web móvil":

 { "platform": "mw", "brand": "blendr", "assets": { "badge-feature-like": { "assetname": "badge-feature-like", "path": "assets/badge-feature-like.jsx", "width": 64, "height": 64, "source": "icons_common" }, "navigation-bar-edit": { "assetname": "navigation-bar-edit", "path": "assets/navigation-bar-edit.jsx", "width": 48, "height": 48, "source": "icons_common" }, "tabbar-livestream": { "assetname": "tabbar-livestream", "path": "assets/tabbar-livestream.jsx", "width": 128, "height": 128, "source": "icons_blendr", "abtest": { "this__is_an_experiment": { "control": "assets/this__is_an_experiment/tabbar-livestream__control.jsx", "variant1": "assets/this__is_an_experiment/tabbar-livestream__variant1.jsx", "variant2": "assets/this__is_an_experiment/tabbar-livestream__variant2.jsx" }, "a_second-experiment": { "control": "assets/a_second-experiment/tabbar-livestream__control.jsx", "variantA": "assets/a_second-experiment/tabbar-livestream__variantA.jsx" } } }, ... } } 

El √ļltimo paso es comprimir todas las carpetas de activos . archivos zip , para que puedan descargarse m√°s f√°cilmente.

El resultado final


El proceso descrito anteriormente, desde la clonación inicial y la manipulación de los archivos de Sketch, hasta la exportación (y conversión) de los activos en el formato deseado para cada plataforma compatible, hasta el almacenamiento de la metainformación recopilada en una biblioteca de activos. repetido para cada marca declarada en el script de compilación.

A continuación se muestra una captura de pantalla de la estructura de las carpetas src y dist , una vez que se completa el proceso de compilación:


Estructura de las carpetas "src" y "dist" después de completar el proceso de compilación.

En este punto, con un simple comando es posible cargar todos los recursos (archivos JSON, archivos ZIP y archivos de activos) en un repositorio remoto, y ponerlos a disposición de todas las plataformas, para descargar y consumir en sus bases de código.

(La forma en que las plataformas reales recuperan y procesan los activos, a través de scripts personalizados que se crearon ad-hoc para este propósito, está más allá del alcance de este artículo. Pero esto probablemente se cubrirá muy pronto en otras publicaciones de blog dedicadas, por uno de los otros desarrolladores que trabajaron conmigo en este proyecto).

Conclusiones (y lecciones aprendidas en el camino)


Siempre me ha encantado Sketch . Durante a√Īos ha sido la herramienta de elecci√≥n "de facto" para el dise√Īo (y desarrollo) de aplicaciones y web. As√≠ que estaba muy interesado y curioso por explorar posibles integraciones como html-sketchapp o herramientas similares, que podr√≠amos usar en nuestros flujos de trabajo y tuber√≠as.

Este flujo (ideal) siempre ha sido el santo grial para mí ( y para muchos otros ):



Sketch como herramienta de dise√Īo se puede imaginar como un posible "objetivo" de la base de c√≥digo.

Pero debo admitir que recientemente comenc√© a preguntarme si Sketch todav√≠a era la herramienta adecuada, especialmente en el contexto de un Sistema de dise√Īo. Entonces, comenc√© a explorar nuevas herramientas como Figma , con sus API abiertas, y Framer X , con su incre√≠ble integraci√≥n con React, porque no ve√≠a esfuerzos equivalentes de Sketch para avanzar hacia la integraci√≥n con el c√≥digo (cualquiera que sea el c√≥digo).

Bueno, este proyecto cambió de opinión. No completamente, pero definitivamente mucho.

Quizás Sketch no está exponiendo oficialmente sus API, pero ciertamente la forma en que han construido la estructura interna de sus archivos es una especie de API "no oficial". Podrían haber usado nombres crípticos u ofuscar las claves en los objetos JSON; en su lugar, han optado por una convención de nomenclatura semántica clara, fácil de leer y legible para humanos. No puedo pensar que esto sea simplemente accidental.

El hecho de que los archivos de Sketch se puedan manipular me ha abierto la mente a una amplia gama de posibles desarrollos y mejoras futuros. Desde complementos para validar el nombre, el estilo y la estructura de las capas para los iconos, hasta posibles integraciones con nuestra wiki y nuestra documentaci√≥n del sistema de dise√Īo (en ambas direcciones), a trav√©s de la creaci√≥n de aplicaciones Node alojadas en Electron o Carlo para facilitar muchas de las tareas repetitivas que los dise√Īadores deben emprender.

Una ventaja inesperada de este proyecto (al menos para m√≠) es que ahora los archivos de Sketch con los "iconos Cosmos" se han convertido en una "fuente de verdad", de manera similar a lo que sucedi√≥ con el sistema de dise√Īo Cosmos . Si un icono no est√° all√≠, no existe en la base de c√≥digo (o mejor, no deber√≠a existir: pero al menos sabemos que es una excepci√≥n). S√© que parece obvio ahora, pero no antes, al menos para m√≠.

Lo que comenz√≥ como un proyecto MVP, pronto se convirti√≥ en una inmersi√≥n profunda (literalmente) en lo interno de los archivos de Sketch, con la conciencia de que estos pueden ser manipulados. Todav√≠a no sabemos a d√≥nde llevar√° todo esto, pero hasta ahora ha sido un √©xito. Los dise√Īadores, desarrolladores, PM y partes interesadas, todos est√°n de acuerdo en que esto ahorrar√° mucho trabajo manual para todos y evitar√° muchos errores potenciales. Pero tambi√©n abrir√° las puertas a los usos de los iconos que han sido imposibles hasta ahora.

Una √ļltima cosa: lo que describ√≠ en esta larga publicaci√≥n es una tuber√≠a que hemos construido aqu√≠ para resolver nuestros problemas particulares, por lo que necesariamente est√° s√ļper personalizada para nuestro contexto. Tenga en cuenta que puede que no se adapte a las necesidades de su negocio o que sea apropiado para su contexto.

Pero lo que es importante para m√≠, y lo que quer√≠a compartir, es que se puede hacer. Tal vez de diferentes maneras, con diferentes enfoques y diferentes formatos de salida, que tal vez impliquen menos complejidad (es decir, es posible que no necesite la marca m√ļltiple y la prueba AB). Pero ahora puede automatizar el flujo de trabajo involucrado en la entrega de sus iconos con un script Node.js personalizado y Sketch.

Encuentra tu propia forma de hacerlo. Es divertido (y relativamente f√°cil).

Créditos


Este gran proyecto fue desarrollado en colaboración con Nikhil Verma (Mobile Web), quien creó la primera versión del script de compilación, y Artem Rudoi (Android) e Igor Savelev (iOS), quienes desarrollaron los scripts que importan y consumen los activos en su respectivas plataformas nativas. Gracias, amigos, fue una maravilla trabajar con ustedes en este proyecto y verlo cobrar vida.

Source: https://habr.com/ru/post/441042/


All Articles