基于nodejs的psd切图功能

使用nodejs 切图

###简述
项目近期进行大量换皮,需要更高效率的切图,搜索了相关资料后决定使用nodejs下的psd2json模块用于切图,也因为工具链中ui编辑器支持json(需要修改导出规范,正在修改中,但需要美术按规范出psd文件,意义不大)。

经使用是节省了一部分重复性高的的工作 如调用ps已录制好动作进行切图,选择存放文件夹;

所需要的psd文件 还是需要手动整理好图层的;当然也可以通过命名规范,来提高开发效率。

或许导出规范弄好后,还可以在节省一部分排版的时间和创建一些控件的时间,因为美术已经排好版,我们可以直接使用位置信息,以及一些文本控件的属性(字体大小,颜色等)。

###环境搭建:
搭建nodejs 环境 http://www.runoob.com/nodejs/nodejs-tutorial.html;
使用npm 安装 psd2json(cmd窗口输入命令: npm i psd2json);
将path\node_modules.bin路径加入到环境变量

修改模块使其支持命令调用:

####1 修改psd2json模块文件夹中的文件

新增convert.js文件用于命令行调用

#!/usr/bin/env node

var psd2json = require("./index.js");

function run(argv) {
    if (argv.length == 0) {
        console.info('Usage: psd2json psdFile [outputFile] [--nopack] [--ignore-font] [#buildId]');
        process.exit(0);
    }

    var psdFile = argv[0];
    var outputFile;
    var option = 0;
    var options = {};
    var buildId;
    for (var i = 1; i < argv.length; i++) {
        var arg = argv[i];
        if (arg.indexOf('--') == 0) {
            switch (arg.substr(2)) {
                case 'nopack':
                    option |= psd2json.constants.NO_PACK;
                    break;
                case 'ignore-font':
                    option |= psd2json.constants.IGNORE_FONT;
                    break;
                default:
                    console.error('unknown argument: ' + arg);
                    process.exit(1);
                    break;
            }
        }
        else if (arg.substr(0,1) == '#') {
            buildId = arg.substr(1);
        }
        else {
            if (!outputFile)
                outputFile = arg;
            else {
                console.error('unknown argument: ' + arg);
                process.exit(1);
            }
        }
    }

    psd2json.convert(psdFile, options)
        .then(function (buildId) {
            console.log('buildId: ' + buildId);
        }).catch(console.err);
}

run(process.argv.slice(2));

修改index.js方法

const fs = require('fs');
const path = require('path');
const psd = require('psd');
const mkdirp = require('mkdirp');

/**
 * Output PSD layout to JSON
 * @param {string} psdFile Relative path or absolute path of PSD file
 * @param {Object} [options] options
 * @param {string} [options.outJsonDir] Set to output files
 * @param {string} [options.outImgDir] Set to output files
 */
exports.convert = function (psdFile, options = {}) {
  return new Promise(function (resolve, reject) {
    const psdFilePath = path.resolve(psdFile);
    const psdFileName = path.basename(psdFilePath, path.extname(psdFilePath));

    var pathInfo = path.parse(psdFile);

    if (!options.outImgDir) {
      options.outImgDir = path.join(pathInfo.dir, pathInfo.name);
    }

    // get root node.
    const psdData = psd.fromFile(psdFilePath);
    psdData.parse();
    const rootNode = psdData.tree();
    const queueNodes = [];
    const queueNodesIndex = [];
    const queueNodesName = [];
    const queueNodesStructure = [];

    queueNodes.push(rootNode._children);
    queueNodesIndex.push(0);
    queueNodesName.push(undefined);
    const psdStructure = {
      'group' : []
    };
    queueNodesStructure.push(psdStructure);

    queueLoop: while (0 < queueNodes.length) {
      const queueIndex = queueNodes.length - 1;
      const nodes = queueNodes[queueIndex];
      const nodesStructure = queueNodesStructure[queueIndex];
      let nodesIndex = queueNodesIndex[queueIndex];
      let nodesName = queueNodesName[queueIndex];
      
      if (nodesName === undefined) {
        nodesName = '';
      } else {
        nodesName += path.sep;
      }
    
      while (nodesIndex < nodes.length) {
        const node = nodes[nodesIndex];
        nodesIndex++;
        if (node.layer.visible === false) continue;
        if (node.type === 'group') {
          queueNodes.push(node._children);
          queueNodesIndex[queueIndex] = nodesIndex;
          queueNodesIndex.push(0);
          queueNodesName.push(nodesName + node.name);
          const structure = {
            'name' : node.name,
            'group' : []
          };
          nodesStructure.group.push(structure);
          queueNodesStructure.push(structure);
          continue queueLoop;
        } else {
          if (options.outImgDir) {
            const outImgDirPath = options.outImgDir;
            mkdirp.sync(outImgDirPath);
            console.log('run' + path.join(outImgDirPath, node.name + '.png'));
            node.layer.image.saveAsPng(path.join(outImgDirPath, node.name + '.png'));
          }
          const structure = {
            'name' : node.name,
            'x' : node.layer.left,
            'y' : node.layer.top,
            'width' : node.layer.width,
            'height' : node.layer.height
          };
          nodesStructure.group.push(structure);
        }
      }
    
      queueNodes.pop();
      queueNodesIndex.pop();
      queueNodesName.pop();
      queueNodesStructure.pop();
    }

    const outJsonData = JSON.stringify(psdStructure.group);

    if (options.outJsonDir) {
      const outJsonDirPath = path.resolve(options.outJsonDir);
      const outJsonPath = path.join(outJsonDirPath, psdFileName + '_ui_cfg.json');
      // make output directory.
      if (!fs.existsSync(outJsonDirPath)) {
        fs.mkdirSync(outJsonDirPath);
      }
      // output file.
      fs.writeFileSync(outJsonPath, outJsonData);
    }

    console.log('run');
  });
}

####2 编写cmd命令调用js文件
在path\node_modules.bin文件夹中加入psd2json.cmd 命令文件

代码

@IF EXIST "%~dp0\node.exe" (
  "%~dp0\node.exe"  "%~dp0\..\psd2json\convert.js" %*
) ELSE (
  @SETLOCAL
  @SET PATHEXT=%PATHEXT:;.JS;=;%
  node  "%~dp0\..\psd2json\convert.js" %*
)

###使用:
在psd文件目录下打开cmd窗口 输入 psd2json @psd文件名字.psd

调用命令
基于nodejs的psd切图功能

调用成功
基于nodejs的psd切图功能

###PS
path为cmd窗口当前路径;
js 文件编写参考 psd2fgui模块;也可通过命令安装该模块:npm i psd2fgui

###参考:
http://gad.qq.com/article/detail/47830 快速转化PSD为UI界面
https://github.com/fairygui/psd2fgui psd2fgui地址