New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

easy-sprite

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

easy-sprite - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

lib/parser.js

2

index.js
/**
* @fileoverview Exports sprite api。
* @fileoverview Exports sprite api.
* @author vinnyguitar@126.com
*/
module.exports = require('./lib/sprite.js').sprite;
/**
* @fileoverview This file is used for read read sprite comment from css file and change the background image。
* @fileoverview This file offers method to create sprite image.
* @author vinnyguitar@126.com
*/
'use strict';
var css = require('css'),
fs = require('fs'),
url = require('url'),
var fs = require('fs'),
async = require('async'),
lwip = require('lwip'),
path = require('path'),
extend = require('extend'),
generator = require('./generator.js'),
q = require('q');
var SPRITE_DEF_REG = /@sprite:\s*\[(.+)\]\s*=>\s*(.+\.png)\??(.*)/;
var PARAM_REG = /^(alg|selector)\s*=\s*(.+)/;
var BG_URL_REG = /url\((.+)\)/;
var PNG2X_REG = generator.PNG2X_REG;
var DEFAULT_MARGIN = 10;
var MEDIA2X = 'only screen and (-webkit-min-device-pixel-ratio: 2),'
+'only screen and (min--moz-device-pixel-ratio: 2),'
+'only screen and (-o-min-device-pixel-ratio: 2/1),'
+'only screen and (min-device-pixel-ratio: 2),'
+'only screen and ( min-resolution: 192dpi),'
+'only screen and (min-resolution: 2dppx)';
var PNG_REG = /\.png$/;
var PNG2X_REG = /(@|-|_)2x\.png$/;
/**
* Collect sprite definition from css rules.
* @param {Array} rules Css rules.
* @return {Array}
* @enum Sprite image package algorithms.
* @type {{BINARY: number, VERTICAL: number, HORIZONTAL: number}}
*/
function collectSpriteDef(rules) {
var result = [];
rules.forEach(function(rule) {
if(rule.type == 'comment' && SPRITE_DEF_REG.test(rule.comment.replace(/['"]/g, ''))) {
var src = RegExp.$1,
target = RegExp.$2,
params = RegExp.$3;
var item = {
rule: rule,
src: src.replace(/\s/, '').split(',')
};
if(PNG2X_REG.test(target)) {
item.target = target.replace(PNG2X_REG, '.png');
item.target2x = target;
var Algorithm = {
BINARY: 1,
VERTICAL: 2,
HORIZONTAL: 3
};
/**
* Fix odd pixel to even.
*/
function fixToEven(pixel) {
return pixel % 2 != 0 ? pixel + 1 : pixel;
};
/**
* Comparator by width.
*/
function byWidth(image1, image2) {
return image2.width - image1.width;
};
/**
* Comparator by height.
*/
function byHeight(image1, image2) {
return image2.height - image1.height;
};
/**
* Read png data from files.
* @param {Array} files Files to read.
* @returns {Promise}
*/
function readPngs(files) {
var promises,
i = 0;
while(i < files.length) {// Read all png files recursively.
var filepath = files[i],
stat = fs.statSync(filepath);
if(stat.isFile(filepath) && PNG_REG.test(filepath)) {
i++;
}else{
files.splice(i, 1);
if(stat.isDirectory()) {
Array.prototype.push.apply(files, fs.readdirSync(filepath).map(function(name) {
return path.resolve(filepath, name);
}));
}
}
}
promises = files.map(function(filepath) {
var deferred = q.defer();
lwip.open(filepath, function(err, image) {
if(err) {
deferred.reject('Open ' + filepath + ' error.');
} else {
item.target = target;
}
if(params) {
var arr = params.split('&');
arr.forEach(function(param) {
if(PARAM_REG.test(param)) {
item[RegExp.$1] = RegExp.$2.trim();
}
deferred.resolve({
file: filepath,
image: image,
width: image.width(),
height: image.height()
});
if(item.alg == 'b' || item.alg == 'binary') {
item.alg = generator.Algorithm.BINARY;
} else if(item.alg == 'v' || item.alg == 'vertical') {
item.alg = generator.Algorithm.VERTICAL;
} else if(item.alg == 'h' || item.alg == 'horizontal') {
item.alg = generator.Algorithm.HORIZONTAL;
}
}
result.push(item);
}
});
return deferred.promise;
});
return result;
return q.all(promises);
};
/**
* Parse css and generate sprite.
* @param options
* @config src Src css file.
* @config dest Output file of parsed css.
* @config spriteDir Output directory for sprite image.
* @config margin Margin between image.
* @returns {*}
* Arrange images using a binary tree algorithm.
* @param {Array} Images to arrange.
* @param {Number} margin Margin between image.
* @returns {{width: Number, height: Number}}
*/
function sprite(options, callback) {
var token = css.parse(fs.readFileSync(options.src).toString('utf8')),
rules = token.stylesheet.rules,
srcDir = path.dirname(options.src),
defList = collectSpriteDef(rules),
defMap = {},
bgImgToSpriteMap = {},
selectorMap = {},
bgMap = {};
if(typeof callback !== 'function') callback = function(){};
//generate sprite image for all def.
var promises = defList.map(function(def) {
//To absolute path.
def.src = def.src.map(formatPath);
def.target = path.resolve(options.spriteDir, def.target);
if(def.target2x) {
def.target2x = path.resolve(options.spriteDir, def.target2x);
}
defMap[def.target] = def;
function binaryArrange(images, margin) {
var queue = images.slice(0),
width = 0,
margin = margin,
maxWidth = 0,
height = Number.MAX_VALUE;
queue.sort(byWidth);
queue.forEach(function(image) {
image.width = fixToEven(image.width) + margin;
image.height = fixToEven(image.height) + margin;
maxWidth = Math.max(maxWidth, image.width);
});
return generator.generateSprite(
def,
options.margin || DEFAULT_MARGIN)
.then(function(mapping) {
extend(bgImgToSpriteMap, mapping);
});
fillRectangle(0, 0, maxWidth * 2, height);
width = 0;
height = 0;
images.forEach(function(image) {
image.width -= margin;
image.height -= margin;
width = Math.max(width, image.x + image.width);
height = Math.max(height, image.y + image.height);
});
return {
width: width,
height: height
};
/////////////////////////////////
function fillRectangle(x1, y1, x2, y2) {
var image = getImage(x2 - x1, y2 - y1);
if(!image) return;
image.x = x1;
image.y = y1;
fillRectangle(fixToEven(x1 + image.width), fixToEven(y1), x2, y1 + image.height);
return fillRectangle(fixToEven(x1), fixToEven(y1) + image.height, x2, y2);
};
return q.all(promises)
.then(function() {
var spriteMap = {};
//Update background position for the rule that use sprite.
rules.forEach(function(rule) {
if(rule.type == 'rule') {
var sprite, selectors,
dec = getBgDec(rule),
key = dec && path.resolve(srcDir, dec.url);
if(sprite = bgImgToSpriteMap[key]) {//use sprite
spriteMap[sprite.sprite] = sprite;
dec.dec.property = 'background-position';
if(sprite.sprite2x) {
dec.dec.value = bgPosition(sprite.x / 2, sprite.y / 2);
function getImage(widthLimit, heightLimit) {
for(var i = 0; i < queue.length; i++) {
var image = queue[i];
if(image.width <= widthLimit && image.height <= heightLimit){
queue.splice(i, 1);
return image;
}
}
};
};
/**
* Arrange images vertically.
* @param {Array} Images to arrange.
* @param {Number} margin Margin between image.
* @returns {{width: Number, height: Number}}
*/
function verticalArrange(images, margin) {
var width = 0,
height = 0,
queue = images.slice(0);
queue.sort(byWidth);
queue.forEach(function(image) {
image.width = fixToEven(image.width);
image.height = fixToEven(image.height);
image.x = 0;
image.y = height;
width = Math.max(image.width, width);
height = height + image.height + margin;
});
return {
width: width,
height: height - margin
}
};
/**
* Arrange images horizontally.
* @param {Array} Images to arrange.
* @param {Number} margin Margin between image.
* @returns {{width: Number, height: Number}}
*/
function horizontalArrange(images, margin) {
var width = 0,
height = 0,
queue = images.slice(0);
queue.sort(byHeight);
queue.forEach(function(image) {
image.width = fixToEven(image.width);
image.height = fixToEven(image.height);
image.x = width;
image.y = 0;
height = Math.max(image.height, height);
width = width + image.width + margin;
});
return {
width: width - margin,
height: height
}
};
/**
* Create a sprite image.
* @param {Object} def Definition of sprite.
* @param {Number} margin Margin between image.
* @return {Promise}
*/
function create(def, margin) {
return readPngs(def.src)
.then(function(images) {
var size,
deferred = q.defer();
switch(def.alg) {
case Algorithm.HORIZONTAL:
size = horizontalArrange(images, margin);
break;
case Algorithm.VERTICAL:
size = verticalArrange(images, margin);
break;
default:
size = binaryArrange(images, margin);
break;
}
async.waterfall([
function(cb) {
lwip.create(size.width, size.height, cb);
},
function(sprite, cb) {
var result = {},
batch = sprite.batch();
images.forEach(function(item) {
batch.paste(item.x, item.y, item.image);
result[item.file] = {
sprite: def.target,
sprite2x: def.target2x,
width: item.width,
height: item.height,
x: item.x,
y: item.y,
spriteWidth: size.width,
spriteHeight: size.height
};
});
batch.writeFile(def.target2x || def.target, function(err) {
if(def.target2x) {
sprite.batch()
.scale(0.5, 0.5)
.writeFile(def.target, function(err) {
cb(err, result);
});
} else {
dec.dec.value = bgPosition(sprite.x, sprite.y);
cb(err, result);
}
if(!defMap[sprite.sprite].selector) {//Not use specific selector invoke background.
if(selectors = bgMap[sprite.sprite]) {
Array.prototype.push.apply(selectors, rule.selectors);
} else {
selectors = rule.selectors.slice(0);
}
bgMap[sprite.sprite] = selectors;
}
} else {
rule.selectors.forEach(function(selector) {
selectorMap[selector] = rule;
});
}
}
});
var rule2x = [],
media2x = {
type: 'media',
media: MEDIA2X,
parent: token
};
defList.forEach(function(def) {
var rule, selectors,
sprite = spriteMap[def.target],
bgSize2x = sprite.spriteWidth / 2 + 'px ' + sprite.spriteHeight / 2 + 'px ';
if(def.target2x) {
var r2x = {
parent: media2x,
type: 'rule',
declarations: []
};
r2x.declarations.push({
parent: r2x,
type: 'declaration',
property: 'background-image',
value: bgUrl(def.target2x)
});
r2x.declarations.push({
parent:r2x,
type: 'declaration',
property: 'background-size',
value: bgSize2x
});
rule2x.push(r2x);
}
if(def.selector && (rule = selectorMap[def.selector])) {
rule.declarations.push({
parent: rule,
property: 'background',
type: 'declaration',
value: bgUrl(def.target)
});
r2x && (r2x.selectors = [def.selector]);
rules.splice(rules.indexOf(def.rule), 1);
} else if(selectors = bgMap[def.target]) {
def.rule.type = 'rule';
def.rule.comment = '';
def.rule.selectors = selectors;
def.rule.declarations = [{
parent: def.rule,
type: 'declaration',
property: 'background',
value: bgUrl(def.target)
}];
r2x && (r2x.selectors = selectors);
], function(err, result) {
if(err) {
deferred.reject(err);
} else {
rules.splice(rules.indexOf(def.rule), 1);
deferred.resolve(result);
}
});
if(rule2x.length) {
media2x.rules = rule2x;
rules.push(media2x);
}
fs.writeFileSync(options.dest, css.stringify(token));
callback();
return deferred.promise;
}, function(err) {
callback(err);
console.error('Some error occur when read png:', err);
});
/////////////////////////
function formatPath(name) {
return path.resolve(srcDir, name);
};
function bgPosition(x, y) {
return (x > 0 ? -x + 'px': 0) + ' ' + (y > 0 ? -y + 'px': 0);
};
function bgUrl(target) {
return 'url('+path.relative(srcDir, target).split(path.sep).join('/')+')';
};
function getBgDec(rule) {
for(var dec, i = 0; i < rule.declarations.length; i++) {
dec = rule.declarations[i];
if(dec.type == 'declaration'
&& (dec.property == 'background' || dec.property=='background-image')
&& BG_URL_REG.test(dec.value)) {
return {
url: RegExp.$1.replace(/['"]/g, ''),
dec: dec
};
}
}
};
};
module.exports.collectSpriteDef = collectSpriteDef;
module.exports.sprite = sprite;
module.exports.Algorithm = Algorithm;
module.exports.PNG2X_REG = PNG2X_REG;
module.exports.fixToEven = fixToEven;
module.exports.readPngs = readPngs;
module.exports.horizontalArrange = horizontalArrange;
module.exports.verticalArrange = verticalArrange;
module.exports.binaryArrange = binaryArrange;
module.exports.create = create;
{
"name": "easy-sprite",
"version": "0.0.1",
"version": "0.0.2",
"description": "A tool that can create spirte image for css.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -1,5 +0,5 @@

#easy-sprite
# easy-sprite
easy-sprite是一个简洁的css雪碧图制作工具。它能根据指定规则,将小图片合并为雪碧图。easy-sprite采用css注释指定雪碧图合并规则,能够灵活的满足多种需求。支持自动生成低倍率图片,轻松搞定屏幕适配。
##安装
## 安装
```

@@ -9,3 +9,3 @@ npm install ease-sprite --save

##使用
## 使用
```

@@ -15,9 +15,11 @@ var sprite = require('ease-sprite');

```
##配置说明
+ **src:**需要处理的css文件。
+ **dest:**css输出文件名。
+ **spriteDir:**雪碧图输出目录。
+ **margin:**雪碧图排列的最小间距离。
## 配置说明
+ **src:**需要处理的css文件。
+ **dest:**css输出文件名。
+ **spriteDir:**雪碧图输出目录。
+ **margin:**雪碧图排列的最小间距离,默认10px。
+ **compress:**输出css文件是否需要压缩,默认false。
+ **sourcemap:**是否需要输出sourcemap,默认false。
##雪碧图声明
## 雪碧图声明
雪碧图的声明采用css注释。格式为:/\* @sprite:[雪碧图源文件(夹)]=>雪碧图输出文件?selector=背景图调用选择器&alg=图片排列算法 \*/。

@@ -27,3 +29,3 @@

##声明示例
## 声明示例

@@ -39,3 +41,3 @@ ```

```
sprite处理后的结果为:
sprite 处理后的结果为:

@@ -52,6 +54,6 @@ ```

##关于二倍图
## 关于二倍图
为了适配高分屏,通常需要做两套大小的图片。有了sprite工具,则只需要提供一套大图,sprite工具会压缩出一套小图,并通过media query来决定实际用什么图。支持二倍图非常简单,只需将雪碧图输出文件的命名定为@2x.png、-2x.png或_2x.png,工具在识别出这类雪碧图输出后就会自动生成对应的一倍图和media queery等。
##License
## License
MIT:[http://vinnyguitar.mit-license.org]()

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc