Last updated: December 17, 2023最后更新:2023年12月17日


Generating Open Graph Images in Eleventy在 Eleventy 中生成开放图图像


I added the og:media image tags yesterday, and the approach seems to work, so I’m going to share it in this post.我昨天添加了 og:media 图像标签,这种方法似乎有效,所以我将在这篇文章中分享它。

Normally you wouldn’t see this banner.

I am using the filter functions and generating the raster images directly inside of the filter with node-canvas, as described in this article.我正在使用过滤器函数,并使用 node-canvas 直接在过滤器内生成光栅图像,如本文所述

First I added the meta tags to my default layout. I called the filter previews, and I will be using the title variable as the argument.首先,我将元标记添加到我的默认布局中。我调用了筛选previews,我将使用 title 变量作为参数。

<meta property="og:image" content="{{ title | previews }}" />

Then I wrote the filter.然后我写了过滤器。

const fs = require('fs');
const path = require('path');
const { createCanvas, registerFont } = require("canvas");
eleventyConfig.addFilter("previews", async function (title) {
  // register custom fonts, if you want to use any
  // this step has to be done before creating the canvas
  registerFont('./custom-font.ttf', { family: 'Custom Font' });
  // set layout variables
  const width = 1200;
  const height = 630;
  // create the canvas
  const canvas = createCanvas(width, height);
  const context = canvas.getContext("2d");
  // get the output path
  const output = path.dirname(;
  // set the filename to the slug of the page, or "preview" as a backup
  const filename = || 'preview';
  // set the extension
  const ext = "png";
  // create the full output path
  const filepath = path.join(output, `${filename}.${ext}`);
  // maybe the file already exists - in that case, just return the relative path
  if (fs.existsSync(filepath)) {
    return `${filename}.${ext}`;
  // canvas won't automatically divide the header into multiple lines,
  // so we have to do it manually - in my case, there'll be at most
  // six words in one line
  const wordsPerLine = 6;
  const header = title
    .split(' ')
    .reduce((result, word, index) => {
      const line = Math.floor(index / wordsPerLine);
      if (!result[line]) {
        result[line] = []; // begin new line
      return result;
    }, [])
    .map(line => line.join(' '))
  // set the canvas background
  context.fillStyle = "#222222";
  context.fillRect(0, 0, width, height);
  // set the header
  const fontSize = 30;
  const lineCount = header.match(/\n/g)?.length ?? 0;
  context.fillStyle = "#ffffff";
  context.font = `${fontSize}px 'Custom Font'`;
  context.textAlign = "center";
  context.fillText(header, width / 2, (height - lineCount * fontSize) / 2);
  // create the image
  const buffer = canvas.toBuffer("image/png");
  // create the output directory if it doesn't already exist
  if (!fs.existsSync(output)) {
    fs.mkdirSync(output, { recursive: true });
  // write the file
  fs.writeFileSync(filepath, buffer);
  // return the relative file path
  return `${filename}.${ext}`;

This is the bare-bones set-up, without any decorations, or the name of the author, or anything like that, but this article and the node-canvas documentation describe how to add more things.这是最基本的设置,没有任何装饰,也没有作者的名字,或者类似的东西,但这篇文章节点画布文档描述了如何添加更多的东西。