guanghulab/image-studio/renderer.js
铸渊 c1b3c02ef6 🎨 铸渊图片工作室 - 小红书/即刻/海报配图生成工具
为冰朔定制专属配图生成系统,支持三种卡片类型:
- 小红书竖版卡片 (1080×1440, 3:4)
- 即刻方卡 (1080×1080, 1:1)
- 海报 (1080×1920, 9:16)

特性:多套配色方案、多种布局风格、
      Puppeteer + HTML/CSS 渲染引擎、
      可部署 Web 服务 + 预览页面

使用方式:冰朔在对话里告诉铸渊内容和类型,
         铸渊运行脚本生成,冰朔直接下载使用。
2026-05-22 23:49:02 +00:00

89 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ═══════════════════════════════════════════════════
* 铸渊图片工作室 · Puppeteer 渲染引擎
* ═══════════════════════════════════════════════════
*/
import puppeteer from 'puppeteer'
import { existsSync, mkdirSync } from 'fs'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
const OUTPUT_DIR = join(__dirname, 'output')
/**
* 渲染 HTML 为图片
* @param {string} html - 完整 HTML 字符串
* @param {object} opts
* @param {number} opts.width - 图片宽度
* @param {number} opts.height - 图片高度
* @param {string} opts.name - 输出文件名(不含扩展名)
* @param {string} opts.format - 输出格式 png/webp/jpeg
* @param {number} opts.quality- 图片质量(仅 webp/jpeg
* @returns {Promise<string>} 输出文件路径
*/
export async function renderToImage(html, opts = {}) {
const {
width = 1080,
height = 1440,
name = `card_${Date.now()}`,
format = 'png',
quality = 90,
} = opts
if (!existsSync(OUTPUT_DIR)) mkdirSync(OUTPUT_DIR, { recursive: true })
const browser = await puppeteer.launch({
headless: true,
executablePath: '/usr/bin/google-chrome',
args: ['--no-sandbox', '--disable-setuid-sandbox'],
})
try {
const page = await browser.newPage()
await page.setViewport({ width, height })
// 注入完整的 HTML
await page.setContent(html, { waitUntil: 'networkidle0' })
// 等待字体加载
await page.evaluate(() => document.fonts.ready)
// 额外等一会儿让渲染稳定
await new Promise(r => setTimeout(r, 500))
const outputPath = join(OUTPUT_DIR, `${name}.${format}`)
await page.screenshot({
path: outputPath,
type: format,
clip: { x: 0, y: 0, width, height },
quality: format !== 'png' ? quality : undefined,
})
return outputPath
} finally {
await browser.close()
}
}
/**
* 批量生成多页(轮播图)
* @param {string[]} pages - 每页的 HTML
* @param {object} opts
* @returns {Promise<string[]>} 输出文件路径数组
*/
export async function renderCarousel(pages, opts = {}) {
const results = []
for (let i = 0; i < pages.length; i++) {
const name = opts.baseName
? `${opts.baseName}_p${i + 1}`
: `carousel_${Date.now()}_p${i + 1}`
const path = await renderToImage(pages[i], { ...opts, name })
results.push(path)
}
return results
}