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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
mottox2
July 17, 2021
Technology
790
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.9k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.6k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4.1k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4.2k
手触りのよいウェブを考える / better-mobile-web
mottox2
3
2.1k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
740
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.3k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
34k
Other Decks in Technology
See All in Technology
紙 to デジタル
ichien178
0
360
ハッカソンで入賞した話 @ Findy LT
asari194617
0
1.5k
みてねにおけるAI-DLC導入活動とAIドリブン開発の現在地/JAWS-UG AI-DLC #2
isaoshimizu
2
280
サイボウズ 開発本部採用ピッチ / Cybozu Engineer Recruit
cybozuinsideout
PRO
12
86k
AIエージェントのためのデータ設計
daiz21
0
520
AWSとGitHub Actionsの責任境界と 組織で安全に使用する取り組み
nealle
1
220
Point Cloud as a Foreign Language for Multi-modal Large Language Model
takmin
0
300
DMMブックスのNext.js化を加速させるAI活用 / Migrating DMM Books to Next.js with AI
kentarom
0
180
[RSJ26] Flow as Flow: Modeling Robot Velocity Fields as Probability Velocity Fields
keio_smilab
PRO
0
110
markdown-poster Introduction
kazamori
0
400
暗号化?某ファイルストレージはどうなるの!? 3rd Partyとうまく付き合う秘密度ラベル設計
kasada
0
210
1000⼈規模のClaude Enterprise運⽤を「Oktaのグループ」と「Slack」に集約する
sansantech
PRO
1
500
Featured
See All Featured
The Limits of Empathy - UXLibs8
cassininazir
1
620
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Side Projects
sachag
455
43k
New Earth Scene 8
popppiees
3
2.5k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
390
Done Done
chrislema
186
16k
Paper Plane
katiecoart
PRO
2
53k
Building the Perfect Custom Keyboard
takai
2
850
Code Reviewing Like a Champion
maltzj
528
40k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Are puppies a ranking factor?
jonoalderson
2
3.8k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
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ͷ࿅্͕͕ͬͨɻ