Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
もう一歩進めたい OG画像の動的生成
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
mottox2
January 19, 2024
Programming
2.8k
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
もう一歩進めたい OG画像の動的生成
mottox2
January 19, 2024
More Decks by mottox2
See All by mottox2
つくり方を変えていく | change-how-we-build
mottox2
2
1.4k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.6k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4.1k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
790
手触りのよいウェブを考える / better-mobile-web
mottox2
3
2k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
730
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.3k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
34k
Other Decks in Programming
See All in Programming
yield再入門 #phpcon
o0h
PRO
0
810
ソフトウェア設計に溶けるインフラ ― AWS CDK のインフラ認識論
konokenj
3
680
変わらないものが、変わるものを決める — 意図駆動開発 × イベントソーシング × イミュータブル | What Doesn't Change Decides What Can — IDD × Event Sourcing × Immutability
tomohisa
0
600
Embedded SREと共に達成した会員管理システムのAWS移行 - SRE NEXT 2026 ランチスポンサーセッション
niftycorp
PRO
1
3.1k
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
410
[RVD26] Vibe Architecture en 2040 : Darwin a-t-il (enfin) eu raison des architectes ?
alexandretouret
0
110
광주소프트웨어마이스터고등학교 DevFest 특강 - 바이브 코딩 시대에서 주니어 개발자로 살아남는 방법
utilforever
1
160
自作OSでスライド発表する
uyuki234
1
4k
えっ!!コードを読まずに開発を!?
hananouchi
0
280
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
120
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
280
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
430
Featured
See All Featured
Automating Front-end Workflow
addyosmani
1370
210k
Documentation Writing (for coders)
carmenintech
77
5.4k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
780
Six Lessons from altMBA
skipperchong
29
4.4k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
270
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
AI: The stuff that nobody shows you
jnunemaker
PRO
9
850
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.1k
Writing Fast Ruby
sferik
630
63k
Transcript
#burikaigi_m もう一歩進めたい OG画像の動的生成 BuriKaigi 2024 @mottox2
#burikaigi_m @mottox2 UIデザインとウェブフロントエンド
#burikaigi_m
#burikaigi_m
#burikaigi_m OG画像 E サイトの情報を伝えるためのOpen Graphというプロトコ P E その中の画像をOG画像と呼んでいh E 特にXではtitleやdescriptionより画像の主張が強いの
で、 各サイトやサービスが工夫を凝らしている
#burikaigi_m @vercel/ogとは?
#burikaigi_m @vercel/og ( JSXからsvgを生成するsatoriとsvgからpngが画像に変 換するresvgをVercel上でまとめて使えるようにしたも) ( Next.jsに組み込まれていて簡単に利用できる JSX satori resvg
SVG PNG
#burikaigi_m 例えば < {{ height: , width: , display: ,
flexDirection: , alignItems: , justifyContent: , backgroundColor: , fontSize: , fontWeight: , }} > < {{ margin: }} > < ></ > </ > < {{ marginTop: }}>Hello, World</ > </ > div svg path path svg div div div style width viewBox fill style d style = = = = = = = '100%' '100%' 'flex' 'column' 'center' 'center' '#fff' "75" "0 0 75 65" "#000" '0 75px' "M37.59.25l36.95 64H.64l36.95-64z" 32 600 40
#burikaigi_m 例えば < < > < /> </ > <
/> < > < /> </ > < > < /> </ > < < > < /> </ > < > < /> </ > svg mask rect mask rect rect mask rect mask image mask rect mask mask rect mask width height viewBox xmlns id x y width height fill x y width height fill id x y width height id x y width height fill x y width height href preserveAspectRatio clip-path mas id x y width height fill id x y width height fill = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "800" "400" "0 0 800 400" "http://w "satori_om-id" "0" "0" "800" "400" "#fff" "0" "0" "800" "400" "#fff" "satori_cp-id-0" "363" "128" "75" "65" "satori_om-id-0" "363" "128" "75" "65" "#fff" "363" "128" "75" "65" "data:image/svg+xml;utf8,%3Csvg fill=%22%23000%22 xmlns=% "none" "url(#satori_cp-id-0)" "satori_om-id-0-0" "363" "128" "0" "65" "#fff" "satori_om-id-1" "304" "233" "193" "39" "#fff" clipPath clipPath
#burikaigi_m 例えば
#burikaigi_m @vercel/ogの嬉しさ ・ しんどさ 世の中の画像生成手法は座標を考えながら使う必要が あるが@vercel/ogではほとんど考えなく て良s 一方、
SatoriはHTMLのサブセッ トであり、 結構できなこと が多い
#burikaigi_m Playground https://satori-playground.vercel.app
#burikaigi_m サポート状況の確認 https://github.com/vercel/satori
#burikaigi_m 無事画像が作れるようになった ただ、 画像を作るだけではつまらない 自動作成された画像感を減らしたい
#burikaigi_m 自動作成されてる感はどこから来るのか? デザイン段階で決まってC 9 画像が浮いていC 9 フォントがデフォルト、 文字詰めが適b 9 Webっぽいグリッドを感じる
#burikaigi_m 標準で使われているフォントを使うと、 ダサさがある d 標準フォントでは太字指定が無視され@ d「」 、 。 などの役物の空きが気になる
#burikaigi_m デフォルト(Roboto) YakuHanJP
#burikaigi_m 標準で使われているフォントを使うと、 ダサさがある おすすめは qracさんが作られている YakuHanJP MergedのSemiboldかBolc Noto
Sans JPに対して、 半角サイズの役物 (句読点と 鉤括弧等の文字) を混ぜたフォント
#burikaigi_m 文字づめがされていない v 文字詰めをやるCSS `font-feature-settings: ‘palt’` はsatoriがサポートしていないため動かないi v → 場合によって代わりに
`letter-spacing: -0.02em` で全体的に詰めておくのもあり こんにちは世界 こんにちは世界 ツメなし ツメあり
#burikaigi_m 画像の使い方 そのまま使うとブログのアイキャッチ感があふれ` B 特定の形にトリミングしてみ` B 上からグラデーションの座布団を引G B ぼかしてみる
#burikaigi_m 視覚調整 Vercel 正しい位置に配置すると目の錯覚で小さく見える 世の中のロゴやフォントにはこの調整が入っている
#burikaigi_m 視覚調整の対応 R ウェブでは保守性のためスキップすることが多3 R 画像を作るならやるべ$ R マイナス側のマージンをつければOK
#burikaigi_m
#burikaigi_m 大切なこと R 何ができて、 何ができないかを知っておくこと ・ 伝えるこT R Web技術で作れることと、 Webのように作ることを混同
しない方がよ1 R レスポンシブとか考えなく てよく て、 特定のサイズに特化し たスタイリングを行えばよい
#burikaigi_m もう一歩、 プログラムで作る意味を見出したい
#burikaigi_m 何ができそうか? S ランダムに背景を選択すF S SVGを利用した動的なビジュアルを作F S 画像の色を考慮したビジュアルを作る
#burikaigi_m KODANSHAtech コーポレートブログの例
#burikaigi_m ランダムな背景を用意する S 真っ先に思いつくであろう活用U S ランダムに背景画像を切り替える実 S 完全ランダムにしてしまうと、 毎回変わってしまうので、 URLに対して一意にするための実装をするとよw
S シードを設定できるランダム関数の実装
#burikaigi_m class constructor = % if <= += return =
* % return - / let = new { ( ) { .seed seed ; ( .seed ) .seed ; } () { .seed .seed ; } () { ( . () ) ; } } rng ( ); console. (rng. ()); SeededRandom next nextFloat next SeededRandom log nextFloat seed this 2147483647 this 0 this 2147483646 this this 16807 2147483647 this 1 2147483646 12345 // 0から1の範囲で乱数を返す // 使用例 // シード値を設定 // 常に同じシード値で同じ乱数列を生成 generated by ChatGPT 同じURLでは同じ乱数が生成されるようにする
#burikaigi_m SVGを利用した動的なビジュアルを作る E vercel/ogはpngを生成する中間状態にSVGを利用して いるので、 SVGの自由度が高めになっていV E SVGを出し分けるJSXを利用して、 (普通なら実現しにく い)
動的なビジュアルを作る
#burikaigi_m
#burikaigi_m SVGでクリエイティブコーディング return ... = / / / = =
= = = = = = < return ... = / / / = = = = = = = < return ... = = / / / = = / / = = / / / < { }> < { blockSize blockSize blockSize b < { / } { / } { / } { < { / } { / } { / } { } </ > // T 270 if (value ) < { }> < { blockSize blockSize blockSize blockSize < { / } { / } { / } { } < { / } { / } { } /> </ > // X if (value ) < { }> < { } { blockSize blockSize blockSize bloc {/* < { } { blockSize blockSize blockSize < { } { blockSize blockSize blockSize bloc </ > // O 2 2 2 2 2 2 2 2 2 0.25 2 2 2 2 2 2 2 2 0.55 2 2 2 2 2 2 2 2 g groupProps path d rect x blockSize y blockSize width blockSize height blockSize rect y blockSize width blockSize height blockSize fill g g groupProps path d rect y blockSize width blockSize height blockSize fill rect width blockSize height blockSize fill g g groupProps path fill d path fill d path fill d g color color color color color color `M0 ${ } L 0 0 A ${ } ${ } 0 0 1 ${ `M${ } 0 L ${ } 0 A ${ } ${ `m${ } ${ } ${ } ${ `M${ } ${ } 0 0h${ }L${ `m${ } ${ } ${ }-${
#burikaigi_m return ... = / / / = = =
= = = = = < return ... = / / / = = = = = = = < return ... = = / / / = = / / = = / / / < { }> < { blockSize blockSize blockSize b < { / } { / } { / } { < { / } { / } { / } { } </ > // T 270 if (value ) < { }> < { blockSize blockSize blockSize blockSize < { / } { / } { / } { } < { / } { / } { } /> </ > // X if (value ) < { }> < { } { blockSize blockSize blockSize bloc {/* < { } { blockSize blockSize blockSize < { } { blockSize blockSize blockSize bloc </ > // O 2 2 2 2 2 2 2 2 2 0.25 2 2 2 2 2 2 2 2 0.55 2 2 2 2 2 2 2 2 g groupProps path d rect x blockSize y blockSize width blockSize height blockSize rect y blockSize width blockSize height blockSize fill g g groupProps path d rect y blockSize width blockSize height blockSize fill rect width blockSize height blockSize fill g g groupProps path fill d path fill d path fill d g color color color color color color `M0 ${ } L 0 0 A ${ } ${ } 0 0 1 ${ `M${ } 0 L ${ } 0 A ${ } ${ `m${ } ${ } ${ } ${ `M${ } ${ } 0 0h${ }L${ `m${ } ${ } ${ }-${ SVGでクリエイティブコーディング h vercel/ogでは画像アウトプット前の中間状態にSVGを 使っているので、 それを想定して書G h これらのパターンをランダムに並べている
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった 色を考慮しない場合
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった 特徴色を取得 輝度を特定の値に変更 グラデーションの値に利用 hsl(34, 28, 57) hsl(34,
28, 30)
#burikaigi_m 画像の色を考慮したビジュアルを作る 画像の各ピクセル情報から、 その画像の特徴的な色を取 り出し、 その色を画像の要素として利用すX 取り出した色をそのまま使うのではなく、 輝度を落として
利用すると使いやすa 画像処理は重いのでEdgeでは動かせず、 従来のサー バーレス上で動作、 または事前に保存しておく必要あり
#burikaigi_m 画像の色を考慮したビジュアルを作る import from import from import from const =
await const = await const = new const = const = new const = const = const = jpeg { FastAverageColor } ; { rgb2hsl, hsl2rgb } (imageUrl) image. () (image) jpeg. (uint8Array) (); [ , , , ] fac. (decoded.data) [ , , ] (r, g, b) (h, s, ) 'jpeg-js' 'fast-average-color' './utils' image imageArray uint8Array decoded fac r g b a h s l themeColor 0.3 fetch arrayBuffer Uint8Array decode FastAverageColor getColorFromArray4 rgb2hsl hsl2rgb
#burikaigi_m まとめ c Web技術で作れることと、 Webのように作ることを混同 しない方がよe c エンジニアリング的な視点とデザイナー的な視点を混ぜ て、 ちょっと凝った画像生成ができるかもしれない
#burikaigi_m Thank you!