-前言-
博主目前还是主力使用Laya进行游戏开发,随着项目扩大,资源量增加,发布愈发的缓慢。究其根本还是问题出在了图片压缩问题上,实在受不了准备将压缩流程剥离出来,在自己想压缩的时候再执行命令压缩也不迟。
-正文-
首先来说一说为什么图片压缩那么慢,图片压缩分为两个步骤:1.PNG压缩,采用了pngquant,改工具在时间空间复杂度都不高。2.JPEG格式压缩,采用了guetzli,这个工具就是让你在压缩时电脑卡得不行的工具,随着压缩的图片越大暂用率越高。
这里就不讨论两个工具的使用了,也不讨论他们的效率,guetzli虽然效率低不过在jpg格式中的压缩率还是很不错,只要我们合适的时候使用也不会影响我们的心情:)
思路
其实思路还是很简单的,读取指定目录下的所有png、jpg然后调用pngquant或者guetzli工具,值得注意的是两个工具都只有单张图片的压缩方式,因此需要不停的调用子进程进行压缩。png是游戏中使用较多的图片格式,jpg一般用于用户头像,游戏log这些应用场景频率较小的地方,更改频率也比较小,因此我们可以将工具分为png压缩、jpg压缩、图片压缩三个命令,视情况选择执行。
使用工具时需要安装下载guetzli,pngquant
1.批处理文件
首先我们定义三个批处理文件,分别处理png压缩任务,jpg压缩任务,图片压缩任务。
//bat jpg压缩
node index.js jpg
@pause
//bat png压缩
node index.js png
@pause
//压缩png jpg
node index.js png jpg
@pause
2.读取指定目录下的所有文件并筛选PNG、JPG文件
//1.首先读取指定目录下的所有文件路径
/**
* 读取指定路径下的所有文件路径并赋值到out中
* @param {string} parentPath
* @param {Array<string>} out
*/
function readAll(parentPath, out) {
try {
let files = fs.readdirSync(parentPath);
files.forEach(function (item) {
let tempPath = path.join(parentPath, item);
let stats = fs.statSync(tempPath);
if (stats.isDirectory()) {
readAll(tempPath, out);
} else {
out.push(tempPath);
}
});
} catch (e) {
console.warn("Path Error:" + e);
return out;
}
}
//2.筛选png,jpg
let allFileList = [];
readAll(root_path, allFileList);
let pngList = [];
let jpgList = [];
for (let i = 0; i < allFileList.length; i++) {
let fileData = path.parse(allFileList[i]);
if (fileData.ext == ".png") {
pngList.push(allFileList[i]);
} else if (fileData.ext == ".jpg" || fileData.ext == ".JPG") {
jpgList.push(allFileList[i]);
}
}
3.根据进程参数执行压缩
之前我们定义的bat分别向nodejs程序传入的执行参数,这时候我们就可以通过参数来指定执行什么函数。
if (process.argv.indexOf(TYPE_PNG) != -1) {
//1.压缩png
compressPNG(pngList, compressCache.PNG, onCompressComplete);
} else {
isPngComplete = true;
}
if (process.argv.indexOf(TYPE_JPG) != -1) {
//2.压缩jpg
compressJPG(jpgList, compressCache.JPG, onCompressComplete);
} else {
isJpgComplete = true;
}
//所有压缩完成回调
function onCompressComplete(type) {
if (type == TYPE_JPG) {
isJpgComplete = true;
console.log("JPG 压缩完成.");
}
if (type == TYPE_PNG) {
isPngComplete = true;
console.log("PNG 压缩完成.")
}
if (isPngComplete && isJpgComplete) {
let encodeCache = JSON.stringify(compressCache);
//回写cache
fs.writeFileSync("./cache/cache.json", encodeCache, "utf-8");
console.log("压缩完成...");
}
}
压缩的时候做了一个小小的优化,通过缓存图片文件的大小及修改时间来判断是否需要压缩文件,对于没有修改的文件就不进行压缩。通过文件名转md5存到cache.json文件中。
4.PNG压缩
/**压缩png */
function compressOnePNG(filePath, completeHandler) {
const exePath = "./lib/pngquant/pngquant.exe";
let param = ['--strip', '--force'];
param.push('--skip-if-larger');
param.push("--quality=" + [pngQualityLow, pngQualityHigh].join("-"));
param.push('--output', filePath);
param.push(filePath);
let childP = child_process.spawn(exePath, param);
childP.stdout.on('data', function (data) {
});
childP.stderr.on('data', function (data) {
});
childP.on("close", function (code) {
let iCode = parseInt(code);
if (iCode != 0) {//error
}
console.log(filePath, "压缩完成");
completeHandler && completeHandler(filePath);
});
}
5.JPG压缩
/**压缩jpg */
function compressOneJPG(url, completeHandler) {
let exePath = "guetzli_" + os.platform() + "_x86";
if (os.arch == "x64") {
exePath += "-64";
}
if ("win32" == os.platform()) {
exePath += ".exe";
}
console.log("开始压缩:",url);
exePath = path.join("./lib/guetzli/", exePath);
var params = ["--quality", jpgQuality];
params.push(url, url);
var childP = child_process.spawn(exePath, params);
childP.stdout.on('data', function (data) {
console.log("stdout:", data);
});
childP.stderr.on('data', function (data) {
console.error("stderr:", data);
console.error("compress fail pic:", url);
});
childP.on('close', function (code) {
console.log("压缩完成:",url)
if (completeHandler != null) {
completeHandler(url);
}
});
}
工具使用说明
1.修改conf目录下的conf.json文件,将root_path配置正确(执行需要压缩图片的目录)
2.运行前执行npm install 命令安装依赖,目前就只用到md5模块。
下载路径:https://download.csdn.net/download/weixin_36719607/12238348