Передача файлов от дизайнера к программисту. Скрипты

от автора

image
Допустим у нас есть команда разработчиков приложений для мобильных устройств. В такой команде точно есть дизайнер и точно есть программист. Сначала они занимаються каждый своей работой — программист разрабатывает прототип, механику, дизайнер делает наброски клавиш, бэкграундов. Но приходит момент когда дизайнер должен передать свою работу для того, что бы увидеть её уже в приложении. И вот тут могут возникнуть трудности.

Рассмотрим это на примере одного экрана меню. В нём может быть 30-40 элементов графики. Это и клавиши (статичные, нажатые), анимированый логотип из 10-ти объектов, всплывающее меню, анимация на бэкграунде. Преимущественно дизайнер выдаёт каждый файл отдельно, а затем программист по новой складывает его. Таким образом выходит конструктор, который складываеться и раскладываеться по несколько раз. Затем дизайн может поменяться и всё приходиться делать по-новой.

Как максимально автоматизировать этот процесс

Вот несколько шагов, которые я использую для того, что бы от дизайнера к программисту пришел максимально удобный набор файлов без потери качества.

Исходный файл в AI в максимальном разрешении (2048*1536px в моём случае) с черновым вариантом дизайна.
— разложить и сгрупировать все объекты по слоям
— назвать их правильно (кнопки — Btn, иконки — Icon, подложки — Uunderlay…)
— сделать все элементы кратными 2px по ширине и высоте
— расположить все элементы по XY кратными 2px
— экспортировать XY-координаты каждого слоя, его название и положение на листе. Поскольку Illustrator не умеет экспортироваль текстовый файл — данный будут помещены в самый верхний слой в объект *Текст*

XY layer coordinates script

if (app.documents.length > 0)   {  var doc = app.activeDocument; var x, y, t; app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;  var count = doc.layers.length;  var out_txt="";   for ( var i = 0; i < count; ++i) {  doc.activeLayer = doc.layers[i];  doc.layers[i].hasSelectedArtwork = true;  }   for ( var i = 0; i < count; ++i) { x = doc.selection[i].position[0];  y = doc.selection[i].position[1]*(-1);  // Layer name, X, Y, layer number out_txt += doc.layers[i].name + ";;;;x=" + x.toFixed(0) + ";;;;y=" + y.toFixed(0) + ";;;;" + i +"\n";  }   //Create text frame in first layer on position 0,0 t = doc.layers[0].textFrames.add();         t.contents = out_txt;   }  

— экспортировать каждый слой в отдельный PNG24 файл в двухкратном размере. Arcticmill

Export Layers as PNG files

// *** Export Layers as PNG files (in multiple resolutions) *** // This script will export all layers that have a name starting with "#", or "%" into a subfolder of the current .ai document. // These options can be configured below: // *** Config options  *** var subFolderName = "Export"; var saveInMultipleResolutions = true; // ... // Note: only use one character! var exportLayersStartingWith = "#"; var exportLayersWithArtboardClippingStartingWith = "%"; // ... var lowResolutionFileAppend = "@Low"; var normalResolutionFileAppend = "-ipad"; var highResolutionFileAppend = "-ipadhd"; // ... var lowResolutionScale = 50; var normalResolutionScale = 100; var highResolutionScale = 200;  // *** Start of script *** var doc = app.activeDocument;  // Make sure we have saved the document if (doc.path != "") {      // Check if we need to create the export directory or we will get errors up ahead     var exportDirectoryPath = doc.path + "/" + subFolderName;      var exportDirectory = new Folder(exportDirectoryPath);      if (!exportDirectory.exists) {         // We must create the export directory it seems         var newFolder = new Folder(exportDirectoryPath);         newFolder.create();     }      var layerData = new Array();      // Finds all layers that should be saved and saves these to the export layers array     collectLayerData(doc, null);      var layersToExportCount = 0;      for (var i = 0; i < layerData.length; i++) {         if ((layerData[i].tag == "include") || (layerData[i].tag == "include_and_clip")) {              // Hide all layers first             hideAllLayers();              var clipToArtboard = false;              if (layerData[i].tag == "include_and_clip") {                 clipToArtboard = true;             }              // Now show all layers needed to actually display the current layer on screen             layerData[i].showIncludingParentAndChildLayers(); //showIncludingParents();              // Now we can export the layer as one or multiple PNG files!              var savePath = doc.path; // Save to same folder as document but in a sub directory              if (saveInMultipleResolutions) {                 // iPhone 3GS (50%)                 savePath.changePath(subFolderName + "/" + layerData[i].layer.name.substring(1, layerData[i].layer.name.length) + fixFileAppend(lowResolutionFileAppend));                 savePNG(savePath, lowResolutionScale, clipToArtboard);                  savePath = doc.path;                  // iPhone 4 (100%)                 savePath.changePath(subFolderName + "/" + layerData[i].layer.name.substring(1, layerData[i].layer.name.length) + fixFileAppend(normalResolutionFileAppend));                 savePNG(savePath, normalResolutionScale, clipToArtboard);                  savePath = doc.path;                  // iPad Retina (200%)                 savePath.changePath(subFolderName + "/" + layerData[i].layer.name.substring(1, layerData[i].layer.name.length) + fixFileAppend(highResolutionFileAppend));                 savePNG(savePath, highResolutionScale, clipToArtboard);              } else {                 // Save normally (100%)                 savePath.changePath(subFolderName + "/" + layerData[i].layer.name.substring(1, layerData[i].layer.name.length));                 savePNG(savePath, normalResolutionScale, clipToArtboard);             }              layersToExportCount++;         }     }      // Restore everything like it was before!     restoreAllLayers();      // Was there anything exported? If not make a warning!     if (layersToExportCount == 0) {         alert("Ooops, Found no layers to export!\n\nRemember that you must add a \"" + exportLayersStartingWith + "\" (when exporting the layer cropped to it's bound) or \"" + exportLayersWithArtboardClippingStartingWith + "\" (when layer should be clipped to artboard) to the beginning of the layer name. Also make sure that they layers you want to export are not locked or hidden.");     } else {         // Show a completed message         alert(layersToExportCount + " layer(s) was successfully exported to: \n" + exportDirectoryPath);     }  } else {     // Document not saved yet!     alert("Sorry, but you must save your document before you can use the export layers script! This is because exported images are saved in a subfolder to your original file."); }  function fixFileAppend(fileAppend) {      if (fileAppend == "") {         return "";     } else {         return fileAppend + ".png";     }  }  function hideAllLayers() {      for (var i = 0; i < layerData.length; i++) {         layerData[i].hide();     } }  function restoreAllLayers() {      for (var i = 0; i < layerData.length; i++) {         layerData[i].restoreVisibility();     }  }  // Collects information about the various layers function collectLayerData(rootLayer, extendedRootLayer) {     for (var i = 0; i < rootLayer.layers.length; i++) {          // We never even process locked or hidden layers         if ((!rootLayer.layers[i].locked) && (rootLayer.layers[i].visible)) {              var extendedLayer = new ExtendedLayer(rootLayer.layers[i]);              // Set up parent             extendedLayer.parentLayer = extendedRootLayer;              // Also add this layer to the parents child collection             if (extendedRootLayer != null) {                 extendedRootLayer.childLayers.push(extendedLayer);             }              layerData.push(extendedLayer);              // Tag these layers so that we later can find out if we should export these layers or not             if (rootLayer.layers[i].name.substring(0, 1) == exportLayersStartingWith) {                 extendedLayer.tag = "include";             } else if (rootLayer.layers[i].name.substring(0, 1) == exportLayersWithArtboardClippingStartingWith) {                 extendedLayer.tag = "include_and_clip";             } else {                 extendedLayer.tag = "skip";             }              // We should not export this layer but we continue looking for sub layers that might need to be exported             collectLayerData(rootLayer.layers[i], extendedLayer);         }     } }  // Holds info and additional methods for layers function ExtendedLayer(layer) {     this.originalVisibility = layer.visible;     this.layer = layer;     this.tag = "";     this.hide = hide;     this.show = show;     this.showIncludingParentAndChildLayers = showIncludingParentAndChildLayers;     this.restoreVisibility = restoreVisibility;     this.restoreVisibilityIncludingChildLayers = restoreVisibilityIncludingChildLayers;     this.layerName = layer.name;      // Set after creating     this.childLayers = new Array();     this.parentLayer = null;      function hide() {         layer.visible = false;     }      function show() {         layer.visible = true;     }      // Shows this layer including it's parent layers (up to the root) and it's child layers     function showIncludingParentAndChildLayers() {          var parentlayerName = "";          if (this.parentLayer != null) {             parentlayerName = this.parentLayer.layerName;         }          // Show all parents first         var aParentLayer = this.parentLayer;          while (aParentLayer != null) {             aParentLayer.restoreVisibility();              // Keep looking             aParentLayer = aParentLayer.parentLayer;         }          // Show our own layer finally         this.restoreVisibilityIncludingChildLayers();     }      function restoreVisibility() {         layer.visible = this.originalVisibility;     }      function restoreVisibilityIncludingChildLayers() {         this.restoreVisibility();          // Call recursively for each child layer         for (var i = 0; i < this.childLayers.length; i++) {             this.childLayers[i].restoreVisibilityIncludingChildLayers();         }     } }  // Save PNG file function savePNG(file, scale, artBoardClipping) {      var exp = new ExportOptionsPNG24();     exp.transparency = true;     exp.horizontalScale = scale     exp.verticalScale = scale;     exp.artBoardClipping = artBoardClipping;      doc.exportFile(file, ExportType.PNG24, exp); } 

— сделать из этих файлов атлас размером 4096*4096 (или меньше, но обьязательно квадратный) с помощью Zwoptex или Texture Packer. Обьязательно дать отступ между картинками 8px
— избежать проблем с прозрачностью, которые случаються, если принебрегти кратностью 2px в векторном файле. К счастью есть экшн для Photoshop AlphaUnity
image
— уменьшить атлас в 2 раза
— перегнать файл в PNG8

Таким образом программист получит от меня текстовый файл с названиями картинок, их координаты, положение на листе, PLIST или XML с координатами и поворотами, атлас картинок в оптимальном качестве и превьюшку. Так сложилось что ни дизайнер ни программист не хотят заниматься этой рутинной, но очень нужной процедурой.

Такой алгоритм действий будет трудным и долгим только в первый раз, но потом очень упростит жизнь всем участникам проэкта.

Линк на скрипты и экшн. GitHub

ссылка на оригинал статьи http://habrahabr.ru/post/158131/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *