はじめに
ブログを作ろうと思い、ある程度完成したので初投稿してみる。SNS等でURLを投稿した際にSEOを意識してOG系のmeta情報を動的に反映させたいと思い、titleやdescriptionは簡単に設定できたが、画像をどうすればいいか悩んでいた。するとVercelがNext.js向けにvercel/ogというライブラリを開発していたので、これを参考に実装できないか検討してみた。関連ライブラリのインストール
Satoriのインストール
vercel/ogは内部的にsatoriというライブラリを利用しているのでインストールする。satoriはJSXやReact LikeなobjectからSVGを生成するライブラリ。$ yarn add satori
vercel/og及びsatoriはOGイメージを動的に生成するAPIのエンドポイントをjsxで簡単に作成することができるが、今回はNuxt.jsのためjsファイルで作成する。(jsxでもできるのかは不明)Resvgのインストール
svgをpng形式に変換するためResvgをインストールする。$ yarn add @resvg/resvg-js
APIのエンドポイント作成
/server/api にog.tsファイルを作成する。内部的な実装は別ファイルに切り出すことで、コードの見通しが良くなり、ここでは画像が生成されるんだろうと予想がつく。import { generateImage } from '~/lib/generateImg'
export default defineEventHandler(async (event) => {
const query = getQuery(event)
const { title, fontFamily } = query
const ogImage = await generateImage({ title, fontFamily })
return ogImage
})
generateImage.ts
import satori, { SatoriOptions } from 'satori'
import { Resvg } from '@resvg/resvg-js'
import { loadGoogleFont } from '../loadGoogleFont'
import { Props } from './type'
const BASE_FONT_FAMILY = 'M PLUS Rounded 1c'
const PLOT_WIDTH = 1200
const PLOT_HEIGHT = 630
export const generateImage = async ({
title,
fontFamily,
}: Props): Promise<Buffer> => {
const fontData = await loadGoogleFont({
family: fontFamily ?? BASE_FONT_FAMILY,
})
const element = //ここにReactLikeなオブジェクトを書く
const options: SatoriOptions = {
width: PLOT_WIDTH,
height: PLOT_HEIGHT,
fonts: [
{
name: fontFamily ?? BASE_FONT_FAMILY,
data: fontData,
weight: 400,
style: 'normal',
},
],
}
const svg = await satori(element, options)
const resvg = new Resvg(svg)
const pngData = resvg.render()
const pngBuffer = pngData.asPng()
return pngBuffer
}
loadGoogleFontは引数で渡されたfont-familyがあればそのfontを、なければBASE_FONTをgoogle fontから読み込んでいる。
このあたりは、Cloudflare Workers で画像生成の記事とhttps://github.com/kvnang/workers-og/blob/main/packages/workers-og/src/font.tsの実装を参考にしている。
Vercelへデプロイする設定
vercelでserver/apiを有効にするためにnuxt.config.tsへ以下の設定を追記する。nitro: {
preset; 'vercel',
}
あとは各ページに設定してVercelにデプロイして確認するだけ。Vercelのデプロイ設定
vercelの設定を以下のようにOverrideする。
meta関連タグの設定
あとは[id].vue等の動的に変化させたいページに上記で設定したエンドポイントを設定する。ついでに他のmetaタグも設定しておく。
<template> <Title>{{ article.title }}</Title> <Meta name="description" :content="article.description" /> <Meta property="og:title" :content="article.title" /> <Meta property="og:url" :content="config.baseUrl + route.path" /> <Meta property="og:image" :content="`${config.baseUrl}/api/og?title=${encodeURI(article.title)}`" /> <Meta property="og:description" :content="article.description" />
</template>
OGイメージの確認
queryでtitleを指定しない場合はデフォルトのメッセージを表示するようにしている。/api/og
/api/og?title=Hello World
参考
vercel/satoriyisibl/resvg-js
Cloudflare Workers で画像生成