Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Chrome の marquee 要素が 優秀だった話
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
tsuyoshi wada
March 29, 2018
Technology
5.2k
8
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Chrome の marquee 要素が 優秀だった話
marquee の深みに迫ります。
tsuyoshi wada
March 29, 2018
More Decks by tsuyoshi wada
See All by tsuyoshi wada
技術面からみる パフォーマンス改善 / Frontrend Vol.13 Dec 19th, 2018
tsuyoshiwada
7
1.4k
Yarn + CI + GitHub で挑む npm パッケージの定期更新
tsuyoshiwada
3
1.8k
Other Decks in Technology
See All in Technology
FORENSIA: ローカルLLMフォレンジックハーネス
sumeshi
2
350
社内の7割が使うデータ基盤を、 データチーム2人で回すためにやったこと
koh_yoshi
4
1.2k
QAエンジニア起点で進める、SmartHRにおける信頼性向上について
kaomi_wombat
1
120
え?フロントエンドエンジニアの ワイがインフラも!?
puku0x
1
430
ガバメントクラウドでのランサムウェア対策
techniczna
2
1k
Invisible to AI? Making TYPO3 Sites Quotable by AI Search Systems
wolfgangwagner
0
170
第3回しろおびセキュリティスポンサーセッション
log0417
0
160
OSPN.JPバージョンアップ作業進捗のご報告 / 20260801-osc26kyoto
akkiesoft
0
420
【5分でわかる】セーフィー エンジニア向け会社紹介
safie_recruit
0
53k
取引先から届く 「セキュリティチェックシート」の読み解き方
kamadamakoto
0
140
Digitization部 紹介資料
sansan33
PRO
2
7.7k
攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習
recruitengineers
PRO
7
2.2k
Featured
See All Featured
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
What does AI have to do with Human Rights?
axbom
PRO
1
2.3k
Designing Powerful Visuals for Engaging Learning
tmiket
1
480
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.3k
Git: the NoSQL Database
bkeepers
PRO
432
67k
Between Models and Reality
mayunak
4
390
Code Reviewing Like a Champion
maltzj
528
40k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Marketing to machines
jonoalderson
1
5.7k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.7k
The SEO identity crisis: Don't let AI make you average
varn
0
530
Transcript
Chrome ͷ marquee ཁૉ͕ ༏लͩͬͨ 2018-03-29 / Meguro.es #14 @wadackel
ࣗݾհ @wadackel / Θͩ ͭΑ͠ tsuyoshiwada https://blog.wadackel.me • Go Ͱ
CHANGELOG δΣωϨʔλ࡞ͬͨΓ • Storybook ͱ Puppeteer ͰεΫϦʔϯγϣοτࡱͬͨΓ • CyberAgent, Inc. Ͱಇ͍͍ͯͨΓ͠·͢
·ͣ࢝Ίʹ͓அΓͰ͕͢…
marquee ཁૉطʹ ഇࢭ͞Ε͍ͯΔͨΊ Θͳ͍Α͏ʹ͠·͠ΐ͏ օ͞Μ͝ଘͷ௨Γ 00000002 ਓੜ ͷ-5 8FMDPNF৺͔Βܴ
marquee ཁૉͷ͓͞Β͍ HTML ্ͰςΩετ͕εΫϩʔϧ͢ΔྖҬΛ ࡞Δɻࢦఆͨ͠ଐੑʹԠͯ͡ɺςΩετ͕Ξ χϝʔγϣϯ͠ͳ͕ΒϨϯμϦϯά͞ΕΔɻ ʮ͍ͭ͜……ಈͧ͘ʂʯ
͔͜͜Β͕ຊɻ marquee in Chrome ͷ Ͳ͕͜༏लͳͷ ʁ
Chrome ͱ Firefox Ͱ marquee ͷಈ࡞Λൺֱͯ͠Έ·͠ΐ͏ https://developer.mozilla.org/ja/docs/Web/HTML/Element/marquee MDN web docs
ͷ DEMO
Firefox ʮΧΫΧΫʯ ͱ ςΩετ͕εΫϩʔϧ͞ΕΔ
Chrome Կނ͔ ʮψϧψϧʯ ͱಈ͘
͜ΕԿʹʁ
Ͳ͏ͤ Chrome ͷ ϨϯμϦϯάΤϯδϯ͕༏लͱ͔ ͦΜͳͰ͠ΐʁ
ͱɺࡶͳղऍͰࡁ·ͣ͞ʹ ϒϥβͷιʔείʔυΛ ͬͯΈΔ
·ͣ Firefox ͷ ϨΠΞτΤϯδϯͰ͋Δ Gecko ͔Β
Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏෦ <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
Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏෦ <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() Λݺͼग़ͯ͠Δ
_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
_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 ʹ ࢉग़ͨ͠࠲ඪΛࢦఆ͍ͯ͠Δ
ૉͳ࣮͕ͩʮΧΫΧΫʯ ͢Δͷೲಘͷ༰ • setTimeout() ͰϧʔϓΛൃੜͤͯ͞Ξχϝʔγϣϯͤͯ͞Δ - ϧʔϓͱϒϥβͷ࠶ඳըλΠϛϯά͕Ұக͠ͳ͍ • ֤ϧʔϓͰͷ࠲ඪΛٻΊͯ scrollTop,
scrollLeft ʹೖ͠Ҡಈ͍ͤͯ͞Δ - ϨΠΞτͷ࠶ܭࢉ͕ൃੜ͢Δ ※ಈ࡞͕ JS Ͱ࣮͞Ε͍ͯͨͷ͕ɺݸਓతʹҙ֎ͩͬͨ
Ͱຊ໋
Chrome վΊ Chromium ͷ ࣮Λ͘
Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏ίʔυ෦ 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
Ξχϝʔγϣϯ͕։࢝͞Εͯ ͍ΔͩΖ͏ίʔυ෦ 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 Λ ͬͯΔͬΆ͍!!
֤ඳըϑϨʔϜͷॲཧΛ ͬͯΈΔ void HTMLMarqueeElement::ContinueAnimation() { // ... લॲཧ (͍ͷͰলུ) AnimationParameters
parameters = GetAnimationParameters(); int scroll_delay = scrollDelay(); int scroll_amount = scrollAmount(); // ... ࣮ࡍʹ࠲ඪΛద༻ͨ͠Γͯ͠Δ (͍ͷͰলུ) } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ
֤ඳըϑϨʔϜͷॲཧΛ ͬͯΈΔ void HTMLMarqueeElement::ContinueAnimation() { // ... લॲཧ (͍ͷͰলུ) AnimationParameters
parameters = GetAnimationParameters(); int scroll_delay = scrollDelay(); int scroll_amount = scrollAmount(); // ... ࣮ࡍʹ࠲ඪΛద༻ͨ͠Γͯ͠Δ (͍ͷͰলུ) } ࢀߟDISPNJVNUIJSE@QBSUZ8FC,JU4PVSDFDPSFIUNM)5.-.BSRVFF&MFNFOUDQQ ԿΒΞχϝʔγϣϯ༻ͷ ύϥϝʔλΛ࡞͍ͬͯΔ
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
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?? Λ࡞͍ͬͯΔΆ͍
͞Βʹ͞Βʹ 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
͞Βʹ͞Βʹ 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 ͷΛੜͯͨ͠
ͦͷଞɺ ιʔείʔυΛݟͯ ؾ͍͕ͮͨ…
ཁૉͷੜॲཧ 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
ShadowRoot Β Slot ཁૉΒ ݟ֮͑ͷ͋Δهड़͕ࢁʂ
ੜ͞ΕΔཁૉͷΠϝʔδ <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>
ࢥ͍͖ͬΓ Web Components
Chromium ͷ marquee ࣮ ૾ΑΓϞμϯͩͬͨ… • ෦࣮ Web Components ͩͬͨ…!!
• requestAnimationFrame ʹΑΔ࠶ඳըλΠϛϯάͷ࠷దԽ - ϧʔϓͱϒϥβͷ࠶ඳըλΠϛϯά͕Ұக • ֤ϧʔϓͰͷ࠲ඪ CSS ͷ transform ϓϩύςΟͰཧ͞Ε͍ͯͨ - ϨΠΞτͷ࠶ܭࢉ͕ى͖ͳ͍ ✨
·ͱΊ
͍ʹ͑͠ͷཁૉࢥ͍ͷ΄͔ Ϟμϯͳٕज़Ͱಈ࡞͍ͯͨ͠ • ShadowDOM ʹΑΔελΠϧͷӅṭ • requestAnimationFrame ʹΑΔΞχϝʔγϣϯͷ੍ޚ • will-change
ΛͬͨΞχϝʔγϣϯͷ࠷దԽ • transform/translate Λͬͨ࠲ඪҠಈ (ඳըෛՙ࠷దԽ) • ීஈͷ Web ϑϩϯτΤϯυ։ൃʹ௨͡Δٕज़Ͱ ߏ͞Ε͍ͯͨ • ͋͘·Ͱ Chromium ͷதͰͶ
ϒϥβͷ࣮Λ͘ͷ ҙ֎ͱָ͍͠!! • C++ ॻ͚ͳͯ͘ɺงғؾͰԿͱͳ͘ॻ͍ͯ͋Δ͜ͱ͕͔Δ • ίϝϯτͦͦ͜͜ॻ͔Ε͍ͯΔͷͰ • ΤσΟλΛ׆༻͢Δ -
$ ctags --languages=C++ ͰλάΛ࡞ͬͯ… - vim Ͱఆٛδϟϯϓͯ͠ιʔείʔυ㓢͢Δͱָ • ripgrep Έ͍ͨͳߴͳ grep ସπʔϧ༗༻ • https://cs.chromium.org ศར
ϒϥβ࣮ݟΔͷ ͜Ε͕ॳΊͯͰͨ͠… ޡΓͳͲ͋Γ·ͨ͠Βɺ༏͘͠ڭ͍͑ͯͨͩ ͚Δͱ͍Ͱ͢
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠