Subscribed unsubscribe Subscribe Subscribe

するするさせたい:サイボウズ採用情報のアニメーション(その3)

Performance Developer Tools Chrome Safari Firefox

サイボウズ採用情報のアニメーションがするするしてないので調べた件、今回は雑記。

  • その1 ― 何が起こっているのか調べる
  • その2 ― 調べながら直しながらするするさせる
  • その3 ― ちょっとしたことや他のブラウザでもするするさせる

描画領域とPaint Flashing

その1でPaint flashingを見たときにはアイコンの周りに枠がついていたのに、その2で描画領域を見たら文書全体で起こっていたたのをふしぎに思ったひとがいるかもしれない。

これはPaint flashingの説明 Hightlights areas of the page that need to be repainted というのをよく読むといいかも。緑色になったところは「再描画が必要になったところ」、paint invalidationを指している。なので変化していない箇所は緑色にならなかったわけだ。

Chromeにおける“Paint”が何を指すかは、Stack Overflowにある回答が詳しい。

モバイルはどうなのか

今回使ったのはふだん使ってるMacBook Air。2011年モデルだしAirだしMacとしてはだいぶしょぼい。それでもなんとかするするさせられた。

ではもっとしょぼい環境はどうなるか。というわけでNexus 5を繋いでリモートデバッグしてみた。 以下通常状態。MacBook Airと比べてCPUの使用率が高い。描画も引き続き高いけど、Scriptingも高いのが気になる。

f:id:myakura:20160515162406p:plain

transform: translateX() をつかったアニメーションに差し替えると、するするした。

f:id:myakura:20160515162413p:plain

いちおう大丈夫っぽい。ただ格安スマホなどもっとしょぼい端末だと、CPUやGPUがこれ以上に専有されるかもしれない。

iPhoneはどうだろう。iOSデバイス持ってないので試せないのだけどiPhoneさまなら大丈夫だろうと勝手に思って眠ることにする(みんなは試そう)。

他のブラウザの開発者ツール

今回はChrome DevToolsを使った。アクティビティの詳細を見られるので説明に便利なのと、まあみんな使ってるんでしょということで。
ただChromeだけの最適化になってしまったらアレだ。他のブラウザはどうなのか。

FirefoxSafariのDeveloper Tools/Web InspectorにもふつうにElementsパネルはある。h で要素も隠せる。

Paint flashingもどちらにもある。FirefoxではハケのアイコンSafariはElementsパネルのパンくず右にあるブラシアイコンで同様のものが見られる。なおFirefoxは色相が常に変わり、Safariでは赤色のハイライトになる。

f:id:myakura:20160515163120p:plain

件のアニメーションはSafariでは真っ赤になった。赤いほうが問題っぽさが増していいかもしれない(Chromeも昔は赤色だったけれども)。

レイヤー表示。Safariの場合はPaint flashingアイコンの横にある四角が4つのアイコンを押すと表示される。

f:id:myakura:20160516063926p:plain

FirefoxではDeveloper Toolsからできない。about:config から layers.draw-borderstrue にすればいける。ちょっと面倒。Bug 877567なのかな。

で、肝心なタイムライン。Firefox, SafariどちらにもあるんだけれどChromeほど高機能ではない。Chromeが強すぎるというのはある。

ただ今回くらいのレベルであれば、そんな問題はないかも。数秒ほど録る、繰り返しを見つける、時間を見てみる。これでそれなりにわかるかなあと。描画領域についてはとりあえず will-change: transform をコンソールからぶっこんでレイヤー作って確かめるとかね。いろんなサイトのタイムラインを録ってみて調べるうちに感覚が養えてくると思うので、いろいろ見てみてほしい。

EdgeはWindows環境が手元にないのでわからないのだけど、ドキュメンテーションを見る限りは結構強力そう。

Safariでするする

さて、Safariでもとのページのタイムラインを録ってみた。

f:id:myakura:20160515172624p:plain

スタイル計算と描画、タイマー発火が頻繁そうなことがわかる。

f:id:myakura:20160515172649p:plain

Layout & Renderingというカラムを押したら詳細が見られる。Paintではその領域を見られるけど、これはinvalidationなのかな。ちょっとわからない。

Web Inspectorに新しく入ったRendering Framesモードでも見てみた。

f:id:myakura:20160515172657p:plain

1フレーム目にタイマー、2フレーム目に計算・描画が行われてて面白い。

さてするするさせてみる。transform を使ったコードをぶっこんでまたタイムラインを録ってみる。

f:id:myakura:20160515173631p:plain

するするした。同じ方法が使えるようだ。

なお、CSSアニメーション版のレイヤーを見てみると謎の水色の枠がでてきた。

f:id:myakura:20160516063943p:plain

こいつはなんなんだろう…Web Inspectorはドキュメンテーションがあまりなく、さらには更新されてないのでけっこうつらい。ソース読むしかないのか……

Firefoxでするする?

Firefoxでもみてみる。Firefoxのタイムライン(Performance)ツール複数のタイムラインを保持できるのがうれしい。
現行のサイトを録ってみた。

f:id:myakura:20160516064037p:plain

あら、そのままでも60FPSに近い。Composite Layersにけっこう時間をとられているね。

f:id:myakura:20160516064046p:plain

Paintもすこしかかっている。描画範囲わからないのかな。もうちょっと調べないといけない。

f:id:myakura:20160516064106p:plain

CSSアニメーションに置き換えたものがこちら。タイマーはなくなったけど、ちょっとところどころフレームが落ちてしまっている。Composite Layersの負荷が変わってないので、もともと負荷が高いのかな。

f:id:myakura:20160516064127p:plain

あとスタイル再計算も走っている。Restyle HintがCSSアニメーションとなっているので、Geckoだとスタイルの再計算が走ってしまうのかな。ふむー

ただ見た目をいうと、CSSアニメーションのほうがするするはしているんだよね。データには現れづらいところなのかな…ちょっと心残り。


もうちょっとなんか考えてた気がするんだけど、メモしてなかったのともう書き疲れたのでこのへんで。
みんなもするするさせるんだよ。

するするさせたい:サイボウズ採用情報のアニメーション(その2)

Performance Chrome Developer Tools CSS

サイボウズの採用情報ページにあるアニメーションがするするしていない。ので前回は何がおこってるのか見てみた。

今回はするするさせられないか、がんばってみる。

  • その1 ― 何が起こっているのか調べる
  • その2 ― 調べながら直しながらするするさせる
  • その3 ― ちょっとしたことや他のブラウザでもするするさせる

アニメーションを止める

まず、いまのjQueryベースなアニメーションを止める。
コンソールに以下をぶっこめばアニメーションが止まって、背景も初期位置に戻る。

jQuery('.icon').stop().css('backgroundPosition', '')

チェーンもできるしjQueryべんりだね。

CSSアニメーションにしてみる

jQueryのアニメーションを別の方法に書き換えるわけだけど、今は2010年代も後半だ。使うならCSSアニメーションしかない。とくに右から左へ一方向っていう単純な動きならなおさらだ。

さっきのjQueryのコードは、こう変換できるかな。

@keyframes loop-icon {
  from { background-position: 0 }
  to { background-position: -1680px }
}
.icon {
  animation-name: loop-icon;
  animation-duration: 60480ms; /* 1680×36 */
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  /* animation: loop-icon 60480ms linear infinite; でもOK */
}

で、これを document.head.innerHTML += <style>...</style> とかでくるんでコンソールにぶっこむとアイコンが動き出す。おー、なんとなくするする動いてる感じ。

描画の詳細を見てみる

なんとなくじゃだめなので、またタイムラインを録ってみる。

f:id:myakura:20160513131612p:plain

およ…?

CPUの使用量が下がった感じはするけど、引き続きジャンクな感じ。
拡大すると、たしかに下がってはいる。

f:id:myakura:20160513131746p:plain

あと、Summaryから“Scripting”の項目が消えている。これはjQueryのアニメーションが setInterval() ベースだったんだけど、それがなくなったからだ。

さて、多少の成果が出たけれど、これではするするとは言えない。もっと調べよう。
拡大して、太い緑色のところをクリックすると、その描画についての詳細を見られる。

f:id:myakura:20160513132737p:plain

描画範囲(Dimensions)が「1362 × 6935」となっている。なんと、アニメーションはページの一部しかないのに、どうやらページ全体を描画しているらしい。だから負荷が高いまま。

will-change で別レイヤーに移す

ページの一部(高さ120px)を動かすためだけにページ全体(6935px)が毎回描画されるのはだいぶつらい。再描画の範囲を最小にしたい。そこで注目するのが、さっきのSummaryにあった「Layer Root」という項目だ。

ブラウザの描画領域は一枚なときもあるけど、合成レイヤー(compositing layer)という複数のレイヤーからなっていることもある。今回のように一部だけ動くとか、そういう際にブラウザが自動的にレイヤーを切って管理してくれる。OHPシート重ねて動かすみたいなやつだ。

というわけで、ブラウザに「動くのここだけだからレイヤー切ってよー」というのを示して全体を描画させないようにする。これには will-change なるプロパティを使う。値にプロパティ名を指定すると、ブラウザはその要素上のそのプロパティが変化するのに適した処理をしてくれるようになる。

レイヤーを新たに作りたい場合、値に transform を指定するのがおまじないだ。

.icon {
  animation-name: loop-icon;
  animation-duration: 60480ms;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  will-change: transform;
}

古の -webkit-transform: translate3d(0, 0, 0) と変わらないじゃんと思うかもしれない。ハック感は否めないけど、でもいちおうオプトインだから。名前も「will-change」であくまで意図だしブラウザが今後もこの挙動とは限らないから(もごもご)……

レイヤーを確認する

レイヤーを切ったのはいいけれど、どう確認すればいいか。もいちどDevToolsのレンダリング設定メニューを開く。そこにある「Show layer borders」をチェックすれば、レイヤーの境界にボーダーが引かれるので、どこにレイヤーがあるかがわかる。

以下はもともとのページのレイヤー。レイヤーの境界は薄いオレンジで、ページの端についている。ページ全体が一枚のレイヤーみたいだ。

f:id:myakura:20160513140733p:plain

水色の水平線が見える。これはタイルというレイヤーのいち領域の区切りを示している。
右と下に緑色の矩形が見えるけど、どうやらスクロールバーの領域もレイヤーの一種みたいだ。

以下は .iconwill-change: transform を与えてレイヤー化したもの。アイコン周りに新しいオレンジの枠ができたのがわかる。

f:id:myakura:20160513140745p:plain

レイヤー分けた効果を確認

ではタイムラインを録ってみよう。

f:id:myakura:20160513142111p:plain

ふむ。ジャンクはまだあるけど、描画の負荷がだいぶ減っている。1フレームとばしで60FPSも達成している。またちょっと進んだね。

f:id:myakura:20160513142126p:plain

あんなに太かったPaintもほっそくなって、描画領域も「1362 × 120」に、レイヤールートも狙い通り #document から div.icon になった。

しかしまだジャンクがあるのが気になる。確かに見るとまだガタガタしている。
あと、Paintingが減ってはじめて気になったのが紫色のRendering。毎回なにをレンダリングしているのか?そう、背景画像だ。

transform でするする

実は、CSSアニメーションにしたから負荷が減るというのは限定的な話で、プロパティによってはレイアウト、スタイルの計算、描画がはしってしまう。レイアウトはスタイル計算を、スタイル計算は描画を引き起こすので、できるかぎり前の段階からの最小化が望ましい。

プロパティごとに引き起こされるレイアウト・スタイル計算・描画は、Paul Lewisらがまとめている。

最近Blink以外のエンジンも頑張っている。
さて、background-position の項をみると、it will cause painting to occur とある。なので描画は結局おこってしまう。あとスタイルの計算もはしってしまう。なのでjQueryまわりのスクリプト・レイアウトの負荷がなくなるだけで、コストの掛かる描画は残ったままになる。

どうすればいいか。アニメーションによるのでどうにもできないケースもあるだろうけど、今回のアニメーションであれば transformtranslateX() に置き換えるといい。transform は描画も起こさないので、この負荷も下げられる。というわけでこちらにしよう。

背景画像の繰り返しを利用してずっと流れているように見せているので、書き換えはちょっと工夫が必要。今回は .icon の幅を広げて、それを translateX() で動かすようにした。

@keyframes loop-icon-transform {
  from { transform: translateX(0px) }
  to { transform: translateX(-1680px) }
}
.kintone {
  overflow: hidden;
}
.icon {
  width: 3360px !important;
  animation: loop-icon-transform 60480ms linear infinite;
}

ウインドウの幅が大きいと端が見えちゃうだろうけれど、だいたいカバーできるだろう。
もうちょっといいつくりがあるのかもしれない。translateX() で動かせて、切れ端が見えないのならなんでもいい。

なお、今回は will-change を指定していない。というのも、指定しなくてもレイヤーを分けてくれたから。will-change もこのアニメーションの自動レイヤーわけもブラウザ依存なので、対応環境によってはつけないといけないかもしれない。

ではタイムラインを録ってみよう。

f:id:myakura:20160513143936p:plain

ててーん。ジャンクなし。だいたい60FPS。RenderingとPaintingも消えた。
見た目もするするしている。するするさせられた!


というわけで、するするさせたいという目標はいちおう達成できたかな。どうぞご利用ください。

ただちょっとだけ気になることとか、あと少し書いときたいこともある。そいつはまた次回。

(追記):書いたよー

おまけ:試してみよう

どんなコードをコンソールにぶっこんだかちょっと書いとこう。おためしあれ。

background-position 版のCSSアニメーションに置き換え。

jQuery('.icon').stop().css('backgroundPosition', '')
document.head.innerHTML += `<style>@keyframes loop-icon { from { background-position: 0 } to { background-position: -1680px } } .icon { animation-name: loop-icon; animation-duration: 60480ms; animation-timing-function: linear; animation-iteration-count: infinite; }</style>`

will-change: transform はStyleパネルから追加している。クリックでオンオフさせやすいので。

以下は translateX 版のCSSアニメーション(最終版)に置き換えるもの。

jQuery('.icon').stop().css('backgroundPosition', '')
document.head.innerHTML += `<style>@keyframes loop-icon-transform { from { transform: translateX(0px) } to { transform: translateX(-1680px) } } .kintone { overflow: hidden; } .icon { width: 3360px !important; animation: loop-icon-transform 60480ms linear infinite; }</style>`

いま考えたらTemplate Strings使ってるんだからべつに改行取っ払うこともなかったな…

するするさせたい:サイボウズ採用情報のアニメーション(その1)

Performance Chrome Developer Tools

サイボウズの採用情報ページを見ていた。

といっても受けるとかではなく、ただ性格悪いことを思っていただけなんだけど。

Kintoneのセクションでアプリっぽいアイコンたちが右から左へと流れているんだけど、それがガタガタとしている。するするしていない。するするさせたい。

というわけで、何が起こっているのかを調べてみようかと。何回かにわけて書くよ。

  • その1 ― 何が起こっているのか調べる
  • その2 ― 調べながら直しながらするするさせる
  • その3 ― ちょっとしたことや他のブラウザでもするするさせる

アニメーションの実装を調べる

あとのことを考えて、Chrome DevToolsを使う。

まず、該当の流れるアイコンのところで右クリックしてinspectする。Elementsパネルで、<div class="icon"> という要素がハイライトされる。div.icon には style 属性がついていて、そこにセットされた background-position の値が絶えず変化しているのが見える。

f:id:myakura:20160510083150p:plain

というわけで、背景画像の位置をJavaScript逐次書き換えるっていう、クラシックなアニメーションの実装方法なのがわかる。

使われているJavaScriptファイルのソースを見てみる。.icon で検索すると見つかった。

var loopIcon = function(){
  $(this).css({backgroundPosition:'0'});
  $('.kintone .icon').animate({backgroundPosition:'-1680px'}, 1680*36, 'linear', loopIcon);
}

jQueryでやっているみたい。で、こういうJavaScript逐次CSSを書き換えていくアニメーションにはたいてい描画絡みの問題がある。

描画されている箇所をみてみる

描画について見てみる。DevToolsのレンダリング設定パネルを使う。

DevToolsのメニューで「Other tools」→「Rendering settings」を選ぶと、下からにゅっとConsoleなどがついたパネルがが生えてくる。Renderingタブが選択されていると思うので、その中にある「Enable paint flashing」をチェックすると描画の変化する箇所に緑色の枠がつく。スクロールしたり、リンクにカーソルを重ねたりすると、「描画の変化」がどういうものかわかると思う。

f:id:myakura:20160510083928p:plain

さて件のアニメーション。ずっと枠がついたまま。なので、この箇所が絶えず描画されているのがわかる。

タイムラインをとってみる

こいつがパフォーマンスにどれくらい影響しているのか、もうちょっと見てみる。
Timelineパネルを開いて、左上にある黒丸を押して2〜3秒ほど録る。

以下はMacBook Air 11" (mid-2011モデル、メモリ4GB)上のChrome Canary (M52)、ちょこちょこブラウザやエディタを開いた状態で録ったもの。記録する際に、PaintとScreenshotのみチェックした。

f:id:myakura:20160512120322p:plain

とれたけどいろいろ情報が多いので、まず上のオーバービューと呼ばれる箇所を見る。その下のフレームチャートなどはあとから見よう。

オーバービューは、フレームとFPS、CPUのアクティビティ、スクリーンショットを見せて大まかなところを把握するためのところ。

f:id:myakura:20160512120342p:plain

フレームのところにあるがたがたした緑色の領域はFPSで、てっぺんが60FPSを指す。その上に見られる赤い線は60FPSを切ったフレームで、いわゆる「フレーム落ち」、海外のWebパフォーマンス界隈だと “jank” と呼ばれるやつを指している。
つまり上に赤い線が見えず、緑色の面積が大きいほどいい。

で、かなりのところでジャンクが発生している。環境によっては赤いのを見ないかもしれないけど、今回はこうなった。

さて、フレームの下、CPUのアクティビティを見る。ちょこちょこと濃い緑色の山があって、たまに紫や橙が見られる。
上のフレームのとこと違って、こちらは山が低い方がいい(負荷が少ないから)。ではこの山になってるとこで、なにがおこっているのか。

フレームチャートを見る

調べるといってもどこを見るといいか。見直すと、ちょっとパターンが感じられる箇所があった。

f:id:myakura:20160512124009p:plain

だいたい等間隔で高い山が現れているので、ここを詳しく見ると、パフォーマンスの問題が見えてきそうだ。そこを見てみよう。
オーバービューのところで範囲選択すると、下のフレームチャートが該当する範囲のものに切り替わる。

f:id:myakura:20160512131823p:plain:w600

一番下のペーンに、アクティビティごとにかかった時間が円グラフにまとめられている。“Painting”と“Other”が多い。後者はさておいて、描画関連の処理は全体400ms中38msと、1割弱もの時間がかかっているのがわかる。

本当にそこなのか、隠してみる

さて、タイムラインを録ってアイコン周りの描画の負荷が高くてフレームが落ちているようになんとなく感じていたものの、それが直接の原因なのかを確かめていなかった。どうすればいいか。隠せばいい。

もういちどElementsパネルに戻って、div.icon を選択し、h キーを押す。そうするとアイコンがぱっと消えてしまう。

f:id:myakura:20160512141442p:plain

div.icon__web-inspector-hide-shortcut__ なんて変なclassがついたけど、中身は visibility: hidden を指定しているだけ。
visibility: hidden が当たると描画関連の処理が行われなくなる。ではこの状態でタイムラインを録ってみよう。

f:id:myakura:20160512142003p:plain

全体的にいろいろ薄くなった。ジャンクな赤色も見えない。FPSのグラフがなくなっているのはアイドル状態になった、つまりは活動しなくてもよくなったから。


というわけで、アイコンのアニメーションが描画に負担をかけてるのがわかった。では負担をかけない、するするするアニメーションにどう作り変えていけばいいだろうか。長くなったのでそいつは次回に。

(追記):次回書いたよー

するするさせたい

Performance

昨年くらいからWebの描画まわりのパフォーマンスについて調べたりしている。
何をしたいのかと言うと、するするさせたい。

するする?

あくまで自分の傾向なんだけど、読み込まれるまで時間がかかるというよりは、ページのスクロールでひっかかりがあったり、クリックしたときのアクションがブロックされたりする方が、不満がある。

そこらへんをなんとかしたいなと。

もうちょい具体的に

もうちょっと技術的に言うなら、余計なreflowやrepaintをなくすとか、render blockingな要因を減らすとか、そういうところだろうか。後者については、するする関係ないと言われるかもしれないけれど、そこも描画に依存するところなので……

というわけで

そこら辺のこと書いたりお仕事にしてこうかと。仕事になるかなー……

Progressive Web Appsとは

Chrome Dev Summitに来ている。

今年のChrome Dev Summitは日ごとにテーマが分かれていて、初日がProgressive Web Apps、2日目がRAILらしい。RAILはGooglerがちまちまと話してるけど、前者についてはまだそんな離されてない気がする。

というわけでセッション聞きながらなんとなく書いてみよう(なのでセッションまとめではないよ)。

アプリに「なっていく」「Web」

Progressive Web Appsは、Alex Russell先生が6月にブログで提唱して、そこからプロモートしているもの。“progressive”を訳すと「漸進的」やら「進行性」とかになるけれど、そのまま訳すより「アプリになっていくWeb」と砕いて解釈するとわかりやすそう。

アプリに「なっていく」とは何かなんだけど、Webアプリが「使ってるうちにネイティブアプリと同じような体験を備える」ってところなのかな。なので基本的にWebアプリというのは変わらない。

また、「Web」というのもポイント。「『Web技術』を使ったアプリ」ではなくて、サーバにホストされURLを持つ、「Web上にあるアプリ」というのが重要。

なんとなく「モバイルをキャッチアップする」というのに向いていたここ最近のWebプラットフォーム。ブラウザのパフォーマンス改善やら、新しいAPIなどよい点もあったけれど、ちょっとそこが行き過ぎてしまった感もある。モバイルであんま使えそうにないAPIいらなくねって言って顰蹙買ったりとかね。

Progressive Web Appsはそこからちょっと立ち返って、「インストール不要で使える」「リンクできる」「ストアなどの制限なく誰でも自由に開発できる」といったWebの性質を活かすことに主眼を置き直し、そのうえで発展を見ているように感じる。

でも、いい点だけ押してても特に何も変わらない。PWAはモバイルのアプリと比べて厳しかった「エンゲージメント」に着目して、そこを改善して、そして辿り着けたものかなあと。

アプリになっていくには

「アプリに『なっていく』」っていうのはどういうことなのか。Webサイトがどうなったらアプリとして感じられるようになるのか。

ひとつは、アクセスの起点。Webアプリの場合、ブラウザのアドレスバーから入力したり検索したりでたどり着くことが多いだろう(WebページならSNSや検索からのが多そうだけれど)。一方でアプリはホーム画面やローンチャにあるアイコンから立ち上げることがかなり多いと思う。どっちが「アプリ」として楽かというとたぶん後者になる。じゃあ、Webアプリをホーム画面とかに登録できるようにすればいい。ホーム画面にはアイコンがついて、タップしたらアプリっぽくWebアプリがブラウザで開かれるといい。

ひとつは、起動した時の感覚。「アプリ」として使うには、オフラインでも「ガワ」や最低限のコンテンツは表示させておきたいし、単体で完結できるものならロケーションバーとかは消しときたい。そういう風にさせられればいい。

ひとつは、起動してない時の関係。ブラウザを開いてない時でも、提示されたい情報や、アップデートされてると嬉しいデータなんかがある。そうさせよう。プッシュ通知とか同期ができるようになるといい。

そしてこういう体験は強制ではなく、それを好みそうと思う人に、適当なタイミングで促せばいい。ブラウザがアクセスしたパターンをみて、アプリとして使ってそうだったら「もっとアプリっぽくしてみない?」と言う。Webアプリが「アプリインストールしない?」とか、最近見ないけど「ホーム画面に登録してみない?」っていうのを勝手実装するんじゃなくて。

あたらしい?

具体的な技術を挙げるなら、Web App Manifest、Service Workerがインストールまわりを、Push APIとNotifications API、Web App Manifestの一部メンバがエンゲージメントまわりを担当する。

ただ、こういう新しい機能を待たずとも、やろうと思えばそれなりにできていただろう。Service Workerで初めて広くもたらされるのはプッシュくらいで、オフラインなどはAppCache使えば(つらいだろうけど)なんとかなる。ホーム画面への追加も、ブラウザの機能で前からある。

マーケティング用の単語というとそうかもね、というところ。でも概念に名前つけるのは、広めることを考えるとそれなりに重要だろう。ふんわりした「Webアプリ」の再定義やオーバーロードはしづらいだろう。あとは開発はそれなりにつらいものだっただろうから、PWAを前提に機能スタックを整備し、その上で名前をつけられたので、そんなに無意味ではないかなあと。

Remove all ads

すこし開かれたWHATWG HTML

HTML Standards

8月末のことなんだけれど、WHATWG HTMLの仕様書GitHubに移った。

体制も少し変わって、HixieがひとりでやっていたHTML仕様の作業にAnneやDomenicなども直接関わるようになった。Issuesでの議論やPull Requestの受け付けも始まり、だいぶモダンな策定環境になった。

これまでのWHATWG HTMLとHTML5仕様書

これまでのWHATWG HTMLは長らくSubversionサーバで運用されていた(もしかするとCVS時代もあったかも)。

HTML仕様書は基本的に1枚のHTMLファイルに書かれていて、それをツールで切り出しmultipage版やPDF版、Web Developer Editionなどを生成していた(あ、<picture>については、Simon PietersがRICGで作業してたものをパッチとして取り込んでいたので、多少いびつなことになっていたけど)。

W3CでのHTML5(関連)仕様書も、W3C版のパッチが必要なところや、Canvas 2D仕様とかの分割にはソース中に特殊なコメントを入れてツールがそれをもとにそれぞれの仕様書をつくるなんてことをしていた。

W3C仕様書は統一的なツールが最近になってようやく運用されはじめたけど、それ以前のものは仕様やEditorによって違っていて、それが他のEditorの参入障壁になっていた気がする。

GitHubでのコミュニケーション

仕様(書)がオープンな環境にうつったこと、またHixie以外の人が積極的に関わり始め、多少なりとも変化が感じられる。

ひとつはW3C HTML5仕様との差を埋める提案が少しずつ出たこと。たとえばパーザは<rb>などに対応した(conformingになってないのは厄介だけど)し、ARIAとの関わりについてはSteve Faulknerの仕様を参照するように変更された。

とはいってもこれはHixieが拘りをなくしたところなだけで、そうでないものはやっぱり手強そう。<main>の違い(複数書けるか書けないか)についてはIssueがたったもの燃えている。<hgroup>消そうみたいなIssueが経つと言うとまた荒れるんじゃないかな。Hixieの考えが悪いってわけでは必ずしもないんだけど、GitHubで運営されているものが持つオープンさとはだいぶ差があるのでそこらへんどうなるかな。バランスとれるといいんだけど。

HTMLはどうなってくのかね

HTMLの策定は、集まったユースケースやデータを元にHixieがうんうんと考えデザインされたものが仕様に出てきて、それをレビューするというフローだった。Web技術の策定にかけるリソースが足りてない2000年台前半から半ばは、執念あるひと(Hixie)が居ることで仕様やWebが進んだ。

ただ、デザインされた機能が実際に使えるものになるとは限らない。自身でも失敗と認める機能(AppCacheやWeb Storage、History APIなど)は少なくない。欲しいものではなかったことがわかった時にはもう遅いなんてことがあった。

Hixieはdeclarativeな機能を好んでるふしがあって、それはたぶんUAの独自性やオープンな競争を尊重してたからだと思うのだけど、同じ挙動というかなり強い互換性が求められる昨今にはあまり好かれるものではない。そういう反省もあってExtensible Webというのが来ていたりもするので、彼の設計思想がふんだんに反映されてるであろうHTMLをどれだけ綺麗に崩していけるかが、HTMLを発展させるポイントなのかなと。とはいえ彼を説き伏せるのはだいぶ至難の業だからねえ。

ただ、HTML自体にどれくらい発展が求められているのかはちょっとわからない。新しい要素ならWeb Componentsで自分でやってねということになる(そうさせたい)だろうし、その他APIはメンテしたりたまにちょっと足したり、他の仕様でさせるためのextension pointを作ったりするくらいで、基本的にはメンテナンスモードのままなのかもしれない。バージョンレスとは言ったけれど、バージョンを切るほどの活力はもうないのかなとも思う。

Content ScriptsなChrome拡張をFirefox拡張に

Mozilla

追記(2015-10-16):WebExtensionsの発表

これを書いた数ヶ月後の8月21日、Mozillaが今後のアドオンをWebExtensionsというChrome拡張APIベースなものに移行していく計画を発表した。

なので、今後のことを考えたりもともとの「Chrome拡張をFirefoxでも動かしたい」というのを考えるとWebExtensionsベースなものにしたほうが良さそう。実際に試したところ、Chrome拡張のmanifest.jsonにWebExtensionsで必要なメンバを足しただけでふつうに動いたので、Content ScriptsベースなChrome拡張ならほとんど手間を書けずにクロスブラウザな拡張にできそう。

というわけで、このエントリで書いたAdd-on SDKベースな拡張はいささかレガシーなものになりそうだけど、気になる方はお読みくださいな。


お手製のしょうもないChrome拡張をFirefox拡張にしてみた。といってもタイトルにある通りContent Scripts、つまりユーザースクリプトレベルなものなので、そんな面倒ではなかった。

Firefox拡張のContent Scripts

まずは同じコンセプトのものがFirefoxにあるのか、ドキュメントを探す。あった。

Many add-ons need to access and modify the content of web pages. But the main add-on code doesn't get direct access to web content. Instead, SDK add-ons need to factor the code that gets access to web content into separate scripts that are called content scripts. This page describes how to develop and implement content scripts.

Content scripts can be one of the more confusing aspects of working with the SDK, but you're very likely to have to use them. There are five basic principles:

  • the add-on's main code, including "main.js" and other modules in "lib", can use the SDK high-level and low-level APIs, but can't access web content directly
  • content scripts can't use the SDK's APIs (no access to globals exports, require) but can access web content
  • SDK APIs that use content scripts, like page-mod and tabs, provide functions that enable the add-on's main code to load content scripts into web pages
  • content scripts can be loaded in as strings, but are more often stored as separate files under the add-on's "data" directory
  • a message-passing API allows the main code and content scripts to communicate with each other

拡張からページのDOMは直接触れられずContent Scripts経由というのはChromeと同じらしい。あとIsolated Worldというコンセプトも共通。

スクリプトの読み込み方は違いがあるけど、Content Scriptsのファイルはそのまま流用できそう(Chromeでしか対応してないものを使ってない限りは)。わーい。

jpm

Firefoxの場合はどうやらAdd-on SDKをインストールしないといけないらしい。これまではそのSDKPythonベースのやつだったらしいんだけど、Fx38からNodeベースのjpmというものになったとのこと。

というわけで npm install -g jpm でさくっとインストール後、jpm init で名前やらエンドポイントやらを設定。

Content Scriptsの読み込み

Chrome拡張では manifest.json からContent Scriptを動作させたいURL(のパターン)とそのファイルを明示的に指定していた。

...
"content_scripts": [
  {
    "matches": [ "https://foo.bar/*" ],
    "js": [ "baz.js" ],
    "css": [ "quux.css" ]
  },
  ...

Firefox拡張の場合はエンドポイントのファイルを用意して、そこからプログラマブルな感じで指定するらしい。jpm initした場合はindex.jsができる。

今回の拡張はページにスクリプトファイルをぶっこむものなので、page-modというAPIを使う。

const data = require('sdk/self').data
const pageMod = require('sdk/page-mod')

pageMod.PageMod({
  include: "https://foo.bar/*",
  contentScriptFile: [ data.url('baz.js') ],
  contentStyleFile: [ data.url('quux.css') ]
})

ファイルの置き場

エンドポイントを指定したら jpm init したディレクトリ直下にできたのだけれど、Content Scriptsのファイルとかはその中に data ディレクトリを作ってそこに置かないといけないらしい。ここで少しはまった。initしたら作ってほしいな。

Chrome拡張はとくにディレクトリわけとかしてなかったので、そのまま読み込めないか試すもアウト。どうやらファイルを指定したとこで使ったself.data.url()data ディレクトリ内のファイルのみしか読めないらしい。まじか…

気になったのでソースを見てみる。

exports.data = Object.freeze({
  url: uri,
  load: function read(path) {
    return readURISync(uri(path));
  }
});

はいはい。では uri はというと…

const uri = (path="") =>
  path.contains(":") ? path : addonDataURI + path.replace(/^\.\//, "");

ええ。それで addonDataURI はというと、

const addonDataURI = baseURI + "data/";

(´Д`)

固定だ…ちょうmagicだ……

というわけであきらめかけてたんだけど、ふと data.url('../hoge.js') としてみたら読み込めた。でもいいのかな…まあ動かなくなったら考えよう。

ディレクトリ固定、もやっとすることはあるけど拡張のレビューとか、他のひとの拡張を知りたいときには見るところが決まっていて便利なのだろうなとも思う。Chromeだとmanifest.json見ないといけないし。

その他

作った拡張は jpm xpi でパッケージ化すれば再起動不要なアドオンとしてインストールできる。

Content Scriptレベルならふつーにできたけど、UIになにかぶっこむとかそういうのはAPIがだいぶ違うので、できるだけモジュールに落とすとかしないと共通部分が減るばかりな気がした。試したいんだけどそういう拡張を作ったことがない(…)ので、なんかアイデアを持とう。

FirefoxのCSS Unprefixing Service

CSS Mozilla

先日about:configにlayout.css.unprefixing-service.enabledなんてのを見つけて「なんだろう」と思ってたのだけど、Compatibility Teamのメーリングリストにアナウンスがあった。どうやら-webkit-CSSプロパティとかがあったら、それを標準のコードとして解釈するものらしい。

This feature is only active for the "top 10" broken Chinese mobile sites that Peipei provided[1] (e.g. several Baidu services and m.taobao.com). The whitelisted sites should have a much-improved user experience, due to Firefox converting legacy -webkit prefixed CSS into equivalent unprefixed CSS.

Mozillaは2012年にもWebKit接頭辞について検討していて、当時はCSSだけでは意味がないと判断していた。ただFirefox OS端末の投入とかで状況が多少変わったんだろうか。それともUA SniffingAPIについても似たようなことやってるのかな?

まだサービスの実装も途中らしく、GradientsやCSS Animationsはサポートされてないとのこと。CSS AnimationsについてはYahoo! JAPANもWebKitのみらしく、このままだと登録しないとかもねなんて話が出ている。ヤフーさん!!!Yトップなんとか!!

PrestoやProject Spartanではすべてのサイトを対象に接頭辞の処理をしている(みたいだ)けど、Mozillaの場合は特定サイトのみに限定させるみたい。特定サイトだけの対応はIEのCompatibility Listの膨れっぷりを見ると茨の道な気もするけれど、がんばってほしい。

SassScriptでリスト関数を拡張する

その昔Sassに凝っていたころに書いたメモを見つけた。供養のために公開。

当時のSassにはセパレータ判別の関数とかなかったので、今は特に必要ないものもあるね。時間経ったね。


グラデーションのmixinを書いたときに「リストを扱う関数が足りないなあ」と思ったので、関数をいろいろ書いてみている、RubyじゃなくSassで。

is-list($arg)

リストかどうかを返す関数。list 型のほか、可変長引数の arglist 型でも true を返す。

@function is-list($arg) {
  @return type-of($arg) == 'list' or type-of($arg) == 'arglist';
}

ただこれ、ちょっと問題があって、('item') などひとつの値を括弧でくるんだものを渡すと false を返してしまう。

$list: ('item');
@debug is-list($list); // DEBUG: false

上の場合 $liststring 型になって結果 false になる。なんでかというと、Sassでは括弧がリストのデリミタではないので、括弧で包んでもリストにならないから。えー……

ただ、もし括弧をデリミタにすると、演算の優先順位を変える括弧と区別がつけられず、(3 + 4) * 5 という式が「3 + 4 → 7 という値の入ったリストに5を掛けている」など解釈されてしまう。ううむ……

ただ、それはそれで困ったもので nth()index() にリストを突っ込んだつもりがアイテムがひとつだったためエラーになったりした。幸い、この前リリースされたSass 3.2.7でリスト関数についてはアイテムがひとつでもリストとして解釈されるように修正されたのでよかったんだけれど、type-of() の挙動は引き続き変わっていない。まあ、仕様なのでちょっと諦める。

余談:アイテムひとつのリストを作る方法

アイテムひとつのリストを作れないわけではない。空のリストをつくって、そこにアイテムをいっこ加えればよい。こうやって作ったリストは型がちゃんと list になる。

$oneitemlist: append((), 'item');
@debug type-of($oneitemlist); // DEBUG: "list"

contains($list, $value)

ある値がリスト内にあるかどうかを返す関数。

$fruits: 'apple' 'banana' 'cherry';
@debug contains($fruits, 'banana'); // DEBUG: true

実装は index() を使っている。

@function contains($list, $value) {
  @if not is-list($list) {
    @warn 'argument error: #{$list}';
    @return 'error';
  }
  @return type-of(index($list, $value)) == 'number';
}

index() だけで済みそうなものだけど、Sassの index() は値が見つかない場合に false を返すので、JavaScriptでやるような indexOf(item) > -1 みたいなのができない。なので type-of() を使い index() が数値を返すかで判定している。

has($value, $in)

has()はただ、contains()引数の順序が替わっただけ。内部的には contains() を使ってる。

@function has($value, $in) {
  @return contains($in, $value);
}
$oyatsu: 'umaibou' 'miyako-konbu' 'big-katsu';
@debug has('banana', $oyatsu); // DEBUG: false

Sass組み込みのリスト関数は第一引数にリストを取るような設計なんだけど、mixinとかfunctionを書いてると、リストではなく値を前に出したほうがコードが読みやすくなるなと思ったことが結構あったので書いてみた。

あと、has()は引数のキーワードを変えている。これでどうなるかというと、ちょっとヒューマンリーダブル感が出る感じに書ける。

$oyatsu: 'umaibou' 'miyako-konbu' 'big-katsu';
@if has('banana', $in: $oyatsu) {
  content: 'わーい';
}

む、書いてみたけど、そんな分り易くないというか、読みにくくなってるだけかも……

separator($list)

Sassのリストはカンマ区切り、空白区切りと、ふたつの書き方がある。そのどっちを使っているか検出する関数。

@function separator($list) {
  @if not is-list($list) {
    @warn 'argument error: #{$list}';
    @return 'error';
  }
  @if length($list) == 1 {
    @return null;
  }
  $list_comma: ();
  $list_space: ();
  @each $item in $list {
    $list_comma: append($list_comma, $item, comma);
    $list_space: append($list_space, $item, space);
  }
  @if $list == $list_comma {
    @return 'comma';
  }
  @else if $list == $list_space {
    @return 'space';
  }
  @else {
    @return 'error';
  }
}

アイテムとその並びが同じでも、セパレータが違うと同一とみなされないことを利用している。

@debug (1, 2, 3) == (1 2 3); // DEBUG: false

中身は同じでセパレータが違う2つのリストを append() しまくってつくってるので、実行コストが多分高い。

reverse($list, $separator)

項目が逆に並んだリストを作る。オプションでセパレータを変更可能。

@function reverse($list, $separator: null) {
  @if not is-list($list) {
    @return $list;
  }
  @if not has($separator, (space comma)) {
    $separator_orig: separator($list);
    @if has($separator_orig, (space comma)) {
      $separator: $separator_orig;
    }
    @else {
      $separator: space;
    }
  }
  $i: length($list);
  $result: ();
  @while $i > 0 {
    $result: append($result, nth($list, $i), $separator);
    $i: $i - 1;
  }
  @return $result;
}

セパレータを変更可能にする必要なんて(たぶん)ないのだけど、もとのセパレータ知らないと返すリストのセパレータをどうすればいいか悩む→セパレータ検出しよう→じゃあ変更オプションもつけようか的な。こうして無駄な機能が増えていくのだろう……

is() ― なんでも系

引数のキーワードでヒューマンリーダブルにしよう計画をやっていてひらめいた。こんなのはどうだろう。

@function is($args_list) {

  $value: nth($args_list, 1);
  $keyword: nth($args_list, 2);
  $target: nth($args_list, 3);

  @if $keyword == 'in' {
    @return contains($target, $value);
  }
}

こう使える。

$fruits: 'apple' 'banana' 'cherry';
@debug is('banana' in $fruits); // DEBUG: true

関数の引数を「スペース区切りのリストひとつ」として扱って、引数を自然文ちっくに書けるようにしている。引数の数は固定しといて、キーワードに応じて処理を振り分ける。キーワードを増やせば拡張可能。

@function is($args_list) {
  ...
  @if $keyword == 'in' {
    @return contains($target, $value);
  }
  @if $keyword == 'typeof' {
    @return type-of($value) == $target;
  }
  @if $keyword == 'not' {
    @return $value != $target;
  }

is(A not B) が要るかどうかはさておき、こんな感じに拡張可能。


という書きかけでした。ほんとはもうちょっと考えてたはずなんだけど忘れてしまった。

RubyではなくSassでというの、当時はアレ感しかない(まあ今もそうか)けど、libsassの隆盛でRubyでの拡張がちょっとという感じもあるし、本体側の拡張よりもSassScriptでできたほうが今後はいいのかもなと少し思った。速く動くのかどうかは知らないけれど。

Android Wearを半年使ってみたので雑感

Google I/OでLG G Watchを手に入れてから半年、Android Wearの感想を改めて。使って1ヶ月後に書いたGoogle+の投稿と変わらないところもあるけど。

腕時計を再発見する

LG G Watchを手に入れるまで、10年くらい腕時計をしていなかった。ケータイもあったし、どこかに時計はあったし。腕になにかついているのは煩わしいとも思っていた。

つけてみると、あらけっこう便利。ケータイを出さなくても、どこかにあるだろう時計を探さなくても、左腕をくいっとすれば時間が見られる。

腕時計をつけている煩わしさはある。パソコンでなにか作業しているときは外している。あと夏場は腕時計の裏に汗をかいて、ああこういうものだったなあと思いだした。

慣れてきて外出時は毎回つけて、それなりに便利かなと思っていたのだけれど、最近あることに気づいた。寒くなって袖の長いものを着ると、まくらないといけなくてちょっと煩わしい。それだけならまだいいんだけど、冬になってニットやコートなどまくりにくいものを着るようになると、ポケットからNexus 5を出したり、周りの時計を探したりするほうが楽になった。

Android Wear

Android Wear端末はスタンドアロンなものではなく、ペアリングするAndroid端末が必要。なのでI/Oでも、使ってるのがiPhoneだからアクティベートできないという人がかなりいた。あとはAndroidのバージョンも4.3以降とかで、使えないAndroid端末を持ってたひとも多かった。Nexus 5も配ってほしかったです!!!

Apple WatchもiPhoneが必要だけれど、それを知らないAndroidユーザーが買っちゃったりしないかね。販売員は大変なのではないか。

NotificationとGoogle Nowの端末

できること。AndroidのNotificationを腕元で見られる・多少それに対して反応できる端末という感じ。GmailやLINEならメッセージの内容も読めたりするけど、行あたりの文字数が少ないので、長いメールだとつらい。

あとはGoogle Nowの情報を適当なタイミングで手元に知らせてくれるので、スマートフォンよりもコンテクスチュアルかも。

ただ自分はNotificationやらがあまり好きではなく、けっこう切っていたりするので、通知ばんばんなひとよりは恩恵を受けてないのかも。

インプットはそんなできない

タッチパネル対応だけど、タッチによる文字入力とかは標準状態ではできない。任意の情報を入力したければ音声検索になる。

最初は面白がって使っていたのだけど、スマートフォンタブレットでもふだん音声検索をそんな使わない(目覚ましセットくらいかなあ)ので、こちらもあまり恩恵に預かれていない。

サードパーティのアプリを入れると文字入力やらゲームやらはできるけど、小さい画面をこねこねしまくるのはね…あまりかっこよくないよね。 あとあぶない。スマートフォンでもけっこう周りがみえなくなるのに、さらに小さい画面なのでほんとに周りがみえない。Apple Watchはけっこう画面をこねこねしそうな感じだけど、大丈夫なのかな。

LollipopでのPhone+Wear連携

出た当初のAndroidは4.4.xだったのだけれど、この状態で使うのは不便だった。

Wearのカードには、詳細をスマートフォンで開くメニューが出るものがある。ただスマートフォンのロックを解除しないといけないので、Wearからスマートフォンのアプリ起動操作→スマートフォン取り出す→ロック解除というステップを踏むのでシームレスにつながってる感がまったくなかった。

Lollipopになって、指定したBluetooth機器が近くにあればスワイプだけでロック解除できるようになった。ステップ数は変わらないけど、それなりにつながってる感はでた。ロックまで自動で解除してくれたらなとは思う。

電池がもたない

腕時計だと数年電池がもつような気がするけれど、Wear端末はBattery400mAhとかそこらなので、2日に一度は充電しないといけないのがつらい。Apple Watchも多少は長持ちしそうだけど、でも腕時計に比べるとだいぶいけてない。

充電も、LG G Watchは専用クレードルにしかUSBの端子がないので、それを忘れると充電できずだいぶつらい。Qiとかに対応してるものだといいけれど、それでも頻繁に充電しないといけないので煩わしい。もうちょっとなんとかなるといいけれど…

悪い中でも良い点というのは、電池の容量が小さいので充電にかかる時間がスマートフォンとかに比べて短いところ。

Web感はまだない

ようやくWebの話に持ってこられた。

画面が小さいとブラウザもなにもという感じなので、あまりWebとどうこうする感じはない。

技術的にも、Service WorkersがChromeに入り始めたところで、Web Push APIやWeb Notifications APIのSW拡張はこれから。来年の今頃くらいにはChromeでshipしてたりするのかな。GeofencingもGeolocation WGでやるようなので、それもくるとWebが少し絡んできそう。

ユースケースとしてはどんなものがあるだろう。スマートフォンでWebサイトにアクセスしてSWがインストールされれば、(パーミッションがらみでステップが増えるだろうけれど)、いろんなことをフックにWebからWear端末に通知できるようになる。

Webサイトへのアクセスがアプリに勝るケース…イベントごととかかなあ。あとは、ショッピングモールとかだろうか。Webにアクセスさせる動線を設計しないといけないけど、アプリのインストールよりは気軽にしてもらえるのではないか。

他にもWeb技術でできることが増えてくるとよいね。ではよいお年を。

Firefoxおめでとう&みんな開発版つかおうよ

Mozilla

Firefox 1.0のリリースから10年経った。おめでとうございます。

リリース前のことだけど、WikipediaFirefoxの記事をちょこちょこいじっていて、FirebirdからFirefoxに名前が変わった件について「Firebirdプロジェクト側から攻撃を受け〜」というふうな感じで書いたら、誰かに表現がきついと言われたことを思い出した。ちぇっ。

Firefox Developer Edition

さてそんな記念日にFirefox Developer Editionがリリースされた。

Aurora channelというBetaより一段階前のもののリブランディングなので、いまのところそんな違いはない気がする。ただMacでゴニョゴニョしなくても並行起動できるFirefoxができたのはうれしい。

テーマが黒くて見づらかったのだけれど、Developer Toolsの設定を開いてテーマをLight themeにしたら見やすくなった。

今後開発者向けの機能がこれだけに入るというの増えるのかな。ChromeOperaIEもとくにそういうのしてなかった気がするので、そうなるとちょっとユニークでおもしろいかも。

リリースチャンネルまでの長い期間をどうするか

David Baronがリリースサイクルについて書いていた。

FirefoxのリリースサイクルはFirefox 5くらいから6週間でチャンネルを移るようになっている。チャンネルはNightly→Aurora→Beta→Releaseの4つ。なので、Nightlyに入った変更や機能追加がリリースに入るまでに18週かかることになる。4, 5ヶ月。長い。

Chromeはというと、Chromium (→) Dev→Beta→Stableという感じ。ブランチが切られてしばらくChromiumとDevのバージョンが離れるけど、基本的には一緒なのでFirefoxよりも新しい機能が6週短く入る。3ヶ月くらいになるのかな。

よりNightlyに近いチャンネルのユーザー数は少ないし、リリースまでに長いので追加した機能が実際に使われるまでが遅くなって、それで起こったバグへの対処が遅くなる。なのでなんとかしたいらしい。

といっても、単純にチャンネルをひとつなくすというアイデアについては、ブランチが切れる間際に入ったものとかはまだリリースクオリティに達していないこともあるという話があるらしい。Aurora→Betaを経てよくするという癖ができてるということなのかな。

で、Betaを「ブランチしてから1週はRelease、以降はAuroraにする」という案をdbaronが出している。Beta内で参照するレポジトリ(安定性)を2段階にすると。こうなると、Nightly→だいたいAuroraな新チャンネル→Releaseとなる。Beta使ってる人はそれなりにいるっぽいので、これだと新しい機能へのフィードバックもそれなりにできそうと。ふむ。

案だけという話なのでどうなるかはわかんない。あとDeveloper Editionとの絡みも不明。でも短くなるといいなあ(ウォッチャー的にも)。

みんな開発版つかわないかい

フィードバックが欲しいというのはどのベンダーでも持っているようで、TPACやBlinkOnでも試験的な機能をベンダー接頭辞の反省を活かしどう安全に提供するかというセッションをやっていた。

Web開発やってる人が、リリース直後にバグでわんわん言うのはなんだか嘆かわしい。Firefoxはプロファイルマネージャを使えば複数のチャンネルを並行起動できるし、Chromeの場合はCanaryがある。Chrome, Firefoxは6週間で新しいものに置き換わる。いま作ってるものがローンチする時期が、いまDevなチャンネルなものがリリースに移る時期になることってけっこうあるのではないか。開発版だと開発中だからというバグもあるけど、よりナイトリーに近いもの使うことで事前に防げるものもあるだろうし、ブラウザ作る側にも少なからずヘルプになるので、悪くないと思うんだけどなあ。

<picture>まわり雑感 その1

HTML

Chrome 38が出て<picture>が有効になったので、いくつか記事を訳した。

HTML5 Rocksのは<picture>の簡単な使い方をざっと紹介。Dev.Operaのネイティブうんぬんは<picture><img srcset>, <img sizes>を細かく説明。もひとつのDev.Operaのサンプルのやつはユースケースごとに組み合わせをいろいろ書いたサンプル。それぞれ少しずつ違っている。おすすめは2つ目。ちょっと長いけど、書いているのがBlink, WebKit<picture>とかを実装しているYoavなので。

以下いろいろ。

だいたい<img srcset>で事足りる気がする

要素ということもあって<picture>に目が向きがちだけれど、“Don’t use (most of the time)”などで言われているとおり何が何でも<picture>つかえというわけではない。

使うのは<img srcset>Retina対応(x デスクリプタ使うやつ)が多いのではないか。他の属性やら<picture>はResponsive Web Designでの利用が多そうだけれど、Retina対応はRWD関係なくやりたいことだろうから。

なので基本的にはこんなコードでよさそう。2xな画像を書いたsrcsetをつけるだけという。

<img srcset="image-2x.png 2x" src="image.png" alt="...">

ただiPhoneさまが3xになったので、3xな画像を…といきっちゃうかもしれない。そしたらこうだ。

<img srcset="image-2x.png 2x, image-3x.png 3x" src="image.png" alt="...">

画像リソースの数は増えるけれど、マークアップが複雑になったとはまだ言わなくてもいいレベルではないか…な。

3xいるのかね

さて、3x。そんなに要らないのではないかと思っている。というのは1x→2xほどのぼけ具合がないから(個人の感想です)。

Androidは昨年秋から3x時代に入ってるのだけれど、アプリのアイコンやアセットで3xなものがあるのはまだ多くない。たとえばLINEとか、スタンプがうっすらぼやけたりしている。 とはいえアプリの対応とかはiPhoneが牽引している感があるので、iPhone 6 Plusが発売されたいま、3xな画像が増えてくるのかなとも思う。

でもね…あまりにもバリエーションが増えすぎてしまうと作成や管理の面でもえらく大変になるだろうし、3xや4xが出たからといって純朴に対応しているとすりきれてしまうのではないか。

軽く調べた感じだと、いまのところAppleapple.comで3xな画像を出していない。iPhone 6 Plusは3xといっても、幅1242pxな画面を1080pxに縮小して表示とすごいことになってるので、単純に2xのアセットを3xで表示した見た目になるわけではない。なのでぼけ度合いは2xなものを3xでというものよりは多少よいのかもしれない。

<img>ではコントロールできない

srcsetsizesで指定する画像はあくまで「ヒント」なので、「意図とは違った画像が表示されるかも」というのは頭の片隅に置いとくといいかもしれない。

たとえば「重いしRetinaなんかいらねえよ」みたいなオプションが設定についたり、ブラウザがそこら辺賢くなってくれたりすると(Operaとかやりそうな気がする)、2x/3xなディスプレイでも1xな画像を出しちゃうなんてことが起こるだろう。

最近だとBlinkで、リサイズ時にすでに高解像度の画像がある場合は、candidateが変化してもそれをダウンロードしなくなった。軽くバグな気もするけれど……

<picture><source>でコントロールできなくもない

「コントロールできないのはちょっと…」というケースはあまりあってほしくないけれどあるかもしれない。<picture><source>を使えば、コントロールさせることも一応できる。<source>はヒントではなく「命令」になるからだ。

たとえば、2xには2xな画像を、3xには3xなそれを確実に提供したい場合はこうなるだろう。

<picture>
  <source media="(min-resolution: 3dppx)" srcset="image-3x.png">
  <source media="(min-resolution: 2dppx)" srcset="image-2x.png">
  <img src="image.png" alt="...">
</picture>
  • srcsetに画像をならべるのではなく、<picture><source>をならべる
  • srcset内の3xの代わりにmediamin-resolution: 3dppxを使う

1行でだいたい同じものが書けてたのにだいぶ大げさになった。Webでコントロールを求めればめんどくさくなるのだ、というのを暗に伝えたいのかもしれない。

もちろん、art directionのように画像を意図どおりに切り替えたいというケースはある。それはちゃんと<picture>を使わないとだめだろう。でもそうでない場合はなるべく減らして、ブラウザに任せたほうが賢いかなと。


マークアップとスタイルのseparation of concernsあたりについても書こうと思ったのだけど、考えがまとまってないのでとりあえず。