软件教程 2025年08月6日
0 收藏 0 点赞 874 浏览 5945 个字
摘要 :

文章目录 一、前期准备 二、导入所需包 三、实现文本换行功能 四、编写图片生成函数 五、运行生成图片 开发过程中,我们常常会有自动生成图片的需求,比如为文章生成缩……




  • 一、前期准备
  • 二、导入所需包
  • 三、实现文本换行功能
  • 四、编写图片生成函数
  • 五、运行生成图片

开发过程中,我们常常会有自动生成图片的需求,比如为文章生成缩略图。今天就来详细讲讲如何借助Node.JS和Canvas实现这一功能。接下来,我们一步步深入学习。

一、前期准备

Node.JS本身并不具备canvas功能,所以我们需要借助外部组件来实现。这里我们选用canvas组件,在项目中运行npm i canvas命令即可完成安装。

要是还想在生成的图片中使用Emoji,普通的canvas包可能无法满足需求。此时,可以使用@napi-rs/canvas这个包的分支,我使用的版本是0.1.14。若在操作过程中遇到问题,不妨尝试通过npm i @napi-rs/canvas@0.1.14命令进行安装。

二、导入所需包

准备工作完成后,就要导入项目所需的包了:

import canvas from \'@napi-rs/canvas\' // 用于创建画布。
import fs from \'fs\' // 用于为我们的图片创建文件。
import cwebp from \'cwebp\' // 用于将图片转换为webp格式。

// 加载我们需要的字体
GlobalFonts.registerFromPath(\'./fonts/Inter-ExtraBold.ttf\', \'InterBold\');
GlobalFonts.registerFromPath(\'./fonts/Inter-Medium.ttf\',\'InterMedium\');
GlobalFonts.registerFromPath(\'./fonts/Apple-Emoji.ttf\', \'AppleEmoji\');

这里导入了canvas用于创建画布;fs模块负责将生成的图片写入服务器并保存;cwebp则用于将图片保存为优化过的webp文件。另外,还注册了三种字体,包括两种不同版本的Inter字体和Apple Emoji字体,大家可以在Inter字体页面和Apple Emoji字体页面获取这些字体 。

三、实现文本换行功能

在HTML画布上书写文本时,文本通常不会自动换行,所以我们得自定义一个函数来实现该功能。这个函数接收6个参数,具体代码如下:

// 这个函数接受6个参数:
// - ctx: 画布的上下文
// - text: 我们想要换行的文本
// - x: 文本的起始x坐标
// - y: 文本的起始y坐标
// - maxWidth: 最大宽度,即容器的宽度
// - lineHeight: 每行的高度(由我们定义)
const wrapText = function(ctx, text, x, y, maxWidth, lineHeight) {
    // 首先,按空格分割单词
    let words = text.split(\' \');
    // 然后我们创建几个变量来存储行的信息
    let line = \'\';
    let testLine = \'\';
    // wordArray是我们将要返回的数组,它将保存
    // 行文本的信息,以及它的x和y起始位置
    let wordArray = [];
    // totalLineHeight将保存行高的信息
    let totalLineHeight = 0;

    // 接下来,我们遍历每个单词
    for(var n = 0; n < words.length; n++) {
        // 测试它的长度
        testLine += `${words[n]} `;
        var metrics = ctx.measureText(testLine);
        var testWidth = metrics.width;
        // 如果太长,则我们开始新的一行
        if (testWidth > maxWidth && n > 0) {
            wordArray.push([line, x, y]);
            y += lineHeight;
            totalLineHeight += lineHeight;
            line = `${words[n]} `;
            testLine = `${words[n]} `;
        } else {
            // 否则我们只有一行!
            line += `${words[n]} `;
        }
        // 当所有单词完成后,我们将剩余的内容推入数组
        if(n === words.length - 1) {
            wordArray.push([line, x, y]);
        }
    }

    // 返回包含单词的数组,以及总行高
    // 总行高将是 (总行数 - 1) * 行高
    return [ wordArray, totalLineHeight ];
}

这个函数的作用是根据设定的最大宽度,将传入的文本进行合理换行,并返回包含每行文本信息及其起始坐标的数组,还有总行高信息。

四、编写图片生成函数

接下来编写generateMainImage函数,它将整合各种信息,为文章或网站生成图片。在这个函数里,颜色等参数都可以自行设定。

// 这个函数接受5个参数:
// canonicalName: 这是我们用来保存图片的名字
// gradientColors: 一个包含两种颜色的数组,例如 [ \'#ffffff\', \'#000000\' ],用于我们的渐变
// articleName: 你希望在图片中显示的文章或网站的标题
// articleCategory: 该文章所属的类别——或者文章的副标题
// emoji: 你希望在图片中显示的emoji
const generateMainImage = async function(canonicalName, gradientColors, articleName, articleCategory, emoji) {

    articleCategory = articleCategory.toUpperCase();
    // gradientColors是一个数组 [ c1, c2 ]
    if(typeof gradientColors === \"undefined\") {
        gradientColors = [ \"#8005fc\", \"#073bae\"]; // 备用值
    }

    // 创建画布
    const canvas = createCanvas(1342, 853);
    const ctx = canvas.getContext(\'2d\')

    // 添加渐变——我们使用createLinearGradient来实现这一点
    let grd = ctx.createLinearGradient(0, 853, 1352, 0);
    grd.addColorStop(0, gradientColors[0]);
    grd.addColorStop(1, gradientColors[1]);
    ctx.fillStyle = grd;
    // 填充我们的渐变
    ctx.fillRect(0, 0, 1342, 853);

    // 在画布上书写我们的Emoji
    ctx.fillStyle = \'white\';
    ctx.font = \'95px AppleEmoji\';
    ctx.fillText(emoji, 85, 700);

    // 添加我们的标题文本
    ctx.font = \'95px InterBold\';
    ctx.fillStyle = \'white\';
    let wrappedText = wrapText(ctx, articleName, 85, 753, 1200, 100);
    wrappedText[0].forEach(function(item) {
        // 我们将填充数组中的文本item[0],在坐标 [x, y]
        // x是数组中的item[1]
        // y是数组中的item[2],减去行高(wrappedText[1]),再减去emoji的高度(200px)
        ctx.fillText(item[0], item[1], item[2] - wrappedText[1] - 200); // 200是emoji的高度
    })

    // 将我们的类别文本添加到画布上
    ctx.font = \'50px InterMedium\';
    ctx.fillStyle = \'rgba(255,255,255,0.8)\';
    ctx.fillText(articleCategory, 85, 553 - wrappedText[1] - 100); // 853 - 200用于emoji,-100用于1行的行高

    if(fs.existsSync(`./views/images/intro-images/${canonicalName}.png`)) {
        return \'图片已存在!我们没有创建任何图片\'
    } else {
        // 将画布设置为png格式
        try {
            const canvasData = await canvas.encode(\'png\');
            // 保存文件
            fs.writeFileSync(`./views/images/intro-images/${canonicalName}.png`, canvasData);
        } catch(e) {
            console.log(e);
            return \'这次无法创建png图片。\'
        }
        try {
            const encoder = new cwebp.CWebp(path.join(__dirname, \'../\', `/views/images/intro-images/${canonicalName}.png`));
            encoder.quality(30);
            await encoder.write(`./views/images/intro-images/${canonicalName}.webp`, function(err) {
                if(err) console.log(err);
            });
        } catch(e) {
            console.log(e);
            return \'这次无法创建webp图片。\'
        }

        return \'图片已成功创建!\';
    }
}

下面详细分析一下这个函数的执行过程:

  1. 数据准备:将文章类别转换为大写形式,并在未传入渐变颜色数组时,设置默认的渐变颜色值。
articleCategory = articleCategory.toUpperCase();
// gradientColors是一个数组 [ c1, c2 ]
if(typeof gradientColors === \"undefined\") {
    gradientColors = [ \"#8005fc\", \"#073bae\"]; // 备用值
}
  1. 创建画布与设置渐变:创建指定尺寸的画布,并获取绘图上下文。利用createLinearGradient方法创建渐变对象,设置渐变颜色并填充整个画布。
// 创建画布
const canvas = createCanvas(1342, 853);
const ctx = canvas.getContext(\'2d\')

// 添加渐变——我们使用createLinearGradient来实现这一点
let grd = ctx.createLinearGradient(0, 853, 1352, 0);
grd.addColorStop(0, gradientColors[0]);
grd.addColorStop(1, gradientColors[1]);
ctx.fillStyle = grd;
// 填充我们的渐变
ctx.fillRect(0, 0, 1342, 853);
  1. 绘制Emoji、标题和类别文本:分别设置Emoji、标题和类别文本的字体、颜色等样式,调用之前编写的wrapText函数处理标题文本换行,并将这些文本绘制到画布的相应位置。
// 在画布上书写我们的Emoji
ctx.fillStyle = \'white\';
ctx.font = \'95px AppleEmoji\';
ctx.fillText(emoji, 85, 700);

// 添加我们的标题文本
ctx.font = \'95px InterBold\';
ctx.fillStyle = \'white\';
let wrappedText = wrapText(ctx, articleName, 85, 753, 1200, 100);
wrappedText[0].forEach(function(item) {
    // 我们将填充数组中的文本item[0],在坐标 [x, y]
    // x是数组中的item[1]
    // y是数组中的item[2],减去行高(wrappedText[1]),再减去emoji的高度(200px)
    ctx.fillText(item[0], item[1], item[2] - wrappedText[1] - 200); // 200是emoji的高度
})

// 将我们的类别文本添加到画布上
ctx.font = \'50px InterMedium\';
ctx.fillStyle = \'rgba(255,255,255,0.8)\';
ctx.fillText(articleCategory, 85, 553 - wrappedText[1] - 100); // 853 - 200用于emoji,-100用于1行的行高
  1. 保存图片:检查指定路径下是否已存在同名的png图片,如果存在则直接返回提示信息;若不存在,则先将画布内容编码为png格式并保存,接着使用cwebp将png图片转换为webp格式保存,最后返回相应的创建结果提示。
if(fs.existsSync(`./views/images/intro-images/${canonicalName}.png`)) {
    return \'图片已存在!我们没有创建任何图片\'
} else {
    // 将画布设置为png格式
    try {
        const canvasData = await canvas.encode(\'png\');
        // 保存文件
        fs.writeFileSync(`./views/images/intro-images/${canonicalName}.png`, canvasData);
    } catch(e) {
        console.log(e);
        return \'这次无法创建png图片。\'
    }
    try {
        const encoder = new cwebp.CWebp(path.join(__dirname, \'../\', `/views/images/intro-images/${canonicalName}.png`));
        encoder.quality(30);
        await encoder.write(`./views/images/intro-images/${canonicalName}.webp`, function(err) {
            if(err) console.log(err);
        });
    } catch(e) {
        console.log(e);
        return \'这次无法创建webp图片。\'
    }

    return \'图片已成功创建!\';
}

五、运行生成图片

完成上述代码编写后,在命令行中运行node index.js,就能执行图片生成操作了。按照上述步骤和代码,我们就能用Node.JS和Canvas自动生成图片了,赶紧动手试试吧!

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6871.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号