182 lines
5.2 KiB
JavaScript
182 lines
5.2 KiB
JavaScript
/**
|
||
* TabBar 图标生成脚本
|
||
* 运行: node scripts/generateIcons.js
|
||
* 需要先安装依赖: npm install canvas
|
||
*/
|
||
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
// 尝试使用 canvas,如果没有安装则使用简单的 PNG 生成
|
||
let useCanvas = false;
|
||
let createCanvas;
|
||
|
||
try {
|
||
const canvas = require('canvas');
|
||
createCanvas = canvas.createCanvas;
|
||
useCanvas = true;
|
||
} catch (e) {
|
||
console.log('canvas 未安装,将生成简单的占位图标');
|
||
}
|
||
|
||
const imagesDir = path.join(__dirname, '..', 'images');
|
||
|
||
// 确保 images 目录存在
|
||
if (!fs.existsSync(imagesDir)) {
|
||
fs.mkdirSync(imagesDir, { recursive: true });
|
||
}
|
||
|
||
// 图标配置
|
||
const icons = [
|
||
{ name: 'tab-rank', icon: '排', desc: '排名' },
|
||
{ name: 'tab-match', icon: '赛', desc: '比赛' },
|
||
{ name: 'tab-points', icon: '分', desc: '积分' },
|
||
{ name: 'tab-user', icon: '我', desc: '我的' }
|
||
];
|
||
|
||
const normalColor = '#999999';
|
||
const activeColor = '#FF6B35';
|
||
const size = 81; // 微信推荐 81x81
|
||
|
||
function generateIconWithCanvas(iconConfig, isActive) {
|
||
const canvas = createCanvas(size, size);
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
// 清空背景(透明)
|
||
ctx.clearRect(0, 0, size, size);
|
||
|
||
// 绘制圆形背景
|
||
ctx.beginPath();
|
||
ctx.arc(size / 2, size / 2, size / 2 - 4, 0, Math.PI * 2);
|
||
ctx.fillStyle = isActive ? activeColor : '#f5f5f5';
|
||
ctx.fill();
|
||
|
||
// 绘制文字
|
||
ctx.font = 'bold 32px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.textBaseline = 'middle';
|
||
ctx.fillStyle = isActive ? '#ffffff' : normalColor;
|
||
ctx.fillText(iconConfig.icon, size / 2, size / 2);
|
||
|
||
return canvas.toBuffer('image/png');
|
||
}
|
||
|
||
// 简单的 PNG 生成(不依赖 canvas)
|
||
function generateSimplePNG(iconConfig, isActive) {
|
||
// 创建一个简单的 1x1 像素的 PNG 作为占位符
|
||
// 实际项目中应该使用真实的图标文件
|
||
const color = isActive ? [255, 107, 53] : [153, 153, 153]; // RGB
|
||
|
||
// 最简单的 PNG 文件头 + IHDR + IDAT + IEND
|
||
// 这是一个 8x8 的纯色 PNG
|
||
const width = 8;
|
||
const height = 8;
|
||
|
||
// PNG 签名
|
||
const signature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
||
|
||
// IHDR chunk
|
||
const ihdr = createChunk('IHDR', Buffer.from([
|
||
0, 0, 0, width, // width
|
||
0, 0, 0, height, // height
|
||
8, // bit depth
|
||
2, // color type (RGB)
|
||
0, // compression
|
||
0, // filter
|
||
0 // interlace
|
||
]));
|
||
|
||
// IDAT chunk - 简化的图像数据
|
||
const zlib = require('zlib');
|
||
const rawData = Buffer.alloc((width * 3 + 1) * height);
|
||
for (let y = 0; y < height; y++) {
|
||
rawData[y * (width * 3 + 1)] = 0; // filter byte
|
||
for (let x = 0; x < width; x++) {
|
||
const offset = y * (width * 3 + 1) + 1 + x * 3;
|
||
rawData[offset] = color[0];
|
||
rawData[offset + 1] = color[1];
|
||
rawData[offset + 2] = color[2];
|
||
}
|
||
}
|
||
const compressed = zlib.deflateSync(rawData);
|
||
const idat = createChunk('IDAT', compressed);
|
||
|
||
// IEND chunk
|
||
const iend = createChunk('IEND', Buffer.alloc(0));
|
||
|
||
return Buffer.concat([signature, ihdr, idat, iend]);
|
||
}
|
||
|
||
function createChunk(type, data) {
|
||
const length = Buffer.alloc(4);
|
||
length.writeUInt32BE(data.length);
|
||
|
||
const typeBuffer = Buffer.from(type);
|
||
const crcData = Buffer.concat([typeBuffer, data]);
|
||
const crc = crc32(crcData);
|
||
|
||
const crcBuffer = Buffer.alloc(4);
|
||
crcBuffer.writeUInt32BE(crc >>> 0);
|
||
|
||
return Buffer.concat([length, typeBuffer, data, crcBuffer]);
|
||
}
|
||
|
||
// CRC32 计算
|
||
function crc32(data) {
|
||
let crc = 0xffffffff;
|
||
const table = getCRC32Table();
|
||
|
||
for (let i = 0; i < data.length; i++) {
|
||
crc = table[(crc ^ data[i]) & 0xff] ^ (crc >>> 8);
|
||
}
|
||
|
||
return crc ^ 0xffffffff;
|
||
}
|
||
|
||
let crc32Table = null;
|
||
function getCRC32Table() {
|
||
if (crc32Table) return crc32Table;
|
||
|
||
crc32Table = new Uint32Array(256);
|
||
for (let i = 0; i < 256; i++) {
|
||
let c = i;
|
||
for (let j = 0; j < 8; j++) {
|
||
c = (c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1);
|
||
}
|
||
crc32Table[i] = c;
|
||
}
|
||
return crc32Table;
|
||
}
|
||
|
||
// 生成图标
|
||
console.log('开始生成 TabBar 图标...\n');
|
||
|
||
icons.forEach(iconConfig => {
|
||
// 生成普通状态图标
|
||
const normalPath = path.join(imagesDir, `${iconConfig.name}.png`);
|
||
const normalBuffer = useCanvas
|
||
? generateIconWithCanvas(iconConfig, false)
|
||
: generateSimplePNG(iconConfig, false);
|
||
fs.writeFileSync(normalPath, normalBuffer);
|
||
console.log(`✓ 已生成: ${iconConfig.name}.png (${iconConfig.desc})`);
|
||
|
||
// 生成选中状态图标
|
||
const activePath = path.join(imagesDir, `${iconConfig.name}-active.png`);
|
||
const activeBuffer = useCanvas
|
||
? generateIconWithCanvas(iconConfig, true)
|
||
: generateSimplePNG(iconConfig, true);
|
||
fs.writeFileSync(activePath, activeBuffer);
|
||
console.log(`✓ 已生成: ${iconConfig.name}-active.png (${iconConfig.desc} - 选中)`);
|
||
});
|
||
|
||
console.log('\n图标生成完成!');
|
||
console.log(`图标目录: ${imagesDir}`);
|
||
|
||
if (!useCanvas) {
|
||
console.log('\n提示: 当前生成的是简单占位图标。');
|
||
console.log('如需更好的图标效果,请:');
|
||
console.log('1. 安装 canvas: npm install canvas');
|
||
console.log('2. 重新运行此脚本');
|
||
console.log('或者从 iconfont.cn 下载合适的图标替换');
|
||
}
|