Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

ࣗݾ঺հ • ΋ͬͱʢ@mottox2ʣ • KODANSHAtech LLC.͋ • ि4ۈ຿ͷσβΠϯΤϯδχΞ • ڵຯ: σβΠϯγεςϜ • झຯ: Apex Legends

Slide 3

Slide 3 text

ࠓ೔࿩͢͜ͱ • PuppeteerΛ࢖ͬͨը૾࡞੒ํ๏ • PuppeteerΛ࢖ͬͨಈը࡞੒ํ๏ • զʑ͸Կ͕Ͱ͖Δͷ͔

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Webٕज़Ͱը૾࡞੒ • Canvasͷը૾Խ • ΫϥΠΞϯτʹґଘ͢ΔɾCanvasࣗମ͕ѻ͍ʹ͍͘ • ը૾഑৴SaaSͷར༻ • ָ͚ͩͲࣗ༝౓͕௿͍ • ✅ Puppeteerͷར༻ • αʔόʔαΠυ͕ඞਢ͕ͩࣗ༝౓͕ߴ͍ɻ

Slide 6

Slide 6 text

Puppeteerͱ͸ • ChromeΛૢ࡞͢ΔAPIΛఏڙ͢ΔϥΠϒϥϦ • SPAͷΫϩʔϦϯάɺUIςετ΍ϑΥʔϜࣗಈૹ৴ͳͲͷ༻్͕૝ఆ ͞Ε͍ͯΔ • εΫϦʔϯγϣοτ΋ࡱΕΔ

Slide 7

Slide 7 text

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)

Slide 8

Slide 8 text

Puppeteerͷίʔυྫ const generateHTML = (props) => { return ` … ` }

Slide 9

Slide 9 text

Puppeteerͱ͸ • ϒϥ΢β্Ͱͷ༷ʑͳදݱΛ࢖ͬͨը૾͕࡞ΕΔɻ • ࣄྫΛ঺հ͠·͢ɻ

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

࠷ॳʹ࡞ͬͨ΋ͷ

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

👍 క·ͬͨҹ৅ʹͳͬͨ

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

💡 վળ͞Εͨ

Slide 20

Slide 20 text

ରԠલޙͷൺֱ • Before/After

Slide 21

Slide 21 text

ରԠલޙͷൺֱ • Before/After

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

ಈըʹ֦ு͢Δ

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

ಈըΛͭ͘Δ // child_processͰ࣮ߦ͢Δɻ͜ΕͰNode.js͔Βಈը͸࡞ΕΔɻ const { execSync } = require('child_process') ; execSync('ffmpeg -i Frame_%d.png -vcodec libx264 -pix_fmt yuv420p -y out.mp4')

Slide 28

Slide 28 text

֤ϑϨʔϜͷը૾Λ࡞Δ • ݱࡏͷframeΛҾ਺ͱͯ͠౉ͨ͠ΒɺͦͷframeͷεΫγϣ͕ؼͬͯ͘ ΔΠϝʔδ • 30fpsͷಈըͰ͋Ε͹ 1frame = 0s, 2frame = 1/30sͱରԠ͢Δɻ

Slide 29

Slide 29 text

֤ϑϨʔϜͷը૾Λ࡞Δ 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() ; })();

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Remotion

Slide 34

Slide 34 text

Remotion • ReactΛϨϯμϦϯάͨ݁͠ՌΛPuppeteerͰੜ੒ɺFFmpegͰ݁߹͠ ͨ΋ͷΛు͖ग़ͯ͘͠ΕΔ • طଘͷಈը΋ซ༻Մೳ • Webٕज़ͱطଘٕज़ʢAfter EffectsʣͷϋΠϒϦου͕Մೳɻ • ݸਓ΍খن໛ͳ૊৫ͳͲͰ͋Ε͹ແྉͰར༻Մೳɻ • ৄ͘͠͸ϥΠηϯεΛ֬ೝ͢Δ͜ͱɻ

Slide 35

Slide 35 text

Remotionͷίʔυྫ import { useCurrentFrame } from "remotion" ; export const MyVideo = () => { // ݱࡏͷϑϨʔϜ਺Λऔಘ const frame = useCurrentFrame(); return (
The current frame is {frame} .
) ; } ; export const RemotionVideo: React.FC = () => ( < > > )

Slide 36

Slide 36 text

զʑʹ͸Կ͕Ͱ͖Δͷ͔

Slide 37

Slide 37 text

PuppeteerΛར༻͢ΔϝϦοτ • ෳ਺ͷٕज़Λ૊Έ߹Θͤͯ࡞ΕΔ • Canvas΍WebGLΛ෦෼తʹऔΓೖΕΒΕΔɻ • ୯ಠͰ΍Γ͖Δʹ͸ݫ͍͠දݱͰ΋࡞Γ΍͍͢ɻ • ͢ͰʹWebʹ͋Δࢿ࢈Λ࢖ͬͨ΋ͷΛը૾ԽɾಈըԽ͢Δ͜ͱͰϝ Ϧοτ͕ੜ·ΕΔɻ • σʔλϏδϡΞϥΠθʔγϣϯͷ·ͱΊͱ͔ɻ

Slide 38

Slide 38 text

࣮ੈքͰ࢖͏ʹ͸ϋʔυϧ͕ • 🤔 ࣮͸ɺಈը࡞੒ͷSaaSͷํ͕खܰͰߴ඼࣭ͳ΋ͷΛ࡞Γ΍͍͢ • AfterEffectsΛ࢖͏ํ͕ϕλʔͳ͜ͱ͕ଟ͍ɻ • ࡞੒ର৅࣍ୈͰϝϦοτʹͳΔ෦෼͸ੜ·Εͯ͘Δͱࢥ͏ɻ • ձࣾͰ΋࢖͍ॴΛ࢕͍ͬͯΔ • ಈը੍࡞Λษڧͯͨ͠ΒɺAfterEffectsͷ࿅౓্͕͕ͬͨɻ