改善 Twitter 卡片的一個故事

我會在 Twitter 上分享我的博客文章,曾經有一段時間,我為每篇博文畫了一張圖片。

我設置了 Hugo,使其使用存儲在帖子文件夾中的 banner.pngbanner.jpg 圖片作為開放圖形圖片,如下所示:

<meta property="og:image" content="https://flaviocopes.com/axios/banner.png" />

如果一篇文章沒有圖片,我會顯示我的頭像代替:

<meta property="og:image" content="https://flaviocopes.com/img/avatar.png" />

問題來了:我很久以前就停止製作這些自定義的橫幅圖片了,而且我的大部分博文都沒有橫幅圖片。

它們在 Twitter 上看起來都一樣:

我無法手工製作500個橫幅圖片。自從看到 Indie Hackers 為論壇博客文章生成這些圖片(一個很棒的點子)之後,我就一直在考慮以編程方式生成這些圖片。

因此,在獲得了一個很好的橫幅圖片的靈感後,我決定為我的每一篇博文創建一個自定義橫幅。

該橫幅是一個 PNG 圖片,為了保持文章的重點(“如何使用 Node.js 和 Canvas 創建並保存圖片”),我將省略其中的一些部分。

此外,有很多不同的方法可以實現我做的事情,下面只是其中一種方法。

首先,我們需要哪些 npm 包?

只需要一個!canvas

npm install canvas

這個包為我們提供了一個基於 Node.js 的 Canvas API 實現,我們在瀏覽器中非常熟悉和喜愛。

換句話說,我用來生成圖片的所有內容在瀏覽器中也可以運行。

只是,我們不是從 <canvas> HTML 元素獲取 Canvas 實例,而是載入庫,從中獲取 createCanvas 函數:

const { createCanvas } = require('canvas')

然後,我調用此函數,傳遞 canvas 的寬度和高度,我設置為 1200x600:

const width = 1200
const height = 600

const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')

讓我們用黑色來繪製(隨便提一下滾石樂隊的參考):

context.fillStyle = '#fff'
context.fillRect(0, 0, width, height)

現在,我們來添加文本。

首先,我選擇了 Menlo 字體,字體大而粗體。我將其居中對齊,然後設置為白色。

最後,我調用 context.fillText() 在 canvas 上繪製文本:

const text = 'Hello, World!'

context.font = 'bold 70pt Menlo'
context.textAlign = 'center'
context.fillStyle = '#fff'
context.fillText(text, 600, 170)

讓我們在文本後面繪製一個藍色框:

const text = 'Hello, World!'

context.textBaseline = 'top'
context.fillStyle = '#3574d4'
const textWidth = context.measureText(text).width
context.fillRect(600 - textWidth / 2 - 10, 170 - 5, textWidth + 20, 120)
context.fillStyle = '#fff'
context.fillText(text, 600, 170)

我們將 textBaseline 屬性設置為 top,以便更容易定位矩形。然後,我使用 measureText() 檢查文本的長度,並使用與繪製文本時使用的相同坐標將其繪製出來。

請確保在文本之前繪製矩形,因為在 Canvas 中,你按照順序將東西一個接一個地繪製在頂部。

很酷!現在,我希望在底部顯示我的網站 URL:

context.fillStyle = '#fff'
context.font = 'bold 30pt Menlo'
context.fillText('flaviocopes.com', 600, 530)

我還想添加我的徽標。為此,讓我們從 canvas 模塊中導入 loadImage 函數:

const { createCanvas, loadImage } = require('canvas')

然後,我們調用它,指定在運行腳本的相同文件夾中包含的 logo.png 圖片:

loadImage('./logo.png').then(image => {
 
})

一旦承諾被解決,我們就有了圖片對象,可以使用 drawImage() 將其繪製到 canvas 上:

loadImage('./logo.png').then(image => {
 context.drawImage(image, 340, 515, 70, 70)
})

這就是了!現在,我們可以使用 toBuffer() 方法將圖片保存到 image.png 文件中:

const buffer = canvas.toBuffer('image/png')
fs.writeFileSync('./image.png', buffer)

以下是完整的代碼:

const fs = require('fs')
const { createCanvas, loadImage } = require('canvas')

const width = 1200
const height = 630

const canvas = createCanvas(width, height)
const context = canvas.getContext('2d')

context.fillStyle = '#000'
context.fillRect(0, 0, width, height)

context.font = 'bold 70pt Menlo'
context.textAlign = 'center'
context.textBaseline = 'top'
context.fillStyle = '#3574d4'

const text = 'Hello, World!'

const textWidth = context.measureText(text).width
context.fillRect(600 - textWidth / 2 - 10, 170 - 5, textWidth + 20, 120)
context.fillStyle = '#fff'
context.fillText(text, 600, 170)

context.fillStyle = '#fff'
context.font = 'bold 30pt Menlo'
context.fillText('flaviocopes.com', 600, 530)

loadImage('./logo.png').then(image => {
 context.drawImage(image, 340, 515, 70, 70)
 const buffer = canvas.toBuffer('image/png')
 fs.writeFileSync('./test.png', buffer)
})