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
Puppeteerでつくる画像と動画 / images and videos made wit...
Search
mottox2
July 17, 2021
Technology
780
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
July 17, 2021
More Decks by mottox2
See All by mottox2
つくり方を変えていく | change-how-we-build
mottox2
2
1.4k
もう一歩進めたい OG画像の動的生成
mottox2
7
2.7k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.6k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4.1k
手触りのよいウェブを考える / 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 Technology
See All in Technology
Data + AI Summit 2026 イベントレポート: 「AIがビジネスで意思決定するデータ基盤」へ
nek0128
0
280
変更し続けられるシステムをどう保つか — AI時代のSSoTという設計原則
kawauso
1
240
インシデント事例と パッケージの全量解析に学ぶ ソフトウェアサプライチェーンの守り方 / supply-chain-attack-defense
flatt_security
0
140
OpenTelemetryにおけるGoのゼロコード・コンパイル時計装について #fukuokago
quiver
0
130
ruby.wasmとPicoRuby.wasmに対応した仮想DOMライブラリを作ってる話 #kaigieffect_kaigi
sue445
PRO
0
150
Amplify Gen2でbackend.tsにCDKを定義する/しない事によるCDKの挙動の違いとユースケース
smt7174
1
430
AmplifyHostingConstructからSSRフレームワークのためのホスティング設計を考察する/amplify-hosting-construct
fossamagna
1
250
仕様駆動開発、導入半年。「本当に速くなってるの?」にデータで答える / AICon2026_hirakawa
rakus_dev
0
130
伝票作成AIエージェントを支える、LLMOpsとインフラの選択肢 / AICon2026_takeda
rakus_dev
0
120
SoccerMaster: A Vision Foundation Model for Soccer Understanding
kzykmyzw
0
150
AIが実装を自走する時代の認知負債との戦い
lycorptech_jp
PRO
1
500
Claude Code公式skillで 自分の仕事を少しずつ手放そう!(Claude Code開発ノウハウ大公開スペシャル by クラスメソッド)
kaym
1
500
Featured
See All Featured
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
470
Claude Code のすすめ
schroneko
67
230k
Visualization
eitanlees
152
17k
Bash Introduction
62gerente
615
220k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
550
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
420
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.8k
Between Models and Reality
mayunak
4
370
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.6k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
390
Transcript
None
ࣗݾհ • ͬͱʢ@mottox2ʣ • KODANSHAtech LLC.͋ • ि4ۈͷσβΠϯΤϯδχΞ • ڵຯ:
σβΠϯγεςϜ • झຯ: Apex Legends
ࠓ͢͜ͱ • PuppeteerΛͬͨը૾࡞ํ๏ • PuppeteerΛͬͨಈը࡞ํ๏ • զʑԿ͕Ͱ͖Δͷ͔
λΠϜϥΠϯͷ͋Ε Ͳ͏ͭ͘Γ·͔͢ʁ • GitHub • Qiita • Zenn • ࣭ശ
Webٕज़Ͱը૾࡞ • Canvasͷը૾Խ • ΫϥΠΞϯτʹґଘ͢ΔɾCanvasࣗମ͕ѻ͍ʹ͍͘ • ը૾৴SaaSͷར༻ • ָ͚ͩͲࣗ༝͕͍ •
✅ Puppeteerͷར༻ • αʔόʔαΠυ͕ඞਢ͕ͩࣗ༝͕ߴ͍ɻ
Puppeteerͱ • ChromeΛૢ࡞͢ΔAPIΛఏڙ͢ΔϥΠϒϥϦ • SPAͷΫϩʔϦϯάɺUIςετϑΥʔϜࣗಈૹ৴ͳͲͷ༻్͕ఆ ͞Ε͍ͯΔ • εΫϦʔϯγϣοτࡱΕΔ
Puppeteerͷίʔυྫ Const puppeteer = require(‘puppeteer’) const getScreenshot = async (html)
=> { const browser = await puppeteer.launch() const page = await browser.newPage(); await page.setContent(html); await page.setViewport({ width: 1200, height: 630 }); const file = await page.screenshot(); // await page.screenshot({ path: ‘dest/screenshot.png’ }); return file } const html = generateHTML(props) getScreenshot(html)
Puppeteerͷίʔυྫ const generateHTML = (props) => { return `<html> <head>
<style>…</style> </head> <body> <!— contents —> </body> </html>` }
Puppeteerͱ • ϒϥβ্Ͱͷ༷ʑͳදݱΛͬͨը૾͕࡞ΕΔɻ • ࣄྫΛհ͠·͢ɻ
ࠓճ࡞ͬͨσϞͷհ • ಉਓࢽଈചձͷެࣜαΠτ • αʔΫϧओ͕൦Λొ͢Δ • ൦ͷOGը૾Λ͖Ε͍ʹݟ͍ͤͨ • Cloud Functions্ͰPuppeteerͷεΫϦʔϯγϣοτΛ࡞ɻϨεϙ
ϯεͱͯ͠ฦ͍ͯ͠Δɻ
࠷ॳʹ࡞ͬͨͷ
🤔 ը૾ͱͯ͠ݟΔͱɺதࠇ͕ؾʹͳΔɻ
จࣈ٧ΊΛվળ͢Δ • CSSͷfont-feature-settingsϓϩύςΟΛར༻͢Δ
👍 క·ͬͨҹʹͳͬͨ
🤔 վߦҐஔ͕ؾʹͳΔɻ୯ޠͷ్தͰվߦͨ͘͠ͳ͍ɻ
վߦҐஔΛվળ͢Δ • ܗଶૉղੳΛͬͯɺจࣈྻΛվߦ͍͍ͯ͠ҐஔͰׂ͢Δ
վߦҐஔΛվળ͢Δ • Kuromojin.js※Λͬͯܗଶૉղੳɺ͍͍ײ͡ʹׂ͓ͯ͘͠ • ׂͨ͠จࣈΛࢺจࣈछྨʹ߹Θͤͯ݁߹͢Δ ※ Kuromoji.jsͷϥούʔ
վߦҐஔΛվળ͢Δ • spanͰ۠ͬͯno-wrapΕվߦҐஔͷίϯτϩʔϧ͕Մೳ • ͳΜ͔มͳέʔε͋Δ͚Ͳɺ͓͓ΉͶΑͦ͞͏ • ✅ σϞ: εΫγϣ൛ͱHTML൛ͷൺֱ
💡 վળ͞Εͨ
ରԠલޙͷൺֱ • Before/After
ରԠલޙͷൺֱ • Before/After
͜͜·Ͱͷ·ͱΊ • ׳Ε͠ΜͩϒϥβͰදݱͰ͖ΔͷΛͬͯը૾͕࡞ΕΔɻ • JavaScript͑Ͱ͖Δ͜ͱ͕૿͑Δɻ • ྫʹڍ͛ͨܗଶૉղੳɺυϛφϯτΧϥʔͷநग़ͳͲ • ʢͨͩ͠ɺPhotoshopͰ࡞ͬͨํָ͕ʣ
ಈըʹ֦ு͢Δ
ಈըΛͭ͘Δ • ਅͬઌʹࢥ͍ͭ͘ͷ͕ɺϒϥβͷಈ࡞Λը͢Δख๏ɻ • ՄೳͰ͋Δ͕҆ఆͨ͠FPSΛҡ࣋Ͱ͖ͳ͍ɻ • ըײ͕ڧ͘ɺಈըͱ͍͍ʹ͍͘ɻ
ಈըΛͭ͘Δ • ָͳͷͰɺFFmpegΛ͍·͢ɻ • σίʔυɺΤϯίʔυɺτϥϯείʔυɺϛοΫεɺσϛοΫεɺ ετϦʔϜɺϑΟϧλϦϯάͳͲΛ݉Ͷඋ͑ͨπʔϧ • CLI͔ͬ͠Γ༻ҙ͞Ε͍ͯΔɻ • ࠓճѻ͍·ͤΜ͕wasm͋Γ·͢ɻ
ಈըΛͭ͘Δ • ֤ϑϨʔϜͷը૾Λ༻ҙͯ͠ԼهίϚϯυΛ࣮ߦ͢Δɻ • ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt
yuv420p -y out.mp4
ಈըΛͭ͘Δ // child_processͰ࣮ߦ͢Δɻ͜ΕͰNode.js͔Βಈը࡞ΕΔɻ const { execSync } = require('child_process') ;
execSync('ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt yuv420p -y out.mp4')
֤ϑϨʔϜͷը૾Λ࡞Δ • ݱࡏͷframeΛҾͱͯͨ͠͠ΒɺͦͷframeͷεΫγϣ͕ؼͬͯ͘ ΔΠϝʔδ • 30fpsͷಈըͰ͋Ε 1frame = 0s, 2frame
= 1/30sͱରԠ͢Δɻ
֤ϑϨʔϜͷը૾Λ࡞Δ const puppeteer = require("puppeteer-core" ) const { getHtml }
= require("./template") ; (async () => { const props = {} ; const browser = await puppeteer.launch( { executablePath : "/Applications/Google Chrome.app/Contents/ MacOS/Google Chrome" , }) ; const page = await browser.newPage() ; for (let i = 1; i <= 60; i++) { const path = `./tmp/frame_${i}.png` ; const html = getHtml({ ...props, frame }) ; await page.setContent(html) ; await page.setViewport({ width: 1200, height: 630 }) ; await page.screenshot( { path , }) ; } await browser.close() ; })();
ಈըԽ͢Δ • ֤ϑϨʔϜͷը૾ΛFFmpegʹ৯ΘͤͨΒɻ
མͱ݀͠ • Frame͝ͱͷHTMLΛར༻͢ΔͨΊɺCSSΞχϝʔγϣϯ͕͑ͳ͍ • LinearҎ֎ͷΞχϝʔγϣϯʹͻͱखؒඞཁ
OSSΛར༻͢Δ • ࣗલͰશ෦༻ҙ͢Δͷے͕ѱ͍ͷͰOSSΛ͏ɻ • RemotionΛ͏ͱΑ͍ɻ
Remotion
Remotion • ReactΛϨϯμϦϯάͨ݁͠ՌΛPuppeteerͰੜɺFFmpegͰ݁߹͠ ͨͷΛు͖ग़ͯ͘͠ΕΔ • طଘͷಈըซ༻Մೳ • Webٕज़ͱطଘٕज़ʢAfter EffectsʣͷϋΠϒϦου͕Մೳɻ •
ݸਓখنͳ৫ͳͲͰ͋ΕແྉͰར༻Մೳɻ • ৄ͘͠ϥΠηϯεΛ֬ೝ͢Δ͜ͱɻ
Remotionͷίʔυྫ import { useCurrentFrame } from "remotion" ; export const
MyVideo = () => { // ݱࡏͷϑϨʔϜΛऔಘ const frame = useCurrentFrame(); return ( <div style={{ flex: 1, justifyContent: "center", alignItems: "center" }} > The current frame is {frame} . </div > ) ; } ; export const RemotionVideo: React.FC = () => ( < > <Compositio n id="MyVideo " component={MyVideo } durationInFrames={150 } fps={30 } width={1920 } height={1080 } / > </ > )
զʑʹԿ͕Ͱ͖Δͷ͔
PuppeteerΛར༻͢ΔϝϦοτ • ෳͷٕज़ΛΈ߹Θͤͯ࡞ΕΔ • CanvasWebGLΛ෦తʹऔΓೖΕΒΕΔɻ • ୯ಠͰΓ͖Δʹݫ͍͠දݱͰ࡞Γ͍͢ɻ • ͢ͰʹWebʹ͋Δࢿ࢈ΛͬͨͷΛը૾ԽɾಈըԽ͢Δ͜ͱͰϝ Ϧοτ͕ੜ·ΕΔɻ
• σʔλϏδϡΞϥΠθʔγϣϯͷ·ͱΊͱ͔ɻ
࣮ੈքͰ͏ʹϋʔυϧ͕ • 🤔 ࣮ɺಈը࡞ͷSaaSͷํ͕खܰͰߴ࣭ͳͷΛ࡞Γ͍͢ • AfterEffectsΛ͏ํ͕ϕλʔͳ͜ͱ͕ଟ͍ɻ • ࡞ର࣍ୈͰϝϦοτʹͳΔ෦ੜ·Εͯ͘Δͱࢥ͏ɻ • ձࣾͰ͍ॴΛ͍ͬͯΔ
• ಈը੍࡞Λษڧͯͨ͠ΒɺAfterEffectsͷ࿅্͕͕ͬͨɻ