nodejs打包工具,node能做什么 图
本文将教大家如何使用node编写一个atlas打包工具,有一定的参考价值,希望对大家有所帮助!
node.js速度课程简介:进入学习
偶然发现了一个非常有用的跨平台图像编解码库节点——images。
仔细看了它的API,一个用它做精灵图集的想法产生了。
于是这个工具sprites-pack-tool就诞生了。
精灵图集我想大家都很熟悉。
比如把下面的几张图片合成一张.
这个图集是通过本文介绍的工具打包和合成的。
合成图的质量还是很高的。
为什么需要使用图集
web开发
在web开发中,我们需要在浏览器中每显示一张图片就请求一次服务器资源。
比如3次请求每次4k和一次请求12k有本质区别,更多时候一个请求不是3 * 4k。
使用atlas使我们能够优化资源加载并提高网站的性能。
游戏开发
在游戏开发中,图集的使用非常重要。无论是一般的帧动画还是svga等动画解决方案,每张图片都不会请求资源。
更多的时候,我们打包成atlas,atlas打包工具texturepacker很受欢迎。
其次,游戏场景太多,通常需要一步步加载资源。有时候,一个动画模型少则十几张,多则近百张。
图集的使用不可或缺.
让我们来看看如何编写一个atlas打包工具。
工具设计
开发一个atlas打包工具脚本需要哪些技能?
node.js的编程能力
二维矩形装箱算法
然后我们思考如何包装一本地图集。
我们需要找到需要打包的文件夹。可能有多个或嵌套的文件夹。
图集是几幅零散图片的组合。
atlas的大小需要是可配置的。
尽量压缩图集空间,让每张图片紧密贴合。
每个文件夹打包成一个图集,需要考虑图片太多的情况。
可能需要生成atlas需要的json文件来记录图片的位置信息。
这是我的设计。
首先我们需要一个打包的对象实例MySpritePackTool,并且支持写配置参数选项。
/* *图集打包对象*/
const MySpritePackTool=function(opt){
this.options={
//图片太多或太长的文件夹的最大递归数
maxCount: opt.maxCount 2,
//要打包的图集的文件路径
assetsPath: opt.assetsPath,
//输出文件路径
outPutPath: opt.outPutPath,
//图集的最大包装尺寸
maxSize: {宽度:2048,高度:2048 }
}
};然后我们需要输出这个对象,这个对象可以被其他项目引用。
module . exports=MySpritePackTool;
开始编写脚本
我们的输入参数越少越好,这就要求我们的程序要遍历文件夹。
例如,我们有以下目录树:
-资产
-索引
-img-3.png
-img-4.png
-登录
-img-5.png
-img-1.png
img-2.png我们需要在每个文件夹下放一本地图册。
思考: 需要什么样的数据结构?
首先,为了便于js解析,我们商定了一个对象,
每一层都需要一个图片信息容器资产;
包含图片识别密钥;
一个文件夹名也方便我们以后给图集命名;
然后在每一层文件夹前设置相同的对象;
结构如下:
{
资产:[
{
id: assets/img-1.png ,
宽度:190,
身高:187
},
.
],
名称:资产,
关键字: img-1.png,img-2.png,,
索引:{
资产:[
{
id: assets/index/img-3.png ,
宽度:190,
身高:187
},
.
],
名称:“索引”,
关键字:“img-3.png,img-4.png,”
},
登录:{
资产:[
{
id:资产/登录/img-5.png ,
宽度:190,
身高:187
}
],
名称:“索引”,
关键字:“img-5.png”
},
不难发现,我们已经可以获得所有需要打包的文件和文件夹。
那么用程序如何实现呢?
Nodejsfs模块主要用于递归操作文件夹,输出所需的节点树。
写的时候注意是图片还是文件夹。
myspritepacktool . prototype . find all files=function(obj,rootPath) {
let node files=[];
if (fs.existsSync(rootPath)) {
//获取所有文件名
node files=fs . readdirsync(root path);
//组装对象
let name arr=root path . split(/);
obj[ assets ]=[];
obj[ name ]=name arr[name arr . length-1];
obj[ keys ]= ;
nodeFiles.forEach(item={
//确定它不是图片路径
如果(!/(.png)(。jpe?g)$/。测试(项目)){
let newPath=path.join(rootPath,item);
//确定现有文件同时是文件夹系统。
if(fs . exists sync(new path)fs . statsync(new path)。isDirectory()) {
//console.log(获取新地址,new path);
obj[item]={ };
this.findAllFiles(obj[item],new path);
}否则{
Console.log(`文件路径:${newPath}不存在!`);
}
}否则{
console . log(` Image path:$ { item } `);
obj[keys]=item ,;
设params={ };
params[ id ]=path . resolve(root path,`)。/$ { item } `);
//获取图片的宽度和高度
params[ width ]=images(path . resolve(root path,`。/${item} `))。width();
params[ height ]=images(path . resolve(root path,`。/${item} `))。height();
资产。push(参数);
}
})
}否则{
Console.log(`文件路径:${rootPath}不存在!`);
}
这样就可以得到我们需要的节点树。
脚本IO
我们已经完成了对文件夹的操作,接下来需要思考。
如何把这些零散的图片打包成一张图?
一个图有两条信息,宽度和高度,实际上是一个长方形。
我们现在要做的就是把这些面积不同的矩形,放到一个长宽最大的大矩形里。
跳开图片, 从矩形放置入手
二维矩形装箱算法有很多,这里我选一个比较简单的。
先弄一个最大长宽的矩形盒子的。
我们先放入一个矩形A,这样剩下的区域就有两块:矩形A的右边和矩形A的下边。
然后我们继续放入矩形B,可以右后下,然后就有了基于矩形b的两块空白空间.
以此类推,我们可以把所有合适的矩形都放进去。
举个例子
将左边的大矩形放入右边的矩形框中,得到:
如你所见,我们节省了大量空间,矩形布局非常紧凑。
如果用代码实现, 是怎么样的呢?
/**
*确定宽度和高度w h
*先在空白区域放一个,剩下的找右边和底部。
*如果右边有,如果有,放在空白处继续遍历。
*下面有没有,有就放进去,然后继续遍历。
*/
const Packer=函数(w,h) {
this.root={ x: 0,y: 0,宽度:w,高度:h };
///* *匹配所有方块*/
packer . prototype . fit=function(blocks){
let节点;
for(设I=0;I块.长度;i ) {
设block=blocks[I];
node=this.findNode(this.root,block.width,block . height);
if(节点){
let fit=this.findEmptyNode(node,block.width,block . height);
block . x=fit . x;
block . y=fit . y;
block.fit=fit
}
}
}
/* *找到可以放入的节点*/
packer . prototype . findnode=function(node,w,h) {
if (node.used) {
return this . findnode(node . right area,w,h) this . findnode(node . down area,w,h);
} else if(node . width=w node . height=h){
返回节点;
}否则{
返回null
}
}
/* *寻找空缺*/
packer . prototype . findemptynode=function(node,w,h) {
//删除已经使用的
node.used=true
//右空格
node.rightArea={
x: node.x w,
y: node.y,
宽度:node.width - w,
高度:高
};
//下部空间
node.downArea={
x: node.x,
y: node.y h,
宽度:节点宽度,
高度:node.height - h
}
返回节点;
}
}使用递归,代码量小,但功能强大。
但是有一个问题,如果超出定长定宽, 或者一个矩形装不完, 我们的算法是不会放入到大矩形中的。
这有点不满意我们对atlas包装的想法。
所以我们还是需要改进这个算法;
添加两个变量,一个记录使用的总的区域,一个记录未被装入的矩形。
//记录使用的总面积
this.usedArea={ width: 0,height: 0 }。
//记录尚未加载的矩形
this . level blocks=[];详细代码可以在源代码包装中查看。
当然, 这里只是最简单的一种二维装箱算法
还有一种加强版的装箱算法, 我放在源码里了, 这里就不赘述了, 原理基本一致
现在,我们可以正确地框出矩形,那么如何使用它来处理成图集呢?
定义一个dealImgsPacking方法并继续处理我们的节点树。
这里,我们使用我们的配置项maxCount,只是为了打印多个图集。
然后我们打包的图集以文件夹 + 当前是第几张的形式命名。
` ${obj[name](计数?-计数: )} `具体方法如下:
myspritepacktool . prototype . dealimgspacking=function(obj){
设count=0;
if(obj . hasownproperty( assets ){
let new blocks=obj[ assets ];
obj[ assets ]=[];
while(new blocks . length 0 count this . options . maxcount){
设Packer 1=new Packer(this . options . maxsize . width,this . options . maxsize . height);
packer 1 . fit(new blocks);
小型张1={
maxArea: packer1.usedArea,
阿特拉斯:新手,
文件名:` ${obj[name](计数?-计数: )}
};
new blocks=packer 1 . level blocks;
资产。推送(sheets 1);
数数;
}
}
for(对象中的字母项){
if (obj[item].hasOwnProperty( assets ){
this . dealimgspacking(obj[item]);
}
}
}通过这个方法,我们对之前的节点树进行了改造;
前一个节点树中的assest变成一个数组,每个数组元素代表一个图集信息。
其结构如下:
资产:[
{
maxArea: {宽度:180,高度:340 },
图集:[
{
id: assets/index/img-3.png ,
宽度:190,
身高:187,
x: 0,
y: 0
}
],
文件名:“资产”},
.
]我们可以清楚的得到,打包后的图集的最大宽度和高度是maxArea,每个图集的宽度和高度位置信息是atlas,图集名称fileName。
接下来,就是最后一步,绘制新的图片,输出图片文件。
遍历文件生成节点树
节点图像的API用于在此绘制和输出图集;
遍历之前得到的节点树,先画一个maxArea大小的空白图。
图像(项目[maxarea])。宽度,项目[maxarea]。height)然后遍历一个图集所需的图片信息,在空白图像上绘制每一张图片。
//绘制空白图像
让news prites=images(item[ maxArea ])。宽度,项目[maxArea]。身高);
//绘制地图集
imgObj.forEach(it={
news plites . draw(images(it[ id ]),it[x],it[ y ]);
});然后画一个图集,输出一个。
newsplites . save(` $ { this . options . output path }/$ { item[ fileName ]} . png `);最后,绘制节点树递归调用的所有图集。
具体代码如下:
myspritepacktool . prototype . draw images=function(obj){
设count=0;
if(obj . hasownproperty( assets ){
//打包一个或多个图集。
let img sinfo=obj[ assets ];
imgsInfo.forEach(item={
if(item . hasownproperty( atlas ){
let imgObj=item[ atlas ];
//console.log(8888 ,imgObj)
//绘制透明图像
让news prites=images(item[ maxArea ])。宽度,项目[maxArea]。身高);
imgObj.forEach(it={
news plites . draw(images(it[ id ]),it[x],it[ y ]);
});
newsplites . save(` $ { this . options . output path }/$ { item[ fileName ]} . png `);
数数;
}
})
}
for(对象中的字母项){
if (obj[item].hasOwnProperty( assets ){
this . draw images(obj[item]);
}
}
}这样,我们就完成了,
运行它,你可以得到下面的图集:
效果还不错。
获取新的图集位置信息
安装
NPI精灵包工具的使用
const MySpritePackTool=require( sprites-pack-tool );
const path=require( path );
/* *打包的最大递归数*/
const MAX _ COUNT=2;
//要合成的地图的路径
const assets path=path . resolve(_ _ dirname,。/assets’);
/* *图集打包工具配置*/
const mySpritePackTool=new mySpritePackTool({
//图片太多或太长的文件夹的最大递归数
maxCount: MAX_COUNT,
//要打包的图集的文件路径
资产路径:资产路径,
//输出文件路径
output path:path . resolve(_ _ dirname,。/res ),
//图集的最大包装尺寸
maxSize: {宽度:2048,高度:2048}
});
/* *图集包装*/
mySpritePackTool。pack 2 sprite();
图集打包并输出
当然,这个工具只是第一版,以后还会继续优化,增加新的功能。
算法可以继续优化,现在有很多空白。
文件夹操作,可以优化。比如写图,可以是每个文件夹里的下一个图集。
添加更多的配置项,比如打开图像压缩。
添加json文件
.
我粗略看了一下,市面上有几款atlas打包工具,要么基于texturePacker,要么基于imagemagick
使用这两个开放的API也可以封装图集,效果质量可能会更好。
但是你必须安装一个额外的应用程序。
同样,你也可以使用webpack的一些加载器或插件。目的是包装图集。
本文介绍的工具是轻量级的,但是它们可以开箱即用。
如何使用
有段时间没写文章了。这个周末无意中想写一个这样的工具,就付诸实践了,结果不错。
如果你有更好的办法,可以留下评论,非常感谢~。
欢迎大家指正。作者功力尚浅。如有不妥,请指正。
文章粗浅, 望诸位不吝您的评论和点赞~
更多关于node的信息,请访问:nodejs教程!以上就是如何使用node开发一个atlas打包工具的细节。请多关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。