UNPKG

19.2 kBJavaScriptView Raw
1'use strict';
2
3/**
4 * 精灵图生成
5 *
6 * @class OutSprite
7 * {
8 * srcDir:'', <string> 源文件路径
9 * distSpreiteDir:'', [string] 精灵图输出目录
10 * distScssDir:'' [string] scss输出目录
11 * }
12 */
13class OutSprite{
14 constructor(option){
15 const _ts = this;
16
17 option = option || {};
18
19 let m = _ts.m = {
20 path:require('path'),
21 fs:require('fs-extra'),
22 spritesmith:require('spritesmith'),
23 svgstore:require('svgstore'),
24 pathInfo:require('./getPathInfo')
25 },
26 config = _ts.config = {};
27
28 //配置写入到_ts.config
29 for(let i in option){
30 config[i] = option[i];
31 };
32
33 return new Promise((resolve,reject)=>{
34 //检查目录是否存在
35 if(m.pathInfo(config.srcDir).type === 'dir'){
36 _ts.init().then(v => {
37 resolve(v);
38 }).catch(e => {
39 reject(e);
40 });
41 }else{
42 reject({
43 status:'error',
44 msg:`${config.srcDir} 不是有效的目录`
45 });
46 };
47 });
48 }
49
50 init(){
51 const _ts = this,
52 m = _ts.m,
53 config = _ts.config;
54
55 let srcDirName = (()=>{
56 let pathDirs = config.srcDir.split(m.path.sep);
57 return pathDirs[pathDirs.length-1];
58 })(),
59
60 spriteTaskList = [],
61
62 imgList = _ts.getImgList();
63
64
65 //处理svg图片
66 spriteTaskList.push(new Promise((resolve,reject)=>{
67 //处理svg图片
68 if(imgList.svg.length){
69 let svgstore = m.svgstore(),
70 dist = m.path.join(config.distSpreiteDir,srcDirName+'.svg');
71
72 //将svg图片添加到svgstore
73 imgList.svg.forEach((item,index)=>{
74 let svgElementName = m.path.basename(item,m.path.extname(item));
75 svgstore.add(svgElementName,m.fs.readFileSync(item,'utf8'));
76 });
77
78 //写入文件
79 m.fs.ensureDir(config.distSpreiteDir,err => {
80 if(err){
81 reject({
82 status:'error',
83 msg:`创建失败 ${config.distSpreiteDir}`,
84 info:err
85 });
86 }else{
87 try {
88 m.fs.writeFileSync(dist,svgstore);
89 resolve({
90 status:'success',
91 msg:`SVG精灵 ${config.srcDir}`,
92 data:svgstore.toString()
93 });
94 } catch (err) {
95 reject({
96 status:'error',
97 msg:`写入失败 ${dist}`,
98 info:err
99 });
100 };
101 };
102 });
103 }else{
104 resolve({
105 status:'success',
106 msg:'目录无Svg图片要处理'
107 });
108 };
109 }));
110
111
112
113 //处理png精灵图片
114 spriteTaskList.push(new Promise((resolve,reject)=>{
115 if(imgList.png.length){
116 //tl,bl
117
118 //设置选项
119 let option = {
120 src:imgList.png, //png图片列表
121 padding:4, //图片间隙大小
122 algorithm:'binary-tree', //图片对齐方式
123 algorithmOpts:{
124 sort:false
125 }
126 };
127
128 (()=>{
129 let rule = srcDirName.split('_'),
130 temp = +rule[rule.length - 1],
131 isAlgorithms = (type)=>{
132 let tempType = 'binary-tree';
133 switch (type) {
134 case 'td':
135 tempType = 'top-down';
136 break;
137 case 'lr':
138 tempType = 'left-right';
139 break;
140 case 'd':
141 tempType = 'diagonal';
142 break;
143 case 'ad':
144 tempType = 'binary-tree';
145 break;
146 };
147 return tempType;
148 };
149
150 option.padding = isNaN(temp) ? 4 : temp;
151 option.algorithm = isNaN(temp) ? isAlgorithms(rule[rule.length - 1]) : isAlgorithms(rule[rule.length - 2]);
152 })();
153
154 //如果配置中有声明图像处理引擎,则传入引擎到配置中
155 if(fws.config.imgEngine !== '' && typeof fws.config.imgEngine === 'string'){
156 option.engine = require(fws.config.imgEngine);
157 };
158
159 //生成png精灵图、sass文件、以及更新项目精灵图索引(sass)
160 let pngMotor = (err,result)=>{
161 if(err){
162 reject({
163 status:'error',
164 msg:`编译出错 ${config.srcDir} *.png`,
165 info:err
166 });
167 }else{
168 //精灵图数据
169 let spriteData = {
170 size:{},
171 spriteNames:[],
172 element:{},
173 url:'',
174 path:''
175 },
176
177 //精灵图目录名称
178 dirName = (()=>{
179 let pathDirs = config.srcDir.split(m.path.sep);
180 return pathDirs[pathDirs.length-1];
181 })(),
182
183 //sass文件输出路径
184 spriteSassDistPath = m.path.join(config.distScssDir,'_spriteData',dirName+'.scss'),
185
186 //sass文件输出目录
187 spriteSassDistDir = m.path.dirname(spriteSassDistPath),
188
189 //精灵图输出路径
190 spriteDist = m.path.join(config.distSpreiteDir,srcDirName+'.png');
191
192 let outTask = [];
193
194 //保存精灵图图片
195 outTask.push(()=>{
196 return new Promise((resolve,reject)=>{
197 if(result.image){
198 m.fs.ensureDir(config.distSpreiteDir,err => {
199 if(err){
200 resolve({
201 status:'error',
202 msg:`创建失败 ${config.distSpreiteDir}`,
203 info:err
204 });
205 }else{
206 try {
207 m.fs.writeFileSync(spriteDist,result.image);
208 resolve({
209 status:'success',
210 msg:`生成 ${spriteDist}`,
211 distPath:spriteDist,
212 data:result.image
213 });
214 } catch (err) {
215 reject({
216 status:'error',
217 msg:`写入失败 ${spriteDist}`,
218 distPath:spriteDist,
219 info:err
220 });
221 };
222 }
223 });
224 };
225 });
226 });
227
228 //保存精灵图SASS
229 outTask.push(()=>{
230 return new Promise((resolve,reject)=>{
231 //得到精灵图的url
232 spriteData.url = (()=>{
233 let sPath = '';
234
235 //如果不是在fws环境下使用,图片url为文件名,否则根据fws.devPath的目录来生成相对的
236 if(global.fws){
237 let surl = spriteDist.replace(fws.devPath,'');
238 //sPath = '..';
239 surl = surl.split(_ts.m.path.sep);
240 surl.forEach((item,index)=>{
241 if(item !== ''){
242 let sep = !sPath ? '' : '/';
243 sPath += sep+item;
244 };
245 });
246 }else{
247 sPath = dirName+'.png';
248 };
249 return sPath;
250 })();
251
252 //得到精灵图输出路径
253 spriteData.path = spriteDist;
254
255 //精灵图元素数据
256 if(result.coordinates){
257 for(let i in result.coordinates){
258 let fileType = _ts.m.path.extname(i),
259 fileName = _ts.m.path.basename(i,fileType);
260 spriteData.spriteNames.push(fileName);
261 spriteData.element[fileName] = result.coordinates[i];
262 };
263 };
264 spriteData.spriteNames = '_!@!&_'+spriteData.spriteNames.toString()+'_&!@!_';
265
266 //精灵图大小
267 if(result.properties){
268 spriteData.size.width = result.properties.width;
269 spriteData.size.height = result.properties.height;
270 };
271
272 //将对象数据转为sass字符串
273 let sSpriteData = JSON.stringify(spriteData,null,2);
274
275 sSpriteData = sSpriteData.replace('"_!@!&_','(');
276 sSpriteData = sSpriteData.replace('_&!@!_"',')');
277 sSpriteData = sSpriteData.replace(/{/g,'(');
278 sSpriteData = sSpriteData.replace(/}/g,')');
279
280 sSpriteData = '$'+dirName+': '+sSpriteData;
281
282 //保存sass 文件
283 m.fs.ensureDir(spriteSassDistDir,err => {
284 if(err){
285 reject({
286 status:'error',
287 msg:`创建失败 ${spriteSassDistDir}`,
288 info:err
289 });
290 }else{
291 try {
292 m.fs.writeFileSync(spriteSassDistPath,sSpriteData);
293 resolve({
294 status:'success',
295 msg:`写入 ${spriteSassDistPath}`,
296 distPath:spriteSassDistPath,
297 data:sSpriteData
298 });
299 } catch (error) {
300 reject({
301 status:'error',
302 msg:`写入失败 ${spriteSassDistPath}`,
303 distPath:spriteSassDistPath,
304 info:err
305 });
306 };
307 };
308 });
309 });
310 });
311
312 //更新精灵图数据(scss/_spriteData.scss)到项目
313 outTask.push(()=>{
314 return new Promise((resolve,reject)=>{
315 let aSdFileList = m.fs.readdirSync(spriteSassDistDir),
316 aSdList = [],
317 _spriteDataContent = '@charset "utf-8";\r\n//以下内容由程序自动更新\r\n',
318 re = /^(\w*).(scss)$/i;
319 //筛选出非标准的.scss文件
320 aSdFileList.forEach((item,index)=>{
321 if(re.test(item)){
322 _spriteDataContent += '@import "./_spriteData/'+item+'";\r\n';
323 aSdList.push(item);
324 };
325 });
326
327 //更新
328 let _fws_spriteDataPath = m.path.join(config.distScssDir,'_spriteData.scss');
329
330 m.fs.ensureDir(config.distScssDir,err => {
331 if(err){
332 reject({
333 status:'error',
334 msg:`创建失败 ${config.distScssDir}`,
335 info:err
336 });
337 }else{
338 try {
339 m.fs.writeFileSync(_fws_spriteDataPath,_spriteDataContent);
340 resolve({
341 status:'success',
342 msg:`写入 ${_fws_spriteDataPath}`,
343 distPath:_fws_spriteDataPath,
344 data:_spriteDataContent
345 });
346 } catch (error) {
347 reject({
348 status:'error',
349 msg:`写入失败 ${_fws_spriteDataPath}`,
350 distPath:_fws_spriteDataPath,
351 info:error
352 });
353 };
354 };
355 });
356
357 });
358 });
359
360 let pngTaskAsync = async ()=>{
361 let data = {};
362 for(let i=0,len = outTask.length; i<len; i++){
363 let result = await outTask[i]();
364
365 switch (i) {
366 case 0:
367 data['spritePng'] = result.data;
368 break;
369
370 case 1:
371 data['spriteScss'] = result.data;
372 break;
373
374 case 2:
375 data['spriteScssMap'] = result.data;
376 break;
377 };
378 };
379 return {
380 status:'success',
381 msg:`PNG精灵 ${config.srcDir}`,
382 data:data
383 };
384 };
385 pngTaskAsync().then(v => {
386 resolve(v);
387 }).catch(e => {
388 reject(e);
389 });
390
391 };
392 };
393
394 m.spritesmith.run(option,pngMotor);
395 }else{
396 resolve({
397 status:'success',
398 msg:'目录无Png图片要处理'
399 });
400 };
401 }));
402
403 return Promise.all(spriteTaskList);
404 }
405
406 //获取图片列表
407 getImgList(){
408 const _ts = this,
409 m = _ts.m,
410 config = _ts.config;
411
412 let imgList = {
413 'png':[],
414 'svg':[]
415 },
416 files = m.fs.readdirSync(config.srcDir);
417 files.forEach((item,index)=>{
418 let type = item.substr(item.length - 4,4).toLowerCase(),
419 filePath = m.path.join(config.srcDir,item);
420 switch (type) {
421 case '.png':
422 imgList.png.push(filePath);
423 break;
424
425 case '.svg':
426 imgList.svg.push(filePath);
427 break;
428 };
429 });
430 return imgList;
431 }
432
433};
434
435module.exports = OutSprite;