Save 37% off PRO during our Black Friday Sale! »

Chrome の marquee 要素が 優秀だった話

Chrome の marquee 要素が 優秀だった話

marquee の深みに迫ります。

Dc696687bae442425a220771df158346?s=128

tsuyoshi wada

March 29, 2018
Tweet

Transcript

  1. Chrome ͷ marquee ཁૉ͕ ༏लͩͬͨ࿩ 2018-03-29 / Meguro.es #14 @wadackel

  2. ࣗݾ঺հ @wadackel / Θͩ ͭΑ͠ tsuyoshiwada https://blog.wadackel.me • Go Ͱ

    CHANGELOG δΣωϨʔλ࡞ͬͨΓ • Storybook ͱ Puppeteer ͰεΫϦʔϯγϣοτࡱͬͨΓ • CyberAgent, Inc. Ͱಇ͍͍ͯͨΓ͠·͢
  3. ·ͣ࢝Ίʹ͓அΓͰ͕͢…

  4. marquee ཁૉ͸طʹ ഇࢭ͞Ε͍ͯΔͨΊ ࢖Θͳ͍Α͏ʹ͠·͠ΐ͏ օ͞Μ͝ଘ஌ͷ௨Γ 00000002 ਓੜ ౓໨ͷ-5 8FMDPNF৺͔Β׻ܴ

  5. marquee ཁૉͷ͓͞Β͍ HTML ্ͰςΩετ͕εΫϩʔϧ͢ΔྖҬΛ ࡞Δɻࢦఆͨ͠ଐੑʹԠͯ͡ɺςΩετ͕Ξ χϝʔγϣϯ͠ͳ͕ΒϨϯμϦϯά͞ΕΔɻ ʮ͍ͭ͜……ಈͧ͘ʂʯ

  6. ͔͜͜Β͕ຊ୊ɻ marquee in Chrome ͷ Ͳ͕͜༏लͳͷ ʁ

  7. Chrome ͱ Firefox Ͱ marquee ͷಈ࡞Λൺֱͯ͠Έ·͠ΐ͏ https://developer.mozilla.org/ja/docs/Web/HTML/Element/marquee MDN web docs

    ͷ DEMO
  8. Firefox ͸ʮΧΫΧΫʯ ͱ ςΩετ͕εΫϩʔϧ͞ΕΔ

  9. Chrome ͸Կނ͔ ʮψϧψϧʯ ͱಈ͘

  10. ͜Ε೗Կʹʁ

  11. Ͳ͏ͤ Chrome ͷ ϨϯμϦϯάΤϯδϯ͕༏लͱ͔ ͦΜͳ࿩Ͱ͠ΐʁ

  12. ͱɺࡶͳղऍͰࡁ·ͣ͞ʹ ϒϥ΢βͷιʔείʔυΛ ௥ͬͯΈΔ

  13. ·ͣ͸ Firefox ͷ ϨΠΞ΢τΤϯδϯͰ͋Δ Gecko ͔Β

  14. Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏෦෼ <method name="start" exposeToUntrustedContent="true"> <body> <![CDATA[ if (this.runId ==

    0) { var myThis = this; var lambda = function myTimeOutFunction(){myThis._doMove(false);} this.runId = window.setTimeout(lambda, this._scrollDelay - this._deltaStartStop); this._deltaStartStop = 0; } ]]> </body> </method> ࢀߟNP[JMMBHFDLPMBZPVUTUZMFYCMNBSRVFFYCMNBSRVFFYNM
  15. Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏෦෼ <method name="start" exposeToUntrustedContent="true"> <body> <![CDATA[ if (this.runId ==

    0) { var myThis = this; var lambda = function myTimeOutFunction(){myThis._doMove(false);} this.runId = window.setTimeout(lambda, this._scrollDelay - this._deltaStartStop); this._deltaStartStop = 0; } ]]> </body> </method> ࢀߟNP[JMMBHFDLPMBZPVUTUZMFYCMNBSRVFFYCMNBSRVFFYNM setTimeout() Λ࢖ͬͯ _doMove() Λݺͼग़ͯ͠Δ
  16. _doMove() Λ௥ͬͯΈΔ <method name="_doMove"> <parameter name="aResetPosition"/> <body> <![CDATA[ // ...

    εΫϩʔϧҐஔͷࢉग़ϩδοΫ (௕͍ͷͰলུ) if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.newPosition; } else { this.outerDiv.scrollLeft = this.newPosition; } // ... ͦͷଞॲཧ ]]> </body> </method> ࢀߟNP[JMMBHFDLPMBZPVUTUZMFYCMNBSRVFFYCMNBSRVFFYNM
  17. _doMove() Λ௥ͬͯΈΔ <method name="_doMove"> <parameter name="aResetPosition"/> <body> <![CDATA[ // ...

    εΫϩʔϧҐஔͷࢉग़ϩδοΫ (௕͍ͷͰলུ) if ((this._direction == "up") || (this._direction == "down")) { this.outerDiv.scrollTop = this.newPosition; } else { this.outerDiv.scrollLeft = this.newPosition; } // ... ͦͷଞॲཧ ]]> </body> </method> ࢀߟNP[JMMBHFDLPMBZPVUTUZMFYCMNBSRVFFYCMNBSRVFFYNM ཁૉͷ scrollTop, scrollLeft ʹ ࢉग़ͨ͠࠲ඪ஋Λࢦఆ͍ͯ͠Δ
  18. ૉ௚ͳ࣮૷͕ͩʮΧΫΧΫʯ ͢Δͷ΋ೲಘͷ಺༰ • setTimeout() ͰϧʔϓΛൃੜͤͯ͞Ξχϝʔγϣϯͤͯ͞Δ - ϧʔϓͱϒϥ΢βͷ࠶ඳըλΠϛϯά͕Ұக͠ͳ͍ • ֤ϧʔϓͰͷ࠲ඪΛٻΊͯ scrollTop,

    scrollLeft ʹ୅ೖ͠Ҡಈ͍ͤͯ͞Δ - ϨΠΞ΢τͷ࠶ܭࢉ͕ൃੜ͢Δ ※ಈ࡞͕ JS Ͱ࣮૷͞Ε͍ͯͨͷ͕ɺݸਓతʹ͸ҙ֎ͩͬͨ
  19. Ͱ͸ຊ໋

  20. Chrome վΊ Chromium ͷ ࣮૷Λ೷͘

  21. Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏ίʔυ෦෼ void HTMLMarqueeElement::start() { if (continue_callback_request_id_) return; RequestAnimationFrameCallback* callback

    = new RequestAnimationFrameCallback(this); continue_callback_request_id_ = GetDocument().RequestAnimationFrame(callback); } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
  22. Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏ίʔυ෦෼ void HTMLMarqueeElement::start() { if (continue_callback_request_id_) return; RequestAnimationFrameCallback* callback

    = new RequestAnimationFrameCallback(this); continue_callback_request_id_ = GetDocument().RequestAnimationFrame(callback); } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ requestAnimationFrame Λ ࢖ͬͯΔͬΆ͍!!
  23. ֤ඳըϑϨʔϜͷॲཧΛ ௥ͬͯΈΔ void HTMLMarqueeElement::ContinueAnimation() { // ... લॲཧ (௕͍ͷͰলུ) AnimationParameters

    parameters = GetAnimationParameters(); int scroll_delay = scrollDelay(); int scroll_amount = scrollAmount(); // ... ࣮ࡍʹ࠲ඪΛద༻ͨ͠Γͯ͠Δ (௕͍ͷͰলུ) } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
  24. ֤ඳըϑϨʔϜͷॲཧΛ ௥ͬͯΈΔ void HTMLMarqueeElement::ContinueAnimation() { // ... લॲཧ (௕͍ͷͰলུ) AnimationParameters

    parameters = GetAnimationParameters(); int scroll_delay = scrollDelay(); int scroll_amount = scrollAmount(); // ... ࣮ࡍʹ࠲ඪΛద༻ͨ͠Γͯ͠Δ (௕͍ͷͰলུ) } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ Կ΍ΒΞχϝʔγϣϯ༻ͷ ύϥϝʔλΛ࡞͍ͬͯΔ
  25. AnimationParameters ͷ ੜ੒ʹഭΔ HTMLMarqueeElement::GetAnimationParameters() { AnimationParameters parameters; Metrics metrics =

    GetMetrics(); // ... ௕͍ͨΊলུ!! parameters.transform_begin = CreateTransform(-metrics.content_width); parameters.transform_end = CreateTransform(metrics.marquee_width); // ... ௕͍ͨΊলུ!! } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
  26. AnimationParameters ͷ ੜ੒ʹഭΔ HTMLMarqueeElement::GetAnimationParameters() { AnimationParameters parameters; Metrics metrics =

    GetMetrics(); // ... ௕͍ͨΊলུ!! parameters.transform_begin = CreateTransform(-metrics.content_width); parameters.transform_end = CreateTransform(metrics.marquee_width); // ... ௕͍ͨΊলུ!! } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ Transform?? Λ࡞͍ͬͯΔΆ͍
  27. ͞Βʹ͞Βʹ CreateTransform ʹഭΔ AtomicString HTMLMarqueeElement::CreateTransform(double value) const { char axis

    = IsHorizontal() ? 'X' : 'Y'; return String::Format("translate%c(", axis) + String::NumberToStringECMAScript(value) + "px)"; } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
  28. ͞Βʹ͞Βʹ CreateTransform ʹഭΔ AtomicString HTMLMarqueeElement::CreateTransform(double value) const { char axis

    = IsHorizontal() ? 'X' : 'Y'; return String::Format("translate%c(", axis) + String::NumberToStringECMAScript(value) + "px)"; } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ CSS ϓϩύςΟͰ͋Δ Transform ͷ஋Λੜ੒ͯͨ͠
  29. ͦͷଞɺ ιʔείʔυΛݟͯ ؾ͍ͮͨ఺͕…

  30. ཁૉͷੜ੒ॲཧ void HTMLMarqueeElement::DidAddUserAgentShadowRoot(ShadowRoot& shadow_root) { auto* style = HTMLStyleElement::Create(GetDocument(), CreateElementFlags());

    style->setTextContent( ":host { display: inline-block; overflow: hidden;" "text-align: initial; white-space: nowrap; }" ":host([direction=\"up\"]), :host([direction=\"down\"]) { overflow: " "initial; overflow-y: hidden; white-space: initial; }" ":host > div { will-change: transform; }"); shadow_root.AppendChild(style); Element* mover = HTMLDivElement::Create(GetDocument()); shadow_root.AppendChild(mover); mover->AppendChild( HTMLSlotElement::CreateUserAgentDefaultSlot(GetDocument())); mover_ = mover; } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
  31. ShadowRoot ΍Β Slot ཁૉ΍Β ݟ֮͑ͷ͋Δهड़͕୔ࢁʂ

  32. ੜ੒͞ΕΔཁૉͷΠϝʔδ <marquee> #shadow-root <style> :host { display: inline-block; overflow: hidden;

    text-align: initial; white-space: nowrap; } :host([direction="up"]), :host([direction="down"]) { overflow: initial; overflow-y: hidden; white-space: initial; } :host > div { will-change: transform; } </style> <!-- ҎԼͷ div ͕ transform ϓϩύςΟͰΞχϝʔγϣϯ͢Δ --> <div> <slot></slot> </div> </marquee>
  33. ࢥ͍͖ͬΓ Web Components

  34. Chromium ͷ marquee ࣮૷ ͸૝૾ΑΓ΋Ϟμϯͩͬͨ… • ಺෦࣮૷͸ Web Components ͩͬͨ…!!

    • requestAnimationFrame ʹΑΔ࠶ඳըλΠϛϯάͷ࠷దԽ - ϧʔϓͱϒϥ΢βͷ࠶ඳըλΠϛϯά͕Ұக • ֤ϧʔϓͰͷ࠲ඪ͸ CSS ͷ transform ϓϩύςΟͰ؅ཧ͞Ε͍ͯͨ - ϨΠΞ΢τͷ࠶ܭࢉ͕ى͖ͳ͍ ✨
  35. ·ͱΊ

  36. ͍ʹ͑͠ͷཁૉ͸ࢥ͍ͷ΄͔ Ϟμϯͳٕज़Ͱಈ࡞͍ͯͨ͠ • ShadowDOM ʹΑΔελΠϧͷӅṭ • requestAnimationFrame ʹΑΔΞχϝʔγϣϯͷ੍ޚ • will-change

    Λ࢖ͬͨΞχϝʔγϣϯͷ࠷దԽ • transform/translate Λ࢖ͬͨ࠲ඪҠಈ (ඳըෛՙ࠷దԽ) • ීஈͷ Web ϑϩϯτΤϯυ։ൃʹ௨͡Δٕज़Ͱ
 ߏ੒͞Ε͍ͯͨ • ͋͘·Ͱ Chromium ͷதͰ͸Ͷ
  37. ϒϥ΢βͷ࣮૷Λ೷͘ͷ͸ ҙ֎ͱָ͍͠!! • C++ ॻ͚ͳͯ͘΋ɺงғؾͰԿͱͳ͘ॻ͍ͯ͋Δ͜ͱ͕෼͔Δ • ίϝϯτ΋ͦͦ͜͜ॻ͔Ε͍ͯΔͷͰ • ΤσΟλΛ׆༻͢Δ -

    $ ctags --languages=C++ ͰλάΛ࡞ͬͯ… - vim Ͱఆٛδϟϯϓͯ͠ιʔείʔυ㓢኶͢Δͱָ • ripgrep Έ͍ͨͳߴ଎ͳ grep ୅ସπʔϧ΋༗༻ • https://cs.chromium.org ΋ศར
  38. ϒϥ΢β࣮૷ݟΔͷ͸ ͜Ε͕ॳΊͯͰͨ͠… ޡΓͳͲ͋Γ·ͨ͠Βɺ༏͘͠ڭ͍͑ͯͨͩ ͚Δͱ޾͍Ͱ͢

  39. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠