Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up for free
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
July 17, 2021
Technology
0
420
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
July 17, 2021
Tweet
Share
More Decks by mottox2
See All by mottox2
Figma Plugin公開までの壁を乗り越える
mottox2
1
690
手触りのよいウェブを考える / better-mobile-web
mottox2
3
1.2k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
400
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
2.8k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
30k
プレイヤー目線の技術ブランディング / personal branding
mottox2
4
3.2k
JSXで作るDSL / jsx-dsl
mottox2
1
1.6k
SSRを検討する際にSSGも検討しませんか? / ssr or ssg
mottox2
15
9.3k
Next.js meets Netlify
mottox2
3
1.5k
Other Decks in Technology
See All in Technology
UIFlowの2.0がやってきた! / ビジュアルプログラミングIoTLT vol.13
you
0
200
CEXやDEXに依存しないブロックチェーン取引について考える
sbtechnight
0
310
デスマーチから身を守るたったひとつの方法
kwappa
1
2.7k
証明書って何だっけ? 〜AWSの中間CA移行に備える〜
minorun365
3
1.8k
聴覚障害のある大学生チームによる臆さない発言環境の形成
hayato_ukuk
0
1.7k
AKIBA.SaaS資料
yasumuusan
0
150
lt53
98_justdoit
0
110
1つのアプリを開発する複数の職能横断チームの運用と今後 ~ タクシーアプリ「GO」の現状と未来 ~
takahia1988
1
3.3k
それでもどうしてRecoilを使うのか / Harajuku.ts Meetup Recoil
okunokentaro
11
3.2k
re:Invent re:Cap / AWS Lambda Updates
bulbulpaul
1
140
2年で10→70人へ! スタートアップの 情報セキュリティ課題と施策
miekobayashi
1
170
アジャイルな組織改善〜手法とマインドセット〜
ishige
7
5.2k
Featured
See All Featured
Designing with Data
zakiwarfel
91
4.2k
Statistics for Hackers
jakevdp
784
210k
The Brand Is Dead. Long Live the Brand.
mthomps
48
2.9k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
318
19k
Fontdeck: Realign not Redesign
paulrobertlloyd
74
4.3k
Three Pipe Problems
jasonvnalue
89
8.9k
Agile that works and the tools we love
rasmusluckow
320
20k
Building Better People: How to give real-time feedback that sticks.
wjessup
346
17k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
44
14k
Building Your Own Lightsaber
phodgson
96
4.9k
The Language of Interfaces
destraynor
149
21k
Fashionably flexible responsive web design (full day workshop)
malarkey
396
63k
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ͷ࿅্͕͕ͬͨɻ