インストールをしてからこの記事を見ることをお勧めします。今回のOGPの作成には Content Collections を使った方法で紹介します。
環境構築が終わったら次に以下のものをインストールします。パッケージ管理はお好きなものでどうぞこの記事ではnpmで進めます。
npm i satori sharp
yarn add satori sharp
pnpm i satori sharp
作るファイルは生成したogpを画像に変換する物と動的に読み込みを行うファイルの2つが必要です。satoriのデザインプレビューしたい場合はローカルで実行するかVercel OG Image Playgroundのサイトで確認してください。satori内部にあるspanタグは自分で変数を定義してください。
import sharp from 'sharp'import satori from 'satori' export async function getOgImage(text: string, tags: string[]) { const fontRegular = await fetchFont("サイトのタイトル名" + tags + '#' + "コピーライト", fontFamily, 500) const fontBold = await fetchFont(text + ("サイトのタイトル名", fontFamily, 700) const svg = await satori(<div style={{ height: '100%', width: '100%', display: 'flex', backgroundColor: '#fefefe', padding: '48px', fontFamily: `sans-serif`, }} > <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-between', height: '100%', width: '100%', border: '6px solid #1e1e1e', backgroundColor: '#FFFFFF', borderRadius: '20px', color: '#333', padding: '48px', }} > <div style={{ display: 'flex', flexDirection: 'column', }} > <div style={{ display: 'flex', fontSize: '48px', fontWeight: 700, wordBreak: 'break-all', }} > <span>タイトル名</span> </div> <div style={{ display: 'flex', flexWrap: 'wrap', alignItems: 'center', marginTop: 12, }} > <div style={{ display: 'flex', fontSize: 24, fontWeight: 500, backgroundColor: 'rgb(229,231,235)', padding: '4px 24px', borderRadius: 9999, marginRight: 12, marginBottom: 12, }} > <span>タグ名</span> </div> </div> </div> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', width: '100%', fontSize: '24px', }} > <span>サイトの著作名</span> <span>サイトのタイトル名</span> </div> </div> </div>, { width: 1200, height: 630, fonts: [ { name: fontFamily, data: fontRegular, weight: 500, style: 'normal', }, { name: fontFamily, data: fontBold, weight: 700, style: 'normal', }, ], }, ) //SvgからPngに変換して返している return await sharp(Buffer.from(svg)).png({ compressionLevel: 9, quality: 50 }).toBuffer()} //必要なフォントの文字だけTTF形式でダウンロード関数async function fetchFont(text: string, font: string, weight: number): Promise<ArrayBuffer> { const API = `https://fonts.googleapis.com/css2?family=${font}:wght@${weight}&text=${encodeURIComponent(text)}` const css = await ( await fetch(API, { headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; de-at) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/5.0.5 Safari/533.21.1', }, }) ).text() const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/) if (!resource) { throw new Error('フォントの取得に失敗しました。') } const res = await fetch(resource[1]) return res.arrayBuffer()}
生成に成功するとこんなものができると思います。
次にアクセス時に動的に生成するファイルを作成します。import文の「getOgImage」呼び出しは自分が置いてる場所に指定してください。Astroの場合は、タイトル名は必須項目なので、Null合体演算子は任意で削除してください。
import type { APIContext } from 'astro'import { getCollection } from 'astro:content'import { getOgImage } from 'path' export async function getStaticPaths() { const posts = await getCollection('blog') return posts.map((post) => ({ params: { slug: post.slug, }, }))} export async function GET({ params }: APIContext) { const { slug } = params if (!slug) return { status: 404 } const post = (await getCollection('blog')).find((post) => post.slug === slug) if (!post) return { status: 404 } const body = await getOgImage(post.data.title ?? 'タイトル名なし', post.data.tags) return new Response(body, { headers: { 'content-type': 'image/png', }, })}
次に動的にタグを取得します。このコードは「タグ名」と書かれた場所にgetOgImage関数の引数tagsを呼び出しmap関数を実装してください。
<div style={{ display: 'flex', fontSize: 24, fontWeight: 500, backgroundColor: 'rgb(229,231,235)', padding: '4px 24px', borderRadius: 9999, marginRight: 12, marginBottom: 12, }} > <span>タグ名</span> </div>
{tags.map((tag, i) => ( <div key={i} style={{ display: 'flex', fontSize: 24, fontWeight: 500, backgroundColor: 'rgb(229,231,235)', padding: '4px 24px', borderRadius: 9999, marginRight: 12, marginBottom: 12, }} > <span>{'#' + tag}</span> </div> ))}
これで終わりです。後は、テストして確認してください。