個人開発界隈ではOGP芸というものが流行っているみたいですね。
SNSにシェアする際に表示されるOGPの設定や画像をいい感じにするみたいなことのことですが、SPAではこれを生成するのが割と面倒だったりします。
自分が作っているg4でもOGPを使ったシェアを実装していますが、SSRを使って配信していて、割と設定が面倒だったりしています。
今回、なるべくSSRをしないでSPAのままやってみることはできないかというのをfly.ioで試してみました。
/image.png?i=0
で配信する。/
に?i=0
のようなパラメータをアクセスすると、入力した↑を含むOGPが反映されたhtmlを表示するhttps://og-image-sample.edgeapp.net?i=0
or
https://og-image-sample.edgeapp.net?i=1
これをtwitterやfacebookなどでシェアしてみてください。
事前にflyのcliをPCにインストールしておく
npmは必要ないです(あったほうが便利だけど最速なので)
必要なファイルは4つだけ
- index.js: なんか処理書くやつ
- template.html: テンプレートです。ここに情報を流し込む
- fly.yml: なんか設定書くやつ
- sample.svg: OGP芸したい適当なsvg。今回はURLによって内容を書き換えるため雑に{{title}}みたいなやつをここに書いて、テキスト置換でそこに内容を埋め込んでます。
``` html:template.html
templateはこんな感じになりました。
ogpのcontentは置き換えるので何でもいいです。とりあえずさり気なくg4のものを入れてアピールしておきます。
次に`index.js`です。こちらにすべての処理を書きます。
``` js:index.js
import { Image } from '@fly/image'
import { mount } from "@fly/fetch/mount"
// templateをテキストで取得するFunction
async function getTemplate() {
const resp = await fetch("file://src/template.html")
return await resp.text()
}
// svgをテキストで取得しつつ中身ちょっと埋め込めるFunction
async function getSvgText(title) {
const resp = await fetch("file://src/sample.svg")
const text = await resp.text()
// g4(https://www.g-g-g-g.games)ではReactでテキスト化されたsvgを吐いてるが、とりあえず単純に置換する
return text.replace('{{content}}', title)
}
// 画像形式のレスポンス作るFunction
async function responseImage(svgText) {
const svgResp = new Response(Buffer.from(svgText))
const buf = await svgResp.arrayBuffer()
const png = new Image(buf).png()
const result = await png.toBuffer()
return new Response(result.data, {
headers: {
'Content-Type': 'image/png',
'Content-Length': result.data.byteLength.toString(),
}
})
}
// 出力データのパターン
const TITLES = [
'g4 is pomodoro rpg!',
'fly.io de OGP!!',
]
// fly.ioのrouterみたいなやつ。ここに処理を書いてく。
const mounts = mount({
// このパスでogpの画像を生成する
'/image.png': async (req, init) => {
const url = new URL(req.url)
// URLからQueryStringを取得
const index = url.searchParams.get('i')
if (index !== '0' && index !== '1') {
return new Response('not found', { status: 404 })
}
// 対応したタイトルを取得
const title = TITLES[index]
// タイトルをsvgに埋め込んだテキストを作る
const svgText = await getSvgText(title)
// svgからpng画像を生成する
return responseImage(svgText)
},
// このパスをシェアする
'/': async (req, init) => {
const url = new URL(req.url)
// URLからQueryStringを取得
const index = url.searchParams.get('i')
if (index !== '0' && index !== '1') {
return new Response('not found', { status: 404 })
}
// 対応したタイトルを取得
const title = TITLES[index]
// テンプレートをparseして編集できるようにする
const doc = Document.parse(await getTemplate())
// テンプレートにOGPを埋め込む
doc.querySelector('meta[name="description"]').setAttribute('content', title)
doc.querySelector('meta[property="og:url"]').setAttribute('content', url.href)
doc.querySelector('meta[property="og:title"]').setAttribute('content', title)
doc.querySelector('meta[property="og:description"]').setAttribute('content', title)
doc.querySelector('meta[property="og:image"]').setAttribute('content', `${url.origin}/image.png?i=${index}`)
doc.querySelector('meta[property="og:image:alt"]').setAttribute('content', title)
// htmlを返す
return new Response(doc.documentElement.outerHTML, {
headers: { 'Content-Type': 'text/html' },
status: 200,
})
},
})
// リクエストをmountsの定義を使って処理するように設定する
fly.http.respondWith(mounts)
index.jsはこんな感じ。
/
はtemplate.html
のogpを書き換えて出すだけ。
/image
はsvgファイルを読み込んで文字列を置換したものをpngに変換して返してます。
fly.ymlやsvgファイルは特に特別な設定はないのでドキュメントや最後にソースを貼るのでそちらを見ていただければ。
あとは、fly.ioのコンソールでアプリを作って、fly deploy
するだけ。
再度になりますが、
https://og-image-sample.edgeapp.net?i=0
or
https://og-image-sample.edgeapp.net?i=1
これをtwitterやfacebookなどでシェアしてみてください。
以上になります。ソースはこちらにあります。
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント