DEVコミュニティで動的にOpenGraph画像を生成する方法

このチュートリアルは最初に私の新しいブログに投稿されたものです。こちらでチェックして、近々新しい独占記事を見逃さないようにしてください。

VercelのOG画像生成ライブラリは、ウェブサイトのコンテンツ用の画像を動的に生成するための素晴らしいツールです。ソーシャルメディア上でコンテンツを際立たせる良い方法です。この記事では、VercelのOG画像生成ライブラリに何が含まれていて、Vercelに限定されずにこれをどのように再現できるかを見ていきます。
私はAstroを使用しますが、一般的なコンセプトは他のフレームワークにも適用できます。

VercelのOG画像生成ライブラリには何が含まれているのか?


Satori

Satoriは@vercel/ogパッケージで使われているライブラリで、Vercelによってメンテナンスされています。SatoriはJSXコンポーネントを取り、それをSVGにレンダリングします。

ReSVG

ReSVGは、SVGのためのライブラリで、SVGをPNG画像にレンダリングするために使用されます。ReSVGは@vercel/ogパッケージで、Satoriによって生成されたSVGをPNG画像にレンダリングする際に使用されます。

いくつかの依存関係をインストールする


SatoriとReSVGを使用して全く同じ機能を再現しますが、もう一つ追加します。Satori-HTMLNate Mooreによるライブラリで、生のHTMLをSatoriに適したJSXに変換します。これによりHTMLを使用して画像を生成できるようになります。

npm install satori satori-html @resvg/resvg-js

フォントのロード


SatoriはSVGで使用したいフォントをロードすることが必要です。使用したいフォントをダウンロードし、プロジェクトにインポートします。例として、Open Sansを使用します。

Viteプラグインを追加して、フォントファイルをロードする必要があります。元々どこでそのコードを見つけたのかは覚えていませんが、書いている間に見つけることができた最初の例はgeoffrich/sveltekit-satoriからなので、楽になった分彼らに感謝します。

astro.config.mjsに移動し、以下のメソッドを追加します。また、@resvg/resvg-jsパッケージをViteの依存関係最適化から除外する必要があります。そうしないと変な挙動が発生します。

export default defineConfig({
  ...

  vite: {
    plugins: [rawFonts(['.ttf'])],
    optimizeDeps: { exclude: ['@resvg/resvg-js'] }
  },
});

function rawFonts(ext) {
  return {
    name: 'vite-plugin-raw-fonts',
    transform(_, id) {
      if (ext.some(e => id.endsWith(e))) {
        const buffer = fs.readFileSync(id);
        return {
          code: `export default ${JSON.stringify(buffer)}`,
          map: null
        };
      }
    }
  };
}

エンドポイントを作成する


画像を生成するエンドポイントを/api/og.png.tsで作成します。これは任意のフレームワークで行うことができますが、Astroを使用することにします。ファイルにsatorisatori-html@resvg/resvg-jsをインポートします。また、使用するフォントもインポートします(最低1つは必要です)。

import satori from 'satori';
import { html } from 'satori-html';
import { Resvg } from '@resvg/resvg-js';
import OpenSans from '../../../lib/OpenSans-Regular.ttf'

その後、リクエストを処理する関数getを定義します。htmlタグ付きテンプレートを使用してHTMLを取得し、JSXコンポーネントを返します。インラインスタイルでパスするか、またはtwプロパティに含むことでTailwindクラスを使用することもできます。

export async function get() {
  const out = html`<div tw="flex flex-col w-full h-full bg-white">
    <h1 tw="text-6xl text-center">Hello World</h1>
  </div>`
}

続いて、Satoriを使用してJSXコンポーネントをSVGにレンダリングします。Satoriにフォントとともに、SVGの高さと幅を渡します。

  let svg = await satori(out, {
    fonts: [
      {
        name: 'Open Sans',
        data: Buffer.from(OpenSans),
        style: 'normal'
      }
    ],
    height: 630,
    width: 1200
  });

new Resvgを使用してResvgにSVGをロードし、引数を渡します。.render()を呼び出してSVGを画像にレンダリングします。

  const resvg = new Resvg(svg, {
    fitTo: {
      mode: 'width',
      value: width
    }
  });

  const image = resvg.render();

最後に、PNGとして画像を返し、コンテンツタイプのヘッダーを設定し、ユースケースが静的な場合は画像をキャッシュします。

  return {
    headers: {
      'Content-Type': 'image/png',
      'Cache-Control': 'public, max-age=31536000, immutable'
    },
    body: image.asPng()
  }

それでおしまい!


http://localhost:3000/api/og.pngにアクセスして画像を表示します。HTMLを変更して、画像にどのような影響があるかを確認できます。これを使用して、ビルド時にOG画像を生成するか、SSRサポートを追加して、ランタイム時に画像を生成することができます。


読んでいただきありがとうございます。何か質問があれば、Twitterや新しいDiscordサーバーでお気軽にお問い合わせください。

こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
https://dev.to/otterlord/generate-opengraph-images-dynamically-i1j