const { createCanvas, loadImage, registerFont } = require("canvas"); const fs = require("fs"); const path = require("path"); registerFont(path.join(process.cwd(), `/public/font/PINGFANG MEDIUM.TTF`), { family: "pf", }); const mergeImage = async (name) => { const WIDTH = 1080; const HEIGHT = 1920; const canvas = createCanvas(WIDTH, HEIGHT); const context = canvas.getContext("2d"); const bg = await loadImage( path.join(process.cwd(), "/public/merge/base_card1.jpg") ); context.drawImage(bg, 0, 0, 1080, 1920); context.font = "48px px"; context.fillStyle = "#282828"; context.textAlign = "center"; //文字水平居中 context.fillText(name, 566 / 2, 422); context.font = `400 80px pf`; context.textAlign = "center"; context.fillStyle = "red"; context.fillText(`龙马精神`, WIDTH / 2, 366 + 4); // return canvas.toBuffer(); const out = fs.createWriteStream(__dirname + "/test2.png"); const stream = canvas.createPNGStream(); stream.pipe(out); out.on("finish", () => console.log("The PNG file was created.")); }; // const avatarOrnament = async (avatarPath, ornamentPath, size = 512) => { // // 1️⃣ 加载图片 // const avatarImg = await loadImage(avatarPath); // const ornamentImg = await loadImage(ornamentPath); // const width = avatarImg.width; // const height = avatarImg.height; // const canvas = createCanvas(width, height); // const ctx = canvas.getContext("2d"); // // 3️⃣ 先画头像 // ctx.drawImage(avatarImg, 0, 0, width, height); // // 4️⃣ 计算挂饰尺寸(取短边的 25%) // const baseSize = Math.min(width, height); // const ornamentSize = Math.floor(baseSize * 0.25); // const ornamentRatio = ornamentImg.width / ornamentImg.height; // const ornamentWidth = // ornamentRatio >= 1 ? ornamentSize : ornamentSize * ornamentRatio; // const ornamentHeight = // ornamentRatio >= 1 ? ornamentSize / ornamentRatio : ornamentSize; // // 5️⃣ 位置:右下角 + 内边距 // const padding = Math.floor(baseSize * 0.05); // const x = width - ornamentWidth - padding; // const y = height - ornamentHeight - padding; // // 6️⃣ 阴影(非常关键:让合成“自然”) // ctx.save(); // ctx.shadowColor = "rgba(0, 0, 0, 0.25)"; // ctx.shadowBlur = Math.floor(baseSize * 0.03); // ctx.shadowOffsetX = Math.floor(baseSize * 0.01); // ctx.shadowOffsetY = Math.floor(baseSize * 0.01); // ctx.drawImage(ornamentImg, x, y, ornamentWidth, ornamentHeight); // ctx.restore(); // // 6. 输出文件 // // const buffer = canvas.toBuffer("image/png"); // // fs.writeFileSync(outputPath, buffer); // const out = fs.createWriteStream(__dirname + "/test3.png"); // const stream = canvas.createPNGStream(); // stream.pipe(out); // out.on("finish", () => console.log("The PNG file was created.")); // return "success"; // }; const avatarOrnament = async (avatarPath, ornamentPath, size = 512) => { // 1. 读取头像 const avatar = await loadImage(avatarPath); // 2. Canvas 尺寸 = 头像尺寸(不裁剪) const width = 500; const height = 500; const canvas = createCanvas(width, height); const ctx = canvas.getContext("2d"); // 3. 绘制头像 ctx.drawImage(avatar, 0, 0, width, height); // 4. 读取装饰外框(PNG,透明) // const frame = await loadImage(ornamentPath); const framePath = path.resolve(__dirname + "/66.png"); const frame = await loadImage(framePath); // 5. 覆盖绘制外框 ctx.drawImage(frame, 0, 0, width, height); // 6. 输出文件 // const buffer = canvas.toBuffer("image/png"); // fs.writeFileSync(outputPath, buffer); const out = fs.createWriteStream(__dirname + "/test6.png"); const stream = canvas.createPNGStream(); stream.pipe(out); out.on("finish", () => console.log("The PNG file was created.")); return "success"; }; module.exports = { mergeImage, avatarOrnament, };