针对小游戏中PNG、JPG压缩工具优化(nodejs)

-前言-

博主目前还是主力使用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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章