> "å°é¢å¾æ¯æç« çè¸ï¼è¸å¥½çäºï¼æææºä¼å±ç¤ºå æ¶µã"
Resources
2Install
npx skillscat add atxinsky/skills/image-generator Install via the SkillsCat registry.
SKILL.md
Image Generator - Canvas封面图生成器
💡 Skill简介
这是一个基于Canvas的封面图生成系统,无需AI绘图API,本地化、免费、快速生成风格统一的公众号配图。
核心能力:
- 5秒生成封面图(无需等待AI)
- 10套预设模板,风格统一
- 动态填充标题/数据
- 支持自定义品牌色
技术方案:
- Node.js + Canvas
- 模板+数据 = 封面图
- 完全本地化,零成本
🎨 模板库(10套风格)
1. 极简风(推荐)
┌─────────────────────┐
│ │
│ 如何用AI工具 │
│ 10分钟做封面图 │
│ │
│ @你的公众号 │
└─────────────────────┘适用:知识类、教程类
配色:黑白灰
2. 渐变风
┌─────────────────────┐
│ ╱╲ (渐变背景) │
│ │
│ 爆款文章的3个秘密 │
│ │
│ 📝 │
└─────────────────────┘适用:干货类、清单类
配色:蓝紫渐变
3. 数据风
┌─────────────────────┐
│ 10,000+ 阅读量 │
│ ──────────── │
│ │
│ 这篇文章教你 │
│ 写出爆款标题 │
└─────────────────────┘适用:案例分析类
配色:橙色系
4. 对比风
┌─────────────────────┐
│ 普通人 VS 高手 │
│ ──────────── │
│ │
│ 差距在这3点 │
└─────────────────────┘适用:对比类、方法论类
配色:红蓝对比
5. 问答风
┌─────────────────────┐
│ ❓ │
│ │
│ 为什么你的文章 │
│ 总是没人看? │
│ │
└─────────────────────┘适用:痛点类、引导类
配色:黄色系
📝 使用方法
基础用法
/image-generator "标题" --template minimalist输入示例
/image-generator "如何用AI工具10分钟做出公众号封面图" --template gradient输出
✅ 封面图生成成功!
文件路径:~/封面图/cover-20260123.png
尺寸:900x500px(公众号推荐尺寸)
模板:gradient(渐变风)
预览:
┌─────────────────────────────┐
│ ╱╲ (蓝紫渐变背景) │
│ │
│ 如何用AI工具 │
│ 10分钟做出公众号封面图 │
│ │
│ @atxin │
└─────────────────────────────┘🔧 技术实现
安装依赖
cd ~/.claude/skills/image-generator
npm init -y
npm install canvas核心代码(generate-cover.js)
const { createCanvas, registerFont } = require('canvas');
const fs = require('fs');
const path = require('path');
// 注册字体(可选,使用系统字体)
// registerFont('fonts/SourceHanSansCN.ttf', { family: 'Source Han Sans CN' });
/**
* 生成封面图
* @param {string} title - 标题
* @param {string} template - 模板名称
* @param {object} options - 配置项
*/
function generateCover(title, template = 'minimalist', options = {}) {
// Canvas尺寸(公众号推荐 900x500)
const width = 900;
const height = 500;
const canvas = createCanvas(width, height);
const ctx = canvas.getContext('2d');
// 根据模板选择样式
const templates = {
minimalist: drawMinimalist,
gradient: drawGradient,
data: drawData,
vs: drawVS,
qa: drawQA,
};
const drawFunc = templates[template] || templates.minimalist;
drawFunc(ctx, title, width, height, options);
// 保存图片
const outputDir = path.join(process.env.HOME, '封面图');
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const filename = `cover-${Date.now()}.png`;
const filepath = path.join(outputDir, filename);
const buffer = canvas.toBuffer('image/png');
fs.writeFileSync(filepath, buffer);
console.log(`✅ 封面图生成成功!`);
console.log(`文件路径:${filepath}`);
console.log(`尺寸:${width}x${height}px`);
return filepath;
}
// ========== 模板1:极简风 ==========
function drawMinimalist(ctx, title, width, height, options) {
// 背景
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, width, height);
// 标题(自动换行)
ctx.fillStyle = '#000000';
ctx.font = 'bold 64px "Microsoft YaHei", sans-serif';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
// 简单换行逻辑
const lines = wrapText(ctx, title, width - 100);
const lineHeight = 80;
const startY = (height - lines.length * lineHeight) / 2;
lines.forEach((line, i) => {
ctx.fillText(line, width / 2, startY + i * lineHeight);
});
// 底部署名
ctx.font = '24px sans-serif';
ctx.fillStyle = '#999999';
ctx.fillText(options.author || '@atxin', width / 2, height - 40);
}
// ========== 模板2:渐变风 ==========
function drawGradient(ctx, title, width, height, options) {
// 渐变背景
const gradient = ctx.createLinearGradient(0, 0, width, height);
gradient.addColorStop(0, '#667eea');
gradient.addColorStop(1, '#764ba2');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, width, height);
// 标题(白色)
ctx.fillStyle = '#FFFFFF';
ctx.font = 'bold 60px sans-serif';
ctx.textAlign = 'center';
const lines = wrapText(ctx, title, width - 100);
const lineHeight = 75;
const startY = (height - lines.length * lineHeight) / 2;
lines.forEach((line, i) => {
ctx.fillText(line, width / 2, startY + i * lineHeight);
});
// Emoji装饰
ctx.font = '80px sans-serif';
ctx.fillText('📝', width / 2, height - 80);
}
// ========== 模板3:数据风 ==========
function drawData(ctx, title, width, height, options) {
// 背景
ctx.fillStyle = '#FFF4E6';
ctx.fillRect(0, 0, width, height);
// 顶部数据
ctx.fillStyle = '#FF6B35';
ctx.font = 'bold 72px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(options.data || '10,000+', width / 2, 120);
// 分割线
ctx.strokeStyle = '#FF6B35';
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(width / 2 - 150, 160);
ctx.lineTo(width / 2 + 150, 160);
ctx.stroke();
// 标题
ctx.fillStyle = '#333333';
ctx.font = 'bold 48px sans-serif';
const lines = wrapText(ctx, title, width - 100);
const lineHeight = 65;
const startY = 240;
lines.forEach((line, i) => {
ctx.fillText(line, width / 2, startY + i * lineHeight);
});
}
// ========== 模板4:对比风(VS) ==========
function drawVS(ctx, title, width, height, options) {
// 左半部分(红色)
ctx.fillStyle = '#FF6B6B';
ctx.fillRect(0, 0, width / 2, height);
// 右半部分(蓝色)
ctx.fillStyle = '#4ECDC4';
ctx.fillRect(width / 2, 0, width / 2, height);
// 中间VS
ctx.fillStyle = '#FFFFFF';
ctx.font = 'bold 96px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('VS', width / 2, height / 2 - 50);
// 底部标题
ctx.font = 'bold 40px sans-serif';
ctx.fillStyle = '#000000';
const lines = wrapText(ctx, title, width - 100);
ctx.fillText(lines[0] || title, width / 2, height - 60);
}
// ========== 模板5:问答风(QA) ==========
function drawQA(ctx, title, width, height, options) {
// 背景
ctx.fillStyle = '#FFF9C4';
ctx.fillRect(0, 0, width, height);
// 问号Emoji
ctx.font = '120px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('❓', width / 2, 140);
// 标题(问句)
ctx.fillStyle = '#333333';
ctx.font = 'bold 52px sans-serif';
const lines = wrapText(ctx, title, width - 100);
const lineHeight = 70;
const startY = 240;
lines.forEach((line, i) => {
ctx.fillText(line, width / 2, startY + i * lineHeight);
});
}
// ========== 辅助函数:文本换行 ==========
function wrapText(ctx, text, maxWidth) {
const words = text.split('');
const lines = [];
let currentLine = '';
for (const char of words) {
const testLine = currentLine + char;
const metrics = ctx.measureText(testLine);
if (metrics.width > maxWidth && currentLine !== '') {
lines.push(currentLine);
currentLine = char;
} else {
currentLine = testLine;
}
}
lines.push(currentLine);
return lines;
}
// 导出函数
module.exports = { generateCover };
// CLI调用示例
if (require.main === module) {
const args = process.argv.slice(2);
const title = args[0] || '默认标题';
const template = args[1] || 'minimalist';
generateCover(title, template, { author: '@atxin' });
}🎨 自定义品牌色
创建 ~/.claude/skills/image-generator/brand-colors.json:
{
"primary": "#667eea",
"secondary": "#764ba2",
"text": "#333333",
"background": "#FFFFFF",
"accent": "#FF6B35"
}在代码中使用:
const brandColors = require('./brand-colors.json');
ctx.fillStyle = brandColors.primary;🚀 高级功能
1. 批量生成
为多篇文章一次性生成封面:
/image-generator --batch ~/文章列表.json文章列表.json:
[
{ "title": "文章1标题", "template": "minimalist" },
{ "title": "文章2标题", "template": "gradient" },
{ "title": "文章3标题", "template": "data", "data": "5000+" }
]2. 动态数据填充
从文章内容提取数据:
/image-generator "文章标题" --auto-data ~/文章.md自动提取:
- 阅读量(从文章metadata)
- 关键数据(从文章正文)
3. A/B测试
生成多个版本对比:
/image-generator "标题" --ab-test输出:
- 版本A:极简风
- 版本B:渐变风
- 版本C:数据风
📊 图片规范
公众号封面尺寸
| 位置 | 尺寸 | 比例 |
|---|---|---|
| 头图(推荐) | 900x500px | 16:9 |
| 正文配图 | 750px宽 | 不限高 |
| 缩略图 | 200x200px | 1:1 |
文件大小
- 推荐:< 500KB
- 最大:< 2MB
格式
- 推荐:PNG(支持透明)
- 备选:JPEG(文件小)
💡 设计原则
1. 可读性优先
- 字体大小:至少48px
- 对比度:黑白分明
- 避免花哨背景影响文字
2. 留白
- 四周留白:至少50px
- 元素间距:足够呼吸感
- 不要塞满整个画面
3. 风格统一
- 同一公众号用2-3套模板
- 配色保持一致
- 字体固定(黑体/宋体)
4. 吸引眼球
- 用数字("10倍""30天")
- 用对比("普通人 vs 高手")
- 用问句("为什么...")
🔧 故障排除
问题1:中文字体显示乱码
# 方案1:使用系统字体
ctx.font = 'bold 64px "Microsoft YaHei", sans-serif';
# 方案2:注册自定义字体
const { registerFont } = require('canvas');
registerFont('fonts/SourceHanSansCN.ttf', { family: 'Source Han Sans CN' });
ctx.font = 'bold 64px "Source Han Sans CN"';问题2:Canvas模块安装失败
# Windows用户
npm install --global --production windows-build-tools
npm install canvas
# Mac用户
brew install pkg-config cairo pango libpng jpeg giflib librsvg
npm install canvas
# Linux用户
sudo apt-get install build-essential libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev
npm install canvas问题3:生成的图片模糊
// 提高Canvas分辨率(Retina屏)
const scale = 2;
const canvas = createCanvas(width * scale, height * scale);
ctx.scale(scale, scale);📚 模板扩展
添加新模板
- 在
generate-cover.js中添加绘制函数:
function drawMyTemplate(ctx, title, width, height, options) {
// 你的设计...
}- 注册到模板列表:
const templates = {
minimalist: drawMinimalist,
gradient: drawGradient,
mytemplate: drawMyTemplate, // 新增
};- 调用:
/image-generator "标题" --template mytemplate🎓 最佳实践
1. 标题拆分规则
- 超过12个字 → 拆2行
- 超过20个字 → 拆3行
- 有数字 → 数字单独一行突出
2. 配色推荐
- 知识类:蓝色系(专业)
- 情感类:粉色系(温暖)
- 干货类:橙色系(活力)
- 深度类:深灰/黑色系(沉稳)
3. 更新频率
- 每月更换1次模板(保持新鲜感)
- 同一模板用不同配色(微调)
🔗 配置文件
创建 ~/.claude/skills/image-generator/config.json:
{
"default_template": "minimalist",
"author": "@atxin",
"output_dir": "~/封面图",
"width": 900,
"height": 500,
"format": "png",
"quality": 0.95
}🚀 快速开始
Step 1: 安装依赖
cd ~/.claude/skills/image-generator
npm install canvasStep 2: 生成第一张封面
/image-generator "如何写好公众号" --template minimalistStep 3: 查看结果
open ~/封面图/cover-*.pngStep 4: 上传到公众号
记住:好的封面图,3秒内吸引眼球,5秒内传递信息。
"封面图是文章的脸,脸好看了,才有机会展示内涵。"