每次我发布一篇文章时,我都会创建一个缩略图来配合它。通常这部分是最乏味的。我通常在 Photoshop 或其他图像编辑器中完成。为了尝试使这更容易,我最近使用 Javascript 和 Node.JS 自动生成了这张图片的帖子缩略图。在本教程中,我们将了解如何使用 Node.JS 和 Canvas 自动生成您自己的文章图像。
在本指南中,我将向您展示如何使用 Node.JS 自动生成帖子缩略图。这是我使用此方法生成的图像示例:
本文的完整代码可以在这个Git Gist中找到
如何在 Node.JS 中使用画布#
由于 Node.JS 是一种后端语言,因此它没有开箱即用的画布。我们必须使用一个名为 的组件canvas
,并将其导入到我们的 Node.JS 中。这可以通过行安装npm i canvas
,并导入到任何 Node.JS 文件中。
如何在 Node.JS Canvas 中使用表情符号#
您可以使用默认模块完成我在这里要做的大部分工作canvas
——但对于我生成的图像,我还想使用表情符号。因此,我正在使用该包的一个分支,称为@napi-rs/canvas
,它支持表情符号。我使用的版本是0.1.14
,所以如果您在复制本指南时遇到问题,请尝试使用以下命令安装它npm i @napi-rs/canvas@0.1.14
现在我们已经介绍了基础知识,让我们开始吧。首先,让我们导入所有的包。我在这里导入一些东西:
- canvas – 这就是我们创建图像的方式。
- fs – 这就是我们将图像写入服务器并保存的方式。
- cwebp – 这就是我们将图像保存为 webp 文件的方式 – 因此它针对网络进行了优化。
- 字体– 我正在导入 3 种字体 –两种是 Inter 版本,这是一种很棒的字体,最后一种是 Apple Emoji 字体。您可以在此处找到 Inter,并在此处找到Apple Emoji 字体。
import canvas from '@napi-rs/canvas' // For canvas. import fs from 'fs' // For creating files for our images. import cwebp from 'cwebp' // For converting our images to webp. // Load in the fonts we need GlobalFonts.registerFromPath('./fonts/Inter-ExtraBold.ttf', 'InterBold'); GlobalFonts.registerFromPath('./fonts/Inter-Medium.ttf','InterMedium'); GlobalFonts.registerFromPath('./fonts/Apple-Emoji.ttf', 'AppleEmoji');
如何使用 Javascript 自动生成帖子缩略图#
接下来我们需要编写一个实用函数来包装文本。这是我们要在画布上做的事情的先决条件。当我们在 HTML 画布上书写文本时,它通常不会自动换行。相反,我们需要创建一个函数来测量容器的宽度,并决定是否包装。一般来说,这是一个有用的画布实用程序功能,因此可能值得保存!注释函数如下所示:
// This function accepts 6 arguments: // - ctx: the context for the canvas // - text: the text we wish to wrap // - x: the starting x position of the text // - y: the starting y position of the text // - maxWidth: the maximum width, i.e., the width of the container // - lineHeight: the height of one line (as defined by us) const wrapText = function(ctx, text, x, y, maxWidth, lineHeight) { // First, split the words by spaces let words = text.split(' '); // Then we'll make a few variables to store info about our line let line = ''; let testLine = ''; // wordArray is what we'l' return, which will hold info on // the line text, along with its x and y starting position let wordArray = []; // totalLineHeight will hold info on the line height let totalLineHeight = 0; // Next we iterate over each word for(var n = 0; n < words.length; n++) { // And test out its length testLine += `${words[n]} `; var metrics = ctx.measureText(testLine); var testWidth = metrics.width; // If it's too long, then we start a new line if (testWidth > maxWidth && n > 0) { wordArray.push([line, x, y]); y += lineHeight; totalLineHeight += lineHeight; line = `${words[n]} `; testLine = `${words[n]} `; } else { // Otherwise we only have one line! line += `${words[n]} `; } // Whenever all the words are done, we push whatever is left if(n === words.length - 1) { wordArray.push([line, x, y]); } } // And return the words in array, along with the total line height // which will be (totalLines - 1) * lineHeight return [ wordArray, totalLineHeight ]; }
现在我们的效用函数已经完成,我们可以编写我们的generateMainImage
函数了。这将获取我们提供的所有信息,并为您的文章或网站生成图像。
对于上下文,在code8cn上,我为数据库中的每个类别提供了两种颜色——这让我为每个类别的每个图像生成一个渐变背景。在此功能中,您可以传递任何您想要的颜色并实现相同的效果 – 或者您可以完全更改功能!这是你的选择。
// This functiona accepts 5 arguments: // canonicalName: this is the name we'll use to save our image // gradientColors: an array of two colors, i.e. [ '#ffffff', '#000000' ], used for our gradient // articleName: the title of the article or site you want to appear in the image // articleCategory: the category which that article sits in - or the subtext of the article // emoji: the emoji you want to appear in the image. const generateMainImage = async function(canonicalName, gradientColors, articleName, articleCategory, emoji) { articleCategory = articleCategory.toUpperCase(); // gradientColors is an array [ c1, c2 ] if(typeof gradientColors === "undefined") { gradientColors = [ "#8005fc", "#073bae"]; // Backup values } // Create canvas const canvas = createCanvas(1342, 853); const ctx = canvas.getContext('2d') // Add gradient - we use createLinearGradient to do this let grd = ctx.createLinearGradient(0, 853, 1352, 0); grd.addColorStop(0, gradientColors[0]); grd.addColorStop(1, gradientColors[1]); ctx.fillStyle = grd; // Fill our gradient ctx.fillRect(0, 0, 1342, 853); // Write our Emoji onto the canvas ctx.fillStyle = 'white'; ctx.font = '95px AppleEmoji'; ctx.fillText(emoji, 85, 700); // Add our title text ctx.font = '95px InterBold'; ctx.fillStyle = 'white'; let wrappedText = wrapText(ctx, articleName, 85, 753, 1200, 100); wrappedText[0].forEach(function(item) { // We will fill our text which is item[0] of our array, at coordinates [x, y] // x will be item[1] of our array // y will be item[2] of our array, minus the line height (wrappedText[1]), minus the height of the emoji (200px) ctx.fillText(item[0], item[1], item[2] - wrappedText[1] - 200); // 200 is height of an emoji }) // Add our category text to the canvas ctx.font = '50px InterMedium'; ctx.fillStyle = 'rgba(255,255,255,0.8)'; ctx.fillText(articleCategory, 85, 553 - wrappedText[1] - 100); // 853 - 200 for emoji, -100 for line height of 1 if(fs.existsSync(`./views/images/intro-images/${canonicalName}.png`))) { return 'Images Exist! We did not create any' } else { // Set canvas as to png try { const canvasData = await canvas.encode('png'); // Save file fs.writeFileSync(`./views/images/intro-images/${canonicalName}.png`), canvasData); } catch(e) { console.log(e); return 'Could not create png image this time.' } 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 'Could not create webp image this time.' } return 'Images have been successfully created!'; } }
Node.JS生成文章图片详解#
让我们详细看看这个函数,这样我们就可以完全理解发生了什么。我们首先准备我们的数据 – 将我们的类别设为大写,并设置默认渐变。然后我们创建我们的画布,并getContext
用来启动一个我们可以画画的空间。
articleCategory = articleCategory.toUpperCase(); // gradientColors is an array [ c1, c2 ] if(typeof gradientColors === "undefined") { gradientColors = [ "#8005fc", "#073bae"]; // Backup values } // Create canvas const canvas = createCanvas(1342, 853); const ctx = canvas.getContext('2d')
然后我们绘制渐变:
// Add gradient - we use createLinearGradient to do this let grd = ctx.createLinearGradient(0, 853, 1352, 0); grd.addColorStop(0, gradientColors[0]); grd.addColorStop(1, gradientColors[1]); ctx.fillStyle = grd; // Fill our gradient ctx.fillRect(0, 0, 1342, 853);
并将我们的表情符号文字写到图像上。
// Write our Emoji onto the canvas ctx.fillStyle = 'white'; ctx.font = '95px AppleEmoji'; ctx.fillText(emoji, 85, 700);
现在我们开始使用包装函数wrapText
. 我们将传递相当长的articleName
,并在靠近图像底部的 处开始它85, 753
。由于wrapText
返回一个数组,我们将遍历该数组以找出每条线的坐标,并将它们绘制到画布上:
在那之后,我们可以添加我们的类别,它应该在表情符号和标题文本之上——我们现在已经计算了这两者。
// Add our title text ctx.font = '95px InterBold'; ctx.fillStyle = 'white'; let wrappedText = wrapText(ctx, articleName, 85, 753, 1200, 100); wrappedText[0].forEach(function(item) { // We will fill our text which is item[0] of our array, at coordinates [x, y] // x will be item[1] of our array // y will be item[2] of our array, minus the line height (wrappedText[1]), minus the height of the emoji (200px) ctx.fillText(item[0], item[1], item[2] - wrappedText[1] - 200); // 200 is height of an emoji }) // Add our category text to the canvas ctx.font = '50px InterMedium'; ctx.fillStyle = 'rgba(255,255,255,0.8)'; ctx.fillText(articleCategory, 85, 553 - wrappedText[1] - 100); // 853 - 200 for emoji, -100 for line height of 1
如何使用 Node.JS 将画布图像保存到服务器#
好的,现在我们已经创建了我们的图像,让我们将它保存到我们的服务器:
- 首先,我们将检查文件是否存在。如果是,我们将返回图像存在并且不做任何其他事情。
- 如果该文件不存在,我们将尝试创建它的 png 版本,使用
canvas.encode
,然后使用fs.writeFileSync
保存它。 - 如果一切顺利,我们将使用文件
cwebp
的替代.webp
版本保存,该版本应该比.png版本小得多。
if(fs.existsSync(`./views/images/intro-images/${canonicalName}.png`))) { return 'Images Exist! We did not create any' } else { // Set canvas as to png try { const canvasData = await canvas.encode('png'); // Save file fs.writeFileSync(`./views/images/intro-images/${canonicalName}.png`), canvasData); } catch(e) { console.log(e); return 'Could not create png image this time.' } 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 'Could not create webp image this time.' } return 'Images have been successfully created!'; }
现在我们有一个功能可以为我们自动生成图像。如您所料,要运行此文件,您需要使用:
node index.js
每次我写一篇新文章时我都会运行这个 – 所以当文章被保存到数据库时,也会为它生成一个图像。这是以这种方式生成的图像的另一个示例:
如何将 Node.JS 图像添加到您的站点#
现在您的图像应该保存到您的服务器。如果您将其放在可通过 URL 访问的位置,则可以将这些图像添加为帖子和网页上的“精选图像”。要将这些图像作为帖子缩略图添加到您的帖子中,以便它们显示在社交媒体中,您只需将以下两个元标记添加到页面的头部。如果您对HTML 和 SEO 元标记的完整列表感兴趣,可以在此处找到相关指南。
<meta property="og:image" content=""> <meta name="twitter:image" content="">
结论#
谢谢阅读。在本指南中,我们介绍了如何使用 Node.JS 创建帖子缩略图。我们还介绍了如何在 Node.JS canva 中使用表情符号。以下是一些对您有用的链接: