UNPKG

13.5 kBJavaScriptView Raw
1'use strict';
2
3const jdfUtils = require('jdf-utils');
4const $ = jdfUtils.base;
5const f = jdfUtils.file;
6const jdf = require('./jdf');
7const path = require('path');
8const Images = require('jdf-css-sprite');
9const logger = require('jdf-log');
10const vfs = require('./VFS/VirtualFileSystem');
11
12const cssSprite = module.exports = {};
13
14cssSprite.init = function() {
15 logger.profile('css sprite');
16 setTragetPath();
17 logger.verbose(`cssSpriteMode: ${jdf.config.output.cssSpriteMode}`);
18 if(jdf.config.output.cssSpriteMode == 0){
19 cssSprite.overall(jdf.config.outputDirName);
20 }
21
22 if(jdf.config.output.cssSpriteMode == 1){
23 // 获取现在要处理的所有css文件
24 vfs.queryFileByTargetType('css').forEach((item) => {
25 item.targetContent = cssSprite.scatter(item.buildPath, item.targetContent, jdf.config.outputDirName);
26 delete item.buildPath;
27 });
28 }
29 logger.profile('css sprite');
30}
31
32function setTragetPath() {
33 // 先把所有文件的targetPath设置一下
34 const cwd = process.cwd();
35 vfs.queryFileByTargetType('css').forEach((file) => {
36 let rp = $.pathJoin(path.relative(cwd, file.targetPath));
37 file.buildPath = rp;
38 logger.verbose(`css sprite: ${rp}`);
39 });
40}
41
42/**
43 * @overall
44 * @param {String} source css路径
45 * @param {String} content css源码内容
46 * @example
47 background:#ffd4ae url(i/i-arrws.png?__sprite) no-repeat;
48 -->
49 background:#ffd4ae url(i/sptire_menu.png?__sprite) no-repeat;background-position:12px 10px;
50 */
51cssSprite.overall = function(source){
52 var reg_background = /background(?:-image)?:([\s\S]*?)(?:;|$)/gi;
53 var reg_img_url = /url\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}]+)\s*\)/i;
54 var reg_position = /(0|[+-]?(?:\d*\.|)\d+px|left|right)\s*(0|[+-]?(?:\d*\.|)\d+px|top)/i;
55 var reg_repeat = /\brepeat-(x|y)/i;
56 var reg_is_sprite = /[?&]__sprite/i;
57
58
59 var result = [];
60 var resultNew = [];
61
62 var maxWidth = 0;
63 var heightTotal = 0;
64 var maxHeight = 0;
65 var widthTotal = 0;
66
67 var margin = jdf.config.output.cssSpriteMargin;//高度阈值
68 var cssSpriteDirection = jdf.config.output.cssSpriteDirection;
69
70 var background = [];
71 const projectPath = jdf.getProjectPath();
72 const cssFiles = vfs.queryFileByTargetType('css');
73
74 cssFiles.forEach((file) => {
75 const content = file.targetContent;
76 const cssBg = content.match(reg_background);
77 const dir = path.dirname(file.buildPath);
78
79 if(cssBg){
80 cssBg.forEach((bg) => {
81 background.push({bg, dir});
82 });
83 }
84 });
85
86 if(background){
87 var resultTemp = {};
88 background.forEach(function(item){
89 var img_url = item.bg.match(reg_img_url);
90
91 if(img_url && reg_is_sprite.test(img_url[0]) && !$.is.httpLink(item)){
92 var res = {
93 content:null,
94 url:null,
95 position:null,
96 repeat:null
97 };
98
99 var url = img_url[0].replace(/url\(|\)|\'|\"/gi,'');
100
101 res.urlOrigin = url;
102 url = url.replace(reg_is_sprite, '');
103
104 var sprite_rem = img_url[0].match(new RegExp('__rem\\d+', 'g'));
105 if(sprite_rem){
106 res.rem = parseFloat(sprite_rem[0].replace('__rem', ''));
107 url = url.replace(/__rem\d+/g, '');
108 }
109
110 res.url = path.join(item.dir, url);
111 const realFilePath = path.relative(path.join(jdf.config.outputDirName, projectPath), res.url);
112 logger.verbose(`sprite file path: ${realFilePath}`);
113 if(f.exists(realFilePath)){
114 res.content = Images(realFilePath);
115
116 //去重
117 if(!resultTemp[res.url]){
118 res.item = item.bg;
119 res.width = res.content.size().width;
120 res.height = res.content.size().height;
121
122 if(cssSpriteDirection == 'vertical'){
123 if(res.width>maxWidth){
124 maxWidth = res.width;
125 }
126 heightTotal += res.height + margin;
127 }else{
128 if(res.height>maxHeight){
129 maxHeight = res.height;
130 }
131 widthTotal += res.width + margin;
132 }
133
134 if(item.bg.match(reg_position)){
135 res.position = item.bg.match(reg_position)[0];
136 }
137 if(item.bg.match(reg_repeat)){
138 res.repeat = item.bg.match(reg_repeat)[0];
139 }
140 result.push(res);
141 }
142 resultTemp[res.url] = res.url;
143
144 }else{
145 logger.warn(res.url + ' may be not exist.');
146 }
147 }
148 });
149 }
150
151 if(result.length>0){
152
153 var outputName = jdf.getProjectPath().replace(/\//g, '_');
154 var outputExtname = '.png';
155
156 var imagesOutput = null;
157 if(cssSpriteDirection == 'vertical'){
158 imagesOutput = Images(maxWidth, heightTotal);
159 }else{
160 imagesOutput = Images(widthTotal, maxHeight);
161 }
162
163 var w = 0;
164 var h = 0;
165 result.forEach(function(item, i){
166 if(!item.repeat){
167 var x = 0, y = h, y2 = -h;
168
169 if(cssSpriteDirection == 'vertical'){
170 var x = 0, y = h, y2 = -h;
171 if(item.position){
172 var position = item.position.split(' ');
173 var x1 = parseInt(position[0], 10)
174 if(x1){
175 x += x1;
176 }
177 var y1 = parseInt(position[1], 10)
178 if(y1){
179 y += y1;
180 y2 -= y1;
181 }
182 }
183
184 //画小图片
185 imagesOutput.draw(item.content, 0, y);
186 }else{
187 var y = 0, x = w, x2 = -w;
188 if(item.position){
189 var position = item.position.split(' ');
190 var x1 = parseInt(position[0], 10)
191 if(x1){
192 x += x1;
193 x2 -= x1;
194 }
195 var y1 = parseInt(position[1], 10)
196 if(y1){
197 y += y1;
198 }
199 }
200
201 //画小图片
202 imagesOutput.draw(item.content, x, 0);
203 }
204
205 var urlOrigin = item.urlOrigin;
206
207 //将需要合并的图片名称替换为合并后的新图片名称
208 var urlOriginNew = urlOrigin.replace(path.basename(item.url, path.extname(item.url)), outputName);
209
210 //将图片的存放目录统一替换为css/i
211 urlOriginNew = urlOriginNew.replace(path.dirname(urlOriginNew), '/css/i');
212
213 var backgroundNew = item.item.replace(urlOrigin, urlOriginNew).replace(reg_is_sprite,'').replace(/__rem\d+/, '');
214 if(cssSpriteDirection == 'vertical'){
215 if(item.rem){
216 backgroundNew += 'background-position:'+(x/item.rem)+'rem '+(y2/item.rem)+'rem;';
217 backgroundNew += 'background-size:' + (maxWidth/item.rem) + 'rem ' + (heightTotal/item.rem) + 'rem;';
218 }else{
219 backgroundNew += 'background-position:'+x+'px '+y2+'px;';
220 }
221 }else{
222 if(item.rem){
223 backgroundNew += 'background-position:'+(x2/item.rem)+'rem '+(y/item.rem)+'rem;';
224 backgroundNew += 'background-size:' + (maxWidth/item.rem) + 'rem ' + (heightTotal/item.rem) + 'rem;';
225 }else{
226 backgroundNew += 'background-position:'+x2+'px '+y+'px;';
227 }
228 }
229
230 item['backgroundNew'] = backgroundNew;
231 resultNew.push(item);
232
233 if(cssSpriteDirection == 'vertical'){
234 h = h + item.height + margin;
235 }else{
236 w = w + item.width + margin;
237 }
238 }
239 });
240
241 cssFiles.forEach((file) => {
242 let content = escape(file.targetContent);
243 const dir = path.dirname(file.buildPath);
244
245 resultNew.forEach((item) =>{
246 //替换css中的图片路径
247 let url = item.urlOrigin.replace('?__sprite', '').replace(/__rem\d+/gi, '');
248 url = path.join(dir, url);
249
250 if(url === item.url){
251 content = content.replace(new RegExp(escape(item.item), 'gi'), escape(item.backgroundNew));
252 }
253 });
254
255 file.targetContent = unescape(content);
256 delete file.buildPath;
257 });
258
259 //保存合并完成的cssSprite图片
260 const originPath = path.join('css', 'i', outputName + outputExtname);
261 const originContent = imagesOutput.encode(outputExtname);
262 logger.verbose(`sprite new file: ${originPath}`);
263 vfs.addFile(path.resolve(originPath), originContent);
264 }
265}
266
267cssSprite.scatter = function(source, content, sourceOrigin){
268 var reg_background = /background(?:-image)?:([\s\S]*?)(?:;|$)/gi;
269 var reg_img_url = /url\s*\(\s*("(?:[^\\"\r\n\f]|\\[\s\S])*"|'(?:[^\\'\n\r\f]|\\[\s\S])*'|[^)}]+)\s*\)/i;
270 var reg_position = /(0|[+-]?(?:\d*\.|)\d+px|left|right)\s*(0|[+-]?(?:\d*\.|)\d+px|top)/i;
271 var reg_repeat = /\brepeat-(x|y)/i;
272 var reg_is_sprite = /[?&]__sprite/i;
273
274 var background = content.match(reg_background);
275 var result = [];
276
277 var maxWidth = 0;
278 var heightTotal = 0;
279 var maxHeight = 0;
280 var widthTotal = 0;
281
282 var margin = jdf.config.output.cssSpriteMargin;//高度阈值
283 var cssSpriteDirection = jdf.config.output.cssSpriteDirection;
284 const projectPath = jdf.getProjectPath();
285
286 if (background) {
287 var resultTemp = {};
288 background.forEach(function(item){
289 var img_url = item.match(reg_img_url);
290 if(img_url && reg_is_sprite.test(img_url[0]) && !$.is.httpLink(item)){
291 var res = {
292 content:null,
293 url:null,
294 position:null,
295 repeat:null
296 };
297
298 var url = img_url[0].replace(/url\(|\)|\'|\"/gi,'');
299 res.urlOrigin = url;
300 url = url.replace(reg_is_sprite, '');
301
302 var sprite_rem = img_url[0].match(new RegExp('__rem\\d+', 'g'));
303 if(sprite_rem){
304 res.rem = parseFloat(sprite_rem[0].replace('__rem', ''));
305 url = url.replace(/__rem\d+/g, '');
306 }
307
308 if(/^\/\w/.test(url)){
309 res.url = path.join(jdf.config.outputDirName, jdf.getProjectPath(), url)
310 }else{
311 res.url = path.join(path.dirname(source),url);
312 }
313 //此处由于生成指向的路径并没有文件,将其转化成文件的实际路径
314 // build/product/index/1.0.0/widget/about/i/i-intro.png
315 // ---->
316 // widget/about/i/i-intro.png
317 const realFilePath = path.relative(path.join(jdf.config.outputDirName, projectPath), res.url);
318 logger.verbose(`sprite file path: ${realFilePath}`);
319
320 if(f.exists(realFilePath)){
321 res.content = Images(realFilePath);
322
323 //去重
324 if(!resultTemp[url]){
325 res.item = item;
326 res.width = res.content.size().width;
327 res.height = res.content.size().height;
328
329 if(cssSpriteDirection == 'vertical'){
330 if(res.width>maxWidth){
331 maxWidth = res.width;
332 }
333 heightTotal += res.height + margin;
334 }else{
335 if(res.height>maxHeight){
336 maxHeight = res.height;
337 }
338 widthTotal += res.width + margin;
339 }
340
341 if(item.match(reg_position)){
342 res.position = item.match(reg_position)[0];
343 }
344 if(item.match(reg_repeat)){
345 res.repeat = item.match(reg_repeat)[0];
346 }
347 result.push(res);
348 }
349 resultTemp[url] = url;
350 }else{
351 logger.error(`${res.url} is not exist`);
352 }
353 }
354 });
355 }
356
357 if(result.length>0){
358
359 var outputName = path.basename(source, path.extname(source));
360 var outputExtname = '.png';
361
362 var imagesOutput = null;
363 if(cssSpriteDirection == 'vertical'){
364 imagesOutput = Images(maxWidth, heightTotal);
365 }else{
366 imagesOutput = Images(widthTotal, maxHeight);
367 }
368
369 var w = 0;
370 var h = 0;
371 result.forEach(function(item, i){
372 if(!item.repeat){
373 outputExtname = path.extname(item.url);
374
375 if(cssSpriteDirection == 'vertical'){
376 var x = 0, y = h, y2 = -h;
377 if(item.position){
378 var position = item.position.split(' ');
379 var x1 = parseInt(position[0], 10)
380 if(x1){
381 x += x1;
382 }
383 var y1 = parseInt(position[1], 10)
384 if(y1){
385 y += y1;
386 y2 -= y1;
387 }
388 }
389
390 //画小图片
391 imagesOutput.draw(item.content, 0, y);
392 }else{
393 var y = 0, x = w, x2 = -w;
394 if(item.position){
395 var position = item.position.split(' ');
396 var x1 = parseInt(position[0], 10)
397 if(x1){
398 x += x1;
399 x2 -= x1;
400 }
401 var y1 = parseInt(position[1], 10)
402 if(y1){
403 y += y1;
404 }
405 }
406
407 //画小图片
408 imagesOutput.draw(item.content, x, 0);
409 }
410
411 //样式替换
412 var urlOrigin = item.urlOrigin;
413 var urlOriginNew = urlOrigin.replace(path.basename(item.url, path.extname(item.url)) , outputName);
414 urlOriginNew = urlOriginNew.replace(/^\//, '');
415
416 urlOriginNew = urlOriginNew.replace(/\.\/|\.\.\//gi, '');
417
418 var backgroundNew = item.item.replace(urlOrigin, urlOriginNew).replace(reg_is_sprite, '').replace(/__rem\d+/, '');
419
420 if(cssSpriteDirection == 'vertical'){
421 if(item.rem){
422 backgroundNew += 'background-position:'+(x/item.rem)+'rem '+(y2/item.rem)+'rem;';
423 backgroundNew += 'background-size:' + (maxWidth/item.rem) + 'rem ' + (heightTotal/item.rem) + 'rem;';
424 }else{
425 backgroundNew += 'background-position:'+x+'px '+y2+'px;';
426 }
427 }else{
428 if(item.rem){
429 backgroundNew += 'background-position:'+(x2/item.rem)+'rem '+(y/item.rem)+'rem;';
430 backgroundNew += 'background-size:' + (maxWidth/item.rem) + 'rem ' + (heightTotal/item.rem) + 'rem;';
431 }else{
432 backgroundNew += 'background-position:'+x2+'px '+y+'px;';
433 }
434 }
435
436 //替换图片路径
437 content = escape(content);
438 content = content.replace(new RegExp(escape(item.item), 'gi'), escape(backgroundNew));
439 content = unescape(content);
440
441 if(cssSpriteDirection == 'vertical'){
442 h = h + item.height + margin;
443 }else{
444 w = w + item.width + margin;
445 }
446 }
447 });
448
449 // 合成大图
450 let originPath = path.resolve(path.dirname(source), 'i', outputName + outputExtname);
451 originPath = path.relative(path.join(jdf.config.outputDirName, projectPath), originPath);
452 const originContent = imagesOutput.encode(outputExtname);
453 logger.verbose(`sprite new file: ${originPath}`);
454 vfs.addFile(path.resolve(originPath), originContent);
455 }
456 return content;
457}