Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Puppeteerでつくる画像と動画 / images and videos made with puppeteer

297a42b94a1bda236982ec1cd81089b6?s=47 mottox2
July 17, 2021

Puppeteerでつくる画像と動画 / images and videos made with puppeteer

297a42b94a1bda236982ec1cd81089b6?s=128

mottox2

July 17, 2021
Tweet

Transcript

  1. None
  2. ࣗݾ঺հ • ΋ͬͱʢ@mottox2ʣ • KODANSHAtech LLC.͋ • ि4ۈ຿ͷσβΠϯΤϯδχΞ • ڵຯ:

    σβΠϯγεςϜ • झຯ: Apex Legends
  3. ࠓ೔࿩͢͜ͱ • PuppeteerΛ࢖ͬͨը૾࡞੒ํ๏ • PuppeteerΛ࢖ͬͨಈը࡞੒ํ๏ • զʑ͸Կ͕Ͱ͖Δͷ͔

  4. λΠϜϥΠϯͷ͋Ε Ͳ͏ͭ͘Γ·͔͢ʁ • GitHub • Qiita • Zenn • ࣭໰ശ

  5. Webٕज़Ͱը૾࡞੒ • Canvasͷը૾Խ • ΫϥΠΞϯτʹґଘ͢ΔɾCanvasࣗମ͕ѻ͍ʹ͍͘ • ը૾഑৴SaaSͷར༻ • ָ͚ͩͲࣗ༝౓͕௿͍ •

    ✅ Puppeteerͷར༻ • αʔόʔαΠυ͕ඞਢ͕ͩࣗ༝౓͕ߴ͍ɻ
  6. Puppeteerͱ͸ • ChromeΛૢ࡞͢ΔAPIΛఏڙ͢ΔϥΠϒϥϦ • SPAͷΫϩʔϦϯάɺUIςετ΍ϑΥʔϜࣗಈૹ৴ͳͲͷ༻్͕૝ఆ ͞Ε͍ͯΔ • εΫϦʔϯγϣοτ΋ࡱΕΔ

  7. 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)
  8. Puppeteerͷίʔυྫ const generateHTML = (props) => { return `<html> <head>

    <style>…</style> </head> <body> <!— contents —> </body> </html>` }
  9. Puppeteerͱ͸ • ϒϥ΢β্Ͱͷ༷ʑͳදݱΛ࢖ͬͨը૾͕࡞ΕΔɻ • ࣄྫΛ঺հ͠·͢ɻ

  10. ࠓճ࡞ͬͨσϞͷ঺հ • ಉਓࢽଈചձͷެࣜαΠτ • αʔΫϧओ͕൦෍෺Λొ࿥͢Δ • ൦෍෺ͷOGը૾Λ͖Ε͍ʹݟ͍ͤͨ • Cloud Functions্ͰPuppeteerͷεΫϦʔϯγϣοτΛ࡞੒ɻϨεϙ

    ϯεͱͯ͠ฦ͍ͯ͠Δɻ
  11. ࠷ॳʹ࡞ͬͨ΋ͷ

  12. 🤔 ը૾ͱͯ͠ݟΔͱɺதࠇ͕ؾʹͳΔɻ

  13. จࣈ٧ΊΛվળ͢Δ • CSSͷfont-feature-settingsϓϩύςΟΛར༻͢Δ

  14. 👍 క·ͬͨҹ৅ʹͳͬͨ

  15. 🤔 վߦҐஔ͕ؾʹͳΔɻ୯ޠͷ్தͰվߦͨ͘͠ͳ͍ɻ

  16. վߦҐஔΛվળ͢Δ • ܗଶૉղੳΛ΍ͬͯɺจࣈྻΛվߦ͍͍ͯ͠ҐஔͰ෼ׂ͢Δ

  17. վߦҐஔΛվળ͢Δ • Kuromojin.js※Λ࢖ͬͯܗଶૉղੳɺ͍͍ײ͡ʹ෼ׂ͓ͯ͘͠ • ෼ׂͨ͠จࣈΛ඼ࢺ΍จࣈछྨʹ߹Θͤͯ݁߹͢Δ ※ Kuromoji.jsͷϥούʔ

  18. վߦҐஔΛվળ͢Δ • spanͰ۠੾ͬͯno-wrap΍Ε͹վߦҐஔͷίϯτϩʔϧ͕Մೳ • ͳΜ͔มͳέʔε΋͋Δ͚Ͳɺ͓͓ΉͶΑͦ͞͏ • ✅ σϞ: εΫγϣ൛ͱHTML൛ͷൺֱ

  19. 💡 վળ͞Εͨ

  20. ରԠલޙͷൺֱ • Before/After

  21. ରԠલޙͷൺֱ • Before/After

  22. ͜͜·Ͱͷ·ͱΊ • ׳Ε਌͠Μͩϒϥ΢βͰදݱͰ͖Δ΋ͷΛ࢖ͬͯը૾͕࡞ΕΔɻ • JavaScript΋࢖͑͹Ͱ͖Δ͜ͱ͕૿͑Δɻ • ྫʹڍ͛ͨܗଶૉղੳ΍ɺυϛφϯτΧϥʔͷநग़ͳͲ • ʢͨͩ͠ɺPhotoshopͰ࡞ͬͨํָ͕ʣ

  23. ಈըʹ֦ு͢Δ

  24. ಈըΛͭ͘Δ • ਅͬઌʹࢥ͍ͭ͘ͷ͕ɺϒϥ΢βͷಈ࡞Λ࿥ը͢Δख๏ɻ • ՄೳͰ͸͋Δ͕҆ఆͨ͠FPSΛҡ࣋Ͱ͖ͳ͍ɻ • ࿥ըײ͕ڧ͘ɺಈըͱ͸͍͍ʹ͍͘ɻ

  25. ಈըΛͭ͘Δ • ָͳͷͰɺFFmpegΛ࢖͍·͢ɻ • σίʔυɺΤϯίʔυɺτϥϯείʔυɺϛοΫεɺσϛοΫεɺ ετϦʔϜɺϑΟϧλϦϯάͳͲΛ݉Ͷඋ͑ͨπʔϧ • CLI΋͔ͬ͠Γ༻ҙ͞Ε͍ͯΔɻ • ࠓճ͸ѻ͍·ͤΜ͕wasm΋͋Γ·͢ɻ

  26. ಈըΛͭ͘Δ • ֤ϑϨʔϜͷը૾Λ༻ҙͯ͠ԼهίϚϯυΛ࣮ߦ͢Δɻ • ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt

    yuv420p -y out.mp4
  27. ಈըΛͭ͘Δ // child_processͰ࣮ߦ͢Δɻ͜ΕͰNode.js͔Βಈը͸࡞ΕΔɻ const { execSync } = require('child_process') ;

    execSync('ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt yuv420p -y out.mp4')
  28. ֤ϑϨʔϜͷը૾Λ࡞Δ • ݱࡏͷframeΛҾ਺ͱͯ͠౉ͨ͠ΒɺͦͷframeͷεΫγϣ͕ؼͬͯ͘ ΔΠϝʔδ • 30fpsͷಈըͰ͋Ε͹ 1frame = 0s, 2frame

    = 1/30sͱରԠ͢Δɻ
  29. ֤ϑϨʔϜͷը૾Λ࡞Δ 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() ; })();
  30. ಈըԽ͢Δ • ֤ϑϨʔϜͷը૾ΛFFmpegʹ৯ΘͤͨΒ׬੒ɻ

  31. མͱ݀͠ • Frame͝ͱͷHTMLΛར༻͢ΔͨΊɺCSSΞχϝʔγϣϯ͕࢖͑ͳ͍ • LinearҎ֎ͷΞχϝʔγϣϯʹ͸ͻͱखؒඞཁ

  32. OSSΛར༻͢Δ • ࣗલͰશ෦༻ҙ͢Δͷ͸ے͕ѱ͍ͷͰOSSΛ࢖͏ɻ • RemotionΛ࢖͏ͱΑ͍ɻ

  33. Remotion

  34. Remotion • ReactΛϨϯμϦϯάͨ݁͠ՌΛPuppeteerͰੜ੒ɺFFmpegͰ݁߹͠ ͨ΋ͷΛు͖ग़ͯ͘͠ΕΔ • طଘͷಈը΋ซ༻Մೳ • Webٕज़ͱطଘٕज़ʢAfter EffectsʣͷϋΠϒϦου͕Մೳɻ •

    ݸਓ΍খن໛ͳ૊৫ͳͲͰ͋Ε͹ແྉͰར༻Մೳɻ • ৄ͘͠͸ϥΠηϯεΛ֬ೝ͢Δ͜ͱɻ
  35. 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 } / > </ > )
  36. զʑʹ͸Կ͕Ͱ͖Δͷ͔

  37. PuppeteerΛར༻͢ΔϝϦοτ • ෳ਺ͷٕज़Λ૊Έ߹Θͤͯ࡞ΕΔ • Canvas΍WebGLΛ෦෼తʹऔΓೖΕΒΕΔɻ • ୯ಠͰ΍Γ͖Δʹ͸ݫ͍͠දݱͰ΋࡞Γ΍͍͢ɻ • ͢ͰʹWebʹ͋Δࢿ࢈Λ࢖ͬͨ΋ͷΛը૾ԽɾಈըԽ͢Δ͜ͱͰϝ Ϧοτ͕ੜ·ΕΔɻ

    • σʔλϏδϡΞϥΠθʔγϣϯͷ·ͱΊͱ͔ɻ
  38. ࣮ੈքͰ࢖͏ʹ͸ϋʔυϧ͕ • 🤔 ࣮͸ɺಈը࡞੒ͷSaaSͷํ͕खܰͰߴ඼࣭ͳ΋ͷΛ࡞Γ΍͍͢ • AfterEffectsΛ࢖͏ํ͕ϕλʔͳ͜ͱ͕ଟ͍ɻ • ࡞੒ର৅࣍ୈͰϝϦοτʹͳΔ෦෼͸ੜ·Εͯ͘Δͱࢥ͏ɻ • ձࣾͰ΋࢖͍ॴΛ࢕͍ͬͯΔ

    • ಈը੍࡞Λษڧͯͨ͠ΒɺAfterEffectsͷ࿅౓্͕͕ͬͨɻ