起因
简单的功能,但是遇到微信小程序纹理大小限制2048的问题(小米8实测),如果是小图一张纹理能容纳,但是如果是上百帧的就只能在质量和帧率上妥协?为了避免限制,所以有了一下实践three-sprite-player
如何实现
一张纹理如何分块?使用Texture的自带功能
// 因为小程序THREE需要scope,所以该npm包无three依赖
texture.wrapS = 1001; // THREE.ClampToEdgeWrapping;
texture.wrapT = 1001; // three.ClampToEdgeWrapping;
texture.minFilter = 1006; // THREE.LinearFilter
texture.repeat.set(1 / this.col, 1 / this.row);
if (sRGB) texture.encoding = 3001; // THREE.sRGBEncoding
// 如果颜色不对请检查encoding的设置和图片是否一致
// 一张纹理的tile
const tileHeight = 1 / this.row;
const currentColumn = this.currTileOffset % this.col;
const currentRow = ~~(this.currTileOffset / this.col);
if (texture) {
// 利用texture的offset做偏移实现
texture.offset.x = currentColumn / this.col;
texture.offset.y = 1 - currentRow / this.row - tileHeight;
}
简单理解就是一张纹理内的tile偏移量解析到对应行和列
微信小程序一张纹理不够放,那就使用多张,相同的道理就是在加一层映射帧序号映射到tile,然后再映射到行和列
public animate() {
if (!this.playing || this.totalFrame === 1) return
const now = Date.now();
this.startTime = this.startTime ?? now;
this.startFrame = this.startFrame ?? this.currFrame;
const nextFrame = this.startFrame + ~~((now - this.startTime) / this.frameGap)
this.currFrame = nextFrame % this.totalFrame
if (nextFrame > this.currFrame) {
this.startTime = now
this.startFrame = this.currFrame
}
this.updateOffset()
}
偏移都比较简单,那么这样的帧序列图片如何生成呢?
帧序列图片合成
这里使用Jimp来做图片编辑工具
const main = async (args: Args) => {
const t = Date.now()
// 解析命令行参数
const { dir, tileW, imgs, cropW, cropH, cropX, cropY, imgW, imgH } = await parseArgs(args);
// 计算出每个tile可以容纳的行列
const col = Math.floor(tileW / cropW);
const row = Math.floor(tileW / cropH);
const tileNum = Math.ceil(imgs.length / (col * row));
// 并发
await Promise.all(Array(tileNum).fill(0).map(async (v, t) => {
const tileImg = await Jimp.create(
col * cropW,
row * cropH,
Jimp.rgbaToInt(0, 0, 0, 0),
);
let sum = 0;
const drawSprites = []
for (let r = 0; r < row; r++) {
for (let c = 0; c < col; c++) {
const index = t * row * col + r * col + c;
if (index < imgs.length) {
drawSprites.push((async () => {
const img = await Jimp.create(dir + path.sep + imgs[index]);
img.resize(imgW, imgH);
img.crop(cropX, cropY, cropW, cropH);
tileImg.composite(img, c * cropW, r * cropH);
sum++;
})())
}
}
}
await Promise.all(drawSprites)
console.log(`tile-${t} contains ${sum} sprites`);
await tileImg.writeAsync(`output-${t}.png`);
}))
};
这样就能形成小程序帧序列相对完善的解决方案了