49

マウスホイールイベント処理マニアックス

Embed Size (px)

Citation preview

Text

マウスホイールイベント処理

マニアックス

中野雅之

•肩書き

•正式: Mozilla Japan 国際化担当マネージャ

•半公式: Mozilla Japan ジョギング部大阪支部長

•非公式: Mozilla Japan 大阪支部長

•大阪の自宅で、自宅警備しながら仕事してます

中野雅之

•色んなアカウント

•メールアドレス: [email protected]

• Skype: masayuki-nakano

• Twitter: @d_toybox

• Facebook: masayuki.nakano.560

• Blog: 「もずはっく日記」で検索

アジェンダ

•各プラットフォームのネイティブイベント

•レガシーイベント

• MouseScrollEvent

• MouseWheelEvent

• WheelEvent

• WheelEventとレガシーイベントの関係

•デフォルトアクションの考え方

各プラットフォームの ネイティブイベント

各プラットフォームのネイティブイベント

• Windows

• WM_MOUESEWHEEL (Win 98以降)

• 垂直方向へのホイール操作に対するイベント

• マウスホイールの回転量が伝達される

• 1ノッチあたり120

• Vista以降では120未満のデルタ値でも発生する (高解像度スクロ

ール)

• ページ単位でのスクロールにも設定可能

• WM_MOUSEHWHEEL (Win Vista以降)

• 水平方向へのホイール操作に対するイベント

• 詳細はWM_MOUSEWHEELと同様

各プラットフォームのネイティブイベント

• Mac

• -deltaX、-deltaY、-deltaZ

• ピクセルスクロールをサポートしていないデバイスで利用

• スクロールする行数を示すと思われる(ドキュメントに明記されてい

ない)

• deltaZを利用するデバイスは無い?

• -scrollingDeltaX、-scrollingDeltaY

• ピクセルスクロールをサポートしているデバイスで利用

• 連続スクロールをサポートするデバイスか否か、という概念がある

• OSレベルで操作量・頻度に応じて、加速が行われる

• システム設定でスクロール速度はカスタマイズ可能

各プラットフォームのネイティブイベント

• Linux

• GdkEventScrollというイベントが発生し、スクロール方向のみ

が分かる

•システム設定でもスクロール量は設定できないので、アプリの

解釈によってスクロール量が決まる

MouseScrollEvent

MouseScrollEvent

• Firefox独自イベント

• Firefoxでは、window.MouseScrollEventは、undefined

にはならない

• DOMMouseScrollと、MozMousePixelScrollの二つのイ

ベントが存在する

•関係性や動作が非常に複雑

MouseScrollEvent

•属性

• detail

•スクロール量を表す

•正なら、下、もしくは右方向

•負なら、上、もしくは左方向

• axis

• HORIZONTAL_AXIS (1) なら水平方向

• VERTICAL_AXIS (2) なら垂直方向

MouseScrollEvent

• DOMMouseScroll

• detail値は、スクロールする行数

• detail値がSCROLL_PAGE_DOWN (32768)か、

SCROLL_PAGE_UP (-32768)なら、1ページ、スクロール

(Windowsのみ)

• MozMousePixelScroll

• detail値は、スクロールするピクセル数

MouseScrollEvent

• DOMMouseScrollとMozMousePixelScrollの関係

• MozMousePixelScrollのスクロール量がtargetの行高

で、1行分以上累積した場合にのみ、

DOMMouseScrollイベントも発生する

•両方発生する場合の発生順序は、 DOMMouseScroll

が先で、 MozMousePixelScrollが後

MouseScrollEvent

• DOMMouseScrollとMozMousePixelScrollの関係

• MozMousePixelScrollのみが発生することがあること

に注意

• DOMMouseScrollイベントだけでpreventDefault()を呼

び出してもスクロールは完全に抑制できない

• Google Mapsでも実際に、これが原因のバグがある

• Bug 681200 - Mouse wheel on google maps

zooms map and scrolls page

MouseWheelEvent

MouseWheelEvent

• IE発祥のイベント。Firefox以外のメジャーブラウザは実装している

•ただし、実装方法は、ブラウザ間でバラバラ

• IEでのみwindow.MouseWheelEventがundefinedではない (Chrome 27、Safari 6、Opera12.12では、サポートしているにも関わらず、undefined)

• mousewheelイベントのみが存在する

MouseWheelEvent

•属性

• wheelDelta

•ホイールの回転量を表す

•正なら、奥方向、負なら、手前方向へホイールが回転

したことを表す

• IEでは垂直方向へのホイール操作でしか発生しない

•値については後ほど解説

MouseWheelEvent

•属性

• detail値

• Opera以外では常にゼロ

• Operaではスクロールする行数を示すと思われる値

•下、もしくは右への操作なら、正の値

•上、もしくは左への操作なら、負の値

MouseWheelEvent

•属性

• wheelDeltaX

•水平方向へのホイールの回転量を表す

• Chrome、Safari、Operaでのみサポート

• IE非サポート

•値の内容はwheelDeltaと同様

•右への操作なら、負の値、左への操作なら正の値

MouseWheelEvent

•属性

• wheelDeltaY

•垂直方向へのホイールの回転量を表す

• Chrome、Safari、Operaでのみサポート

• IE非サポート

•値の内容はwheelDeltaと同様

•下への操作なら、負の値、上への操作なら正の値

MouseWheelEvent

• wheelDelta*の値の闇

• IEでは、ネイティブイベントのWM_MOUSEWHEEL、

WM_MOUSEHWHEELのデルタ値がそのまま設定されるため、

常に、ホイールの回転量を表す

•高解像度スクロールがサポートされていないデバイス

であれば、1ノッチあたり、120になる

MouseWheelEvent

• wheelDelta*の値の闇

• Windows版Chromeでは、IE同様、ネイティブイベント

のWM_MOUSEWHEEL、WM_MOUSEHWHEELのデルタ値がその

まま設定されるため、常に、ホイールの回転量を表す

•高解像度スクロールがサポートされていないデバイス

であれば、1ノッチあたり、120になる

MouseWheelEvent

• wheelDelta*の値の闇

• Mac版Chromeでは、連続スクロールがサポートされて

いないデバイスの場合、加速を考慮しない値、つまり、

純粋な操作量が利用される。手元のマウスでは1ノッチ

で120

•連続スクロールがサポートされているデバイスの場合、

加速を考慮した値を使用している。おそらく、Safariと同

じ結果になる

MouseWheelEvent

• wheelDelta*の値の闇

• Safariでは、連続スクロールがサポートされているデバ

イスでも、そうではないデバイスでも関係なく、加速を考

慮した値を使用している

•連続スクロールがサポートされていないデバイスの場

合、Mac版Chromeとは異なる値になる

MouseWheelEvent

• wheelDelta*の値の闇

• Mac版Operaでは、連続スクロールがサポートされてい

るデバイスでも、そうではないデバイスでも関係なく、加

速を考慮した値を使用している

•スクロール量(detail値)に対して、-40倍の値が用いら

れているため、多くの場合、同じ操作で、Chromeや

Safariよりも格段に大きな数字になる

MouseWheelEvent

• wheelDelta*の値の闇

• Mac版のブラウザでは、加速を考慮した場合には、

Windows版や、Linux版の同じブラウザとも互換性がな

くなる

•その理由は簡単で、デバイスの操作量を示す物理的な

デルタ値のはずが、Macでのみ、論理的なスクロール

量に応じて算出した値を用いているため

MouseWheelEvent

• wheelDelta*の値の闇

• Linux版Chromeでは、1ネイティブイベントあたり、120

がセットされるため、Windows版との互換性が高い

• Linux版Operaでは、1ネイティブイベントあたり、80が

セットされる

レガシーイベントまとめ

• Firefoxのみが独自イベントを実装しているが、理解すれ

ば一番実用的(ただし、複雑で好ましいものではない)

• IEでは横スクロールに対応できない

• Chrome、Safari、Operaでは互換性がなさ過ぎる上に、

Chromeの動作に関しては、ブラウザの名前から処理を

切り分けることも不可能にしている

•要するに、Firefox以外では使い物にならない

WheelEvent

WheelEvent

• W3CのDOM Level 3 Eventsで標準化されている

• IE 9発祥のイベントで、Firefoxも、17以降でサポート

• Safari、Chromeはオブジェクトのみサポートしているため、window.WheelEventは、IE、Firefox、Safari、Chromeでundefinedとならない

• wheelイベントのみが存在し、斜め方向のスクロールもひとつのイベントで表現できるようになっている

WheelEvent

•属性

• deltaX、deltaY

• X軸、Y軸方向へのスクロール量をdouble値で示す

•正なら、右、もしくは下方向

•負なら、左、もしくは上方向

• Firefoxでは、Macで加速が行われている場合、その値

が反映される

WheelEvent

•属性

• deltaZ

• Z軸方向へのスクロール量をdouble値で示す

• Macのネイティブイベントに存在する概念なので存在

すると思われる

• FirefoxではMac版でも常にゼロとなる(正負をどうす

れば良いのか判断できないため)

•他のOSではそもそもこの概念はない

WheelEvent

•属性

• deltaMode

• deltaX、deltaY、deltaZの値の単位を表す

• DOM_DELTA_PIXEL (0)の場合、ピクセル数

• DOM_DELTA_LINE (1)の場合、行数

• DOM_DELTA_PAGE (2)の場合、ページ数

• delta*がdouble値のため、0.5行のスクロールや、0.5

ページのスクロール、といった表現が可能

WheelEvent

• IE9

• deltaX、deltaY、deltaZはdouble値ではなく、整数値

• IE9、IE10

• deltaMode値はDOM_DELTA_PIXEL

•行単位のスクロールの場合、1行、もしくは1文字あたり、

固定値を乗算した値(参考: Y方向時: 32px (IE10)、

41px (IE9)、おそらく環境・設定依存)

•ページ単位のスクロールの場合、そのページの高さ(≠

実際にスクロールされる要素の高さ)

WheelEvent

• Firefox

• Windows版では、deltaMode値は、DOM_DELTA_LINEか、

DOM_DELTA_PAGE (システム設定依存)

• Linux版では、deltaMode値は常に、DOM_DELTA_LINE

• Mac版では、deltaMode値は、DOM_DELTA_LINEか、

DOM_DELTA_PIXEL (操作したデバイスがピクセル単

位のスクロールをサポートしているか否かに依存)

•各delta値は設定で変更できるため、ゲームの入力のよ

うな用途では利用できない

WheelEventと レガシーイベントの関係

WheelEventとレガシーイベントの関係

• IE、Firefox共通

• "wheel"イベントが発生した後に、レガシーイベントが発

生する

• "wheel"イベント、レガシーイベントのどちらの

preventDefault()でも、デフォルトアクションをキャン

セルできる

• "wheel"イベントで、preventDefault()を呼び出すと、

レガシーイベントは発生しない

WheelEventとレガシーイベントの関係

• Firefox

• "wheel"イベントの情報と、イベントのターゲットの行高

や、targetに一番近い、スクロール可能な要素の幅・高

さから、レガシーイベントが生成される

WheelEventとレガシーイベントの関係

•まとめると、WheelEventをサポートしているブラウザであ

れば、"wheel"イベントのみを処理し、それ以外のブラウ

ザでは、"mousewheel"イベントを処理すれば良い

•しかし、Chrome、Safariでは、window.WheelEventが

undefinedを返さないので正しい判定が不可能になって

いる

WheelEventとレガシーイベントの関係

•イベント処理後に必ず、preventDefault()を呼び出す

のであれば、全てのイベントにリスナを設置することで解

決する

•例:

foo.addEventListener("wheel", wheelHandler, false);

foo.addEventListener("mousewheel"

mousewheelHandler, false);

WheelEventとレガシーイベントの関係

•イベント処理後に、preventDefault()を呼び出さないの

であれば、悩ましい。

• "onwheel" in document.bodyはFirefoxではtrueにな

るが、IEではfalse

• window.MouseWheelEventはIE 8以前、Safari、Chrome、

Operaはundefinedを返すので、IE 9以降かどうかの判

定に使えるものの、Safari、Chromeが実装を改善すると、

破綻するので危険

デフォルトアクションの考え方

デフォルトアクションの考え方

• preventDefault()を呼び出さない場合に、ブラウザによ

って実行されるのがデフォルトアクション

•モディファイアキー無しであれば、スクロールが一般的

• Windowsであれば、Ctrlキーを押している場合、スクロー

ルではなく、ズームになるのが一般的

•つまり、"wheel"イベントや、レガシーホイールイベント

は、スクロールが発生することを予告するイベントでは

ない

デフォルトアクションの考え方

• Firefoxでは、マウスホイールの操作にトランザク

ションの概念があるため、targetに一番近い、スク

ロール可能な祖先要素がスクロールされるとは限ら

ない(Mouse wheel transaction)

•ページ全体にスクロール中に、他のスクロール可能な

要素がマウスカーソルの下に移動してきても、ページ

全体をスクロール対象として考える(タイムアウトあり)

• DOMイベントのtargetは、これに関係なく、常に、カー

ソルの下にある要素になる

デフォルトアクションの考え方

•つまり、デフォルトアクションがスクロールの場合であっ

ても、"wheel"イベントは、実際のデフォルトアクション

の内容と一致するとは限らない

デフォルトアクションの考え方

•これらのことから言えるのは、"wheel"イベントやレガシ

ーイベントを利用して、独自のスクロール可能な要素を

作る場合には細心の注意が必要

•本当に必要なことか?

•アクセシビリティへの配慮は十分か?

•全てのプラットフォームの全てのブラウザの、全ての

現役のバージョンでテストする覚悟(予算)はあるか?

•万が一、問題が報告された場合に対応する覚悟(余

裕)はあるか?

まとめ

•できれば、面倒極まりないので、関わらない

•やるなら、WheelEventをメインに、がんばる

•WebKitとBlink陣営に、WheelEventを実装する

ようにみんなで声をあげよう!

Text

Q&A?