$30 off During Our Annual Pro Sale. View Details »

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

mottox2
July 17, 2021

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

mottox2

July 17, 2021
Tweet

More Decks by mottox2

Other Decks in Technology

Transcript

  1. View Slide

  2. ࣗݾ঺հ
    • ΋ͬͱʢ@mottox2ʣ


    • KODANSHAtech LLC.͋


    • ि4ۈ຿ͷσβΠϯΤϯδχΞ


    • ڵຯ: σβΠϯγεςϜ


    • झຯ: Apex Legends

    View Slide

  3. ࠓ೔࿩͢͜ͱ
    • PuppeteerΛ࢖ͬͨը૾࡞੒ํ๏


    • PuppeteerΛ࢖ͬͨಈը࡞੒ํ๏


    • զʑ͸Կ͕Ͱ͖Δͷ͔

    View Slide

  4. λΠϜϥΠϯͷ͋Ε
    Ͳ͏ͭ͘Γ·͔͢ʁ


    • GitHub


    • Qiita


    • Zenn


    • ࣭໰ശ

    View Slide

  5. Webٕज़Ͱը૾࡞੒
    • Canvasͷը૾Խ


    • ΫϥΠΞϯτʹґଘ͢ΔɾCanvasࣗମ͕ѻ͍ʹ͍͘


    • ը૾഑৴SaaSͷར༻


    • ָ͚ͩͲࣗ༝౓͕௿͍


    • ✅ Puppeteerͷར༻


    • αʔόʔαΠυ͕ඞਢ͕ͩࣗ༝౓͕ߴ͍ɻ

    View Slide

  6. Puppeteerͱ͸
    • ChromeΛૢ࡞͢ΔAPIΛఏڙ͢ΔϥΠϒϥϦ


    • SPAͷΫϩʔϦϯάɺUIςετ΍ϑΥʔϜࣗಈૹ৴ͳͲͷ༻్͕૝ఆ
    ͞Ε͍ͯΔ


    • εΫϦʔϯγϣοτ΋ࡱΕΔ

    View Slide

  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)

    View Slide

  8. Puppeteerͷίʔυྫ
    const generateHTML = (props) => {


    return `




















    `


    }

    View Slide

  9. Puppeteerͱ͸
    • ϒϥ΢β্Ͱͷ༷ʑͳදݱΛ࢖ͬͨը૾͕࡞ΕΔɻ


    • ࣄྫΛ঺հ͠·͢ɻ

    View Slide

  10. ࠓճ࡞ͬͨσϞͷ঺հ
    • ಉਓࢽଈചձͷެࣜαΠτ


    • αʔΫϧओ͕൦෍෺Λొ࿥͢Δ


    • ൦෍෺ͷOGը૾Λ͖Ε͍ʹݟ͍ͤͨ


    • Cloud Functions্ͰPuppeteerͷεΫϦʔϯγϣοτΛ࡞੒ɻϨεϙ
    ϯεͱͯ͠ฦ͍ͯ͠Δɻ

    View Slide

  11. ࠷ॳʹ࡞ͬͨ΋ͷ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. վߦҐஔΛվળ͢Δ
    • Kuromojin.js※Λ࢖ͬͯܗଶૉղੳɺ͍͍ײ͡ʹ෼ׂ͓ͯ͘͠


    • ෼ׂͨ͠จࣈΛ඼ࢺ΍จࣈछྨʹ߹Θͤͯ݁߹͢Δ
    ※ Kuromoji.jsͷϥούʔ

    View Slide

  18. վߦҐஔΛվળ͢Δ
    • spanͰ۠੾ͬͯno-wrap΍Ε͹վߦҐஔͷίϯτϩʔϧ͕Մೳ


    • ͳΜ͔มͳέʔε΋͋Δ͚Ͳɺ͓͓ΉͶΑͦ͞͏


    • ✅ σϞ: εΫγϣ൛ͱHTML൛ͷൺֱ

    View Slide

  19. 💡 վળ͞Εͨ

    View Slide

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

    View Slide

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

    View Slide

  22. ͜͜·Ͱͷ·ͱΊ
    • ׳Ε਌͠Μͩϒϥ΢βͰදݱͰ͖Δ΋ͷΛ࢖ͬͯը૾͕࡞ΕΔɻ


    • JavaScript΋࢖͑͹Ͱ͖Δ͜ͱ͕૿͑Δɻ


    • ྫʹڍ͛ͨܗଶૉղੳ΍ɺυϛφϯτΧϥʔͷநग़ͳͲ


    • ʢͨͩ͠ɺPhotoshopͰ࡞ͬͨํָ͕ʣ

    View Slide

  23. ಈըʹ֦ு͢Δ

    View Slide

  24. ಈըΛͭ͘Δ
    • ਅͬઌʹࢥ͍ͭ͘ͷ͕ɺϒϥ΢βͷಈ࡞Λ࿥ը͢Δख๏ɻ


    • ՄೳͰ͸͋Δ͕҆ఆͨ͠FPSΛҡ࣋Ͱ͖ͳ͍ɻ


    • ࿥ըײ͕ڧ͘ɺಈըͱ͸͍͍ʹ͍͘ɻ

    View Slide

  25. ಈըΛͭ͘Δ
    • ָͳͷͰɺFFmpegΛ࢖͍·͢ɻ


    • σίʔυɺΤϯίʔυɺτϥϯείʔυɺϛοΫεɺσϛοΫεɺ
    ετϦʔϜɺϑΟϧλϦϯάͳͲΛ݉Ͷඋ͑ͨπʔϧ


    • CLI΋͔ͬ͠Γ༻ҙ͞Ε͍ͯΔɻ


    • ࠓճ͸ѻ͍·ͤΜ͕wasm΋͋Γ·͢ɻ

    View Slide

  26. ಈըΛͭ͘Δ
    • ֤ϑϨʔϜͷը૾Λ༻ҙͯ͠ԼهίϚϯυΛ࣮ߦ͢Δɻ


    • ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt yuv420p -y out.mp4

    View Slide

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

    execSync('ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt yuv420p -y out.mp4')

    View Slide

  28. ֤ϑϨʔϜͷը૾Λ࡞Δ
    • ݱࡏͷframeΛҾ਺ͱͯ͠౉ͨ͠ΒɺͦͷframeͷεΫγϣ͕ؼͬͯ͘
    ΔΠϝʔδ


    • 30fpsͷಈըͰ͋Ε͹ 1frame = 0s, 2frame = 1/30sͱରԠ͢Δɻ

    View Slide

  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()
    ;

    })();

    View Slide

  30. ಈըԽ͢Δ
    • ֤ϑϨʔϜͷը૾ΛFFmpegʹ৯ΘͤͨΒ׬੒ɻ

    View Slide

  31. མͱ݀͠
    • Frame͝ͱͷHTMLΛར༻͢ΔͨΊɺCSSΞχϝʔγϣϯ͕࢖͑ͳ͍


    • LinearҎ֎ͷΞχϝʔγϣϯʹ͸ͻͱखؒඞཁ

    View Slide

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


    • RemotionΛ࢖͏ͱΑ͍ɻ

    View Slide

  33. Remotion

    View Slide

  34. Remotion
    • ReactΛϨϯμϦϯάͨ݁͠ՌΛPuppeteerͰੜ੒ɺFFmpegͰ݁߹͠
    ͨ΋ͷΛు͖ग़ͯ͘͠ΕΔ


    • طଘͷಈը΋ซ༻Մೳ


    • Webٕज़ͱطଘٕज़ʢAfter EffectsʣͷϋΠϒϦου͕Մೳɻ


    • ݸਓ΍খن໛ͳ૊৫ͳͲͰ͋Ε͹ແྉͰར༻Մೳɻ


    • ৄ͘͠͸ϥΠηϯεΛ֬ೝ͢Δ͜ͱɻ

    View Slide

  35. Remotionͷίʔυྫ
    import { useCurrentFrame } from "remotion"
    ;

    export const MyVideo = () =>
    {

    // ݱࡏͷϑϨʔϜ਺Λऔಘ
    const frame = useCurrentFrame();


    return
    (

    "center", alignItems: "center" }}
    >

    The current frame is {frame}
    .

    >

    )
    ;

    }
    ;

    export const RemotionVideo: React.FC = () =>
    (

    <
    >

    n

    id="MyVideo
    "

    component={MyVideo
    }

    durationInFrames={150
    }

    fps={30
    }

    width={1920
    }

    height={1080
    }

    /
    >


    >

    )

    View Slide

  36. զʑʹ͸Կ͕Ͱ͖Δͷ͔

    View Slide

  37. PuppeteerΛར༻͢ΔϝϦοτ
    • ෳ਺ͷٕज़Λ૊Έ߹Θͤͯ࡞ΕΔ


    • Canvas΍WebGLΛ෦෼తʹऔΓೖΕΒΕΔɻ


    • ୯ಠͰ΍Γ͖Δʹ͸ݫ͍͠දݱͰ΋࡞Γ΍͍͢ɻ


    • ͢ͰʹWebʹ͋Δࢿ࢈Λ࢖ͬͨ΋ͷΛը૾ԽɾಈըԽ͢Δ͜ͱͰϝ
    Ϧοτ͕ੜ·ΕΔɻ


    • σʔλϏδϡΞϥΠθʔγϣϯͷ·ͱΊͱ͔ɻ

    View Slide

  38. ࣮ੈքͰ࢖͏ʹ͸ϋʔυϧ͕
    • 🤔 ࣮͸ɺಈը࡞੒ͷSaaSͷํ͕खܰͰߴ඼࣭ͳ΋ͷΛ࡞Γ΍͍͢


    • AfterEffectsΛ࢖͏ํ͕ϕλʔͳ͜ͱ͕ଟ͍ɻ


    • ࡞੒ର৅࣍ୈͰϝϦοτʹͳΔ෦෼͸ੜ·Εͯ͘Δͱࢥ͏ɻ


    • ձࣾͰ΋࢖͍ॴΛ࢕͍ͬͯΔ


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

    View Slide