Polymerは、特別なユーザーインタラクションに向けて独自の「ジェスチャーイベント」をオプションでサポートしています。ジェスチャーイベントは、タッチ及びマウスの両環境でアップ/ダウン/トラックに関して一貫したイベントを発生させることができ、仕様のmouse-イベントやtouch-イベントに代えて利用されることが推奨されます。これにより、タッチとマウスの両デバイス間で相互運用性が向上します。

通常、モバイルブラウザではtapの代わりに、標準のclickを利用します。 後方互換を担保するためtapイベントはジェスチャーイベントのミックスインに含まれていますが、最新のモバイルブラウザではもはや必要とされません。

ハイブリッドエレメントを使用する場合は、デフォルトでジェスチャーイベントがサポートされます。Polymer.Elementをベースにクラススタイルで作成したエレメントでは、Polymer.GestureEventListenersミックスインをインポートすることで、ジェスチャサポートを明示的に追加する必要があります。

<link rel="import" href="polymer/lib/mixins/gesture-event-listeners.html">

<script>
    class TestEvent extends Polymer.GestureEventListeners(Polymer.Element) {
      ...
</script>

ジェスチャーイベントにはいくつか追加の設定が必要なため、単純に一般的なaddEventListenerメソッドを用いてリスナーを追加することはできません。ジェスチャーイベントをリッスンするには、:

  • ジェスチャーイベントの一つにアノテーション付イベントリスナーを使用する。

    <div id="dragme" on-track="handleTrack">Drag me!</div>
    

    アノテーション付イベントリスナーを使用した場合、Polymerは自動的にジェスチャーイベントを特別に記録するようになります。

  • Polymer.Gestures.addListener/Polymer.Gestures.removeListenerメソッドを使用する

    Polymer.Gestures.addListener(this, 'track', e => this.trackHandler(e));
    

    このPolymer.Gestures.addListenerメソッドを使用して、ホストエレメントにリスナーを追加できます。

サポートされるジェスチャーイベントのタイプは次のとおりです。各タイプについて簡単な説明とevent.detailから取得できる詳細なプロパティを列挙して紹介します。:

  • down:指/ボタンが下げられた
    • x:イベントのclientX座標
    • y:イベントのclientY座標
    • sourceEventdownアクションを最初に発生させたDOMイベント
  • up:指/ボタンが上げられた
    • x:イベントのclientX座標
    • y:イベントのclientY座標
    • sourceEventupアクションを最初に発生させたDOMイベント
  • tap:ダウン&アップが発生した
    • x:イベントのclientX座標
    • y:イベントのclientY座標
    • sourceEventtapアクションを最初に発生させたDOMイベント
  • track:指/ボタンが下げながら動いた
    • state:トラッキング状態を示す文字列:
      • start:トラッキングが最初に検出された時に発生(指/ボタンが押され、事前に設定された距離の閾値を超えて移動した時)
      • track:トラッキングの最中に発生
      • end:トラッキングが終了した時に発生
    • x:イベントのclientX座標
    • y:イベントのclientY座標
    • dx:トラックイベントの開始時から水平方向にピクセル単位で生じた変化
    • dy:トラックイベントの開始時から垂直方向にピクセル単位で生じた変化
    • ddx:トラックイベントの終了時からから水平方向にピクセル単位で生じた変化
    • ddy:トラックイベントの終了時からから垂直方向にピクセル単位で生じた変化
    • hover():現在ホバーされているエレメントを判断するために呼び出される可能性のあるメソッド

宣言的イベントリスナーの例

<link rel="import" href="polymer/polymer-element.html">
<link rel="import" href="polymer/lib/mixins/gesture-event-listeners.html">

<dom-module id="drag-me">
  <template>
    <style>
      #dragme {
        width: 500px;
        height: 500px;
        background: gray;
      }
    </style>

    <div id="dragme" on-track="handleTrack">[[message]]</div>
  </template>

  <script>
    class DragMe extends Polymer.GestureEventListeners(Polymer.Element) {

      static get is() {return 'drag-me'}

      handleTrack(e) {
        switch(e.detail.state) {
          case 'start':
            this.message = 'Tracking started!';
            break;
          case 'track':
            this.message = 'Tracking in progress... ' +
              e.detail.x + ', ' + e.detail.y;
            break;
          case 'end':
            this.message = 'Tracking ended!';
            break;
        }
      }

    }
    customElements.define(DragMe.is, DragMe);
  </script>
</dom-module>

命令的なイベントリスナーの例

以下の例では、ホストエレメントに対してリスナーを追加するのにPolymer.Gestures.addListenerを使用しています。このようなケースでは、アノテーション付イベントリスナーで設定することができません。もし、リスナーがホストエレメントまたはShadow DOMの子に対して設定されている場合には、通常、一度追加したイベントリスナーに関してその削除について心配する必要はありません。

動的に追加された子にイベントリスナーを設定する場合には、子を削除する時点で、Polymer.Gestures.addListenerによって設定されたイベントリスナーを削除する必要があるかもしれません。これは、子エレメントがガベージコレクトされるようにするためです。

<link rel="import" href="../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../bower_components/polymer/lib/mixins/gesture-event-listeners.html">

<dom-module id="drag-me-app">
  <template>
    <style>
      :host {
        border: 1px solid blue;
        background: gray;
      }
    </style>
    [[message]]
  </template>

  <script>
    class DragMeApp extends Polymer.GestureEventListeners(Polymer.Element) {
      static get is() { return 'drag-me-app'; }
      static get properties() {
        return {
          message: {
            type: String,
            value: "Select my text. I will track you."
          }
        };
      }
      constructor() {
        super();
        Polymer.Gestures.addListener(this, 'track', e => this.handleTrack(e));
      }
      handleTrack(e) {
        switch(e.detail.state) {
          case 'start':
            this.message = 'Tracking started!';
            break;
          case 'track':
            this.message = 'Tracking in progress... ' +
              e.detail.x + ', ' + e.detail.y;
            break;
          case 'end':
            this.message = 'Tracking ended!';
            break;
        }
      }
    }
    customElements.define(DragMeApp.is, DragMeApp);
  </script>
</dom-module>

ブラウザは、特定のジェスチャーに対するネイティブな処理を実装しており、タッチによるスクロールやユーザーによるピンチジェスチャーによるコンテンツのズームなどをサポートしています。

ジェスチャーイベントをリッスンすると、デフォルトではブラウザネイティブなジェスチャー処理は無効になります。例えば、trackイベントのリスナーを持つノードは、ブラウザがスクロールやピンチズームのジェスチャーを処理しないようにします。

Polymerのジェスチャーイベントに加えてネイティブなジェスチャー処理を利用したい場合は、 Polymer.Gestures.setTouchAction関数を使用することで、ブラウザがどのイベントをネイティブに処理するか指定できます。例えば、ブラウザによる垂直方向のスクロールをサポートし、エレメントが左右スワイプを処理できるようにするには次のように記述します。:

constructor() {
  super();
  Polymer.Gestures.addListener(this, 'track', this.handleTrack);
  // Let browser handle vertical scrolling and zoom
  Polymer.Gestures.setTouchAction(this, 'pan-y pinch-zoom');
}

setTouchActionの最初の引数は、リスナーがアタッチされているノードです。 2番目の引数は touch-action CSSプロパティの有効な値です。

touch-actionキーワードの完全なリストについては、touch-action on MDNを参照してください。

イベントリスナーを追加した後は、いつでも setTouchActionを呼び出すことができます。この変更は、setTouchActionが呼び出さル時、実行中のジェスチャーには影響しません。

ネイティブジェスチャー処理が有効になっているときは、ブラウザによっては、Polymerのジェスチャーイベントが発生する可能性があります。ジェスチャーイベントが発生すると、ネイティブブラウザの処理の前にリスナーが呼び出されます。イベントで preventDefaultを呼び出すことで、ネイティブブラウザの処理を防ぐことができます。

handleTrack(e) {
  // do something
  ...
  // suppress native scrolling
  e.preventDefault();
}

ジェスチャーイベントリスナーがスクロールパフォーマンスを妨げないようにしたい場合は、次のセクションで説明するように、すべてのジェスチャーイベントリスナーを強制的にpassiveにします。

アプリケーションは Polymer.setPassiveTouchGestures(true)を呼び出すことで、ジェスチャー関連の全てのイベントリスナーを強制的にpassiveにすることができます。passiveイベントリスナーは、preventDefaultを呼び出してデフォルトのブラウザ処理を妨げることはないので、ブラウザはイベントリスナーから戻るのを待たずにネイティブのジェスチャーを処理できます。

ジェスチャー関連のイベントリスナーを設定する前にsetPassiveTouchGesturesを呼び出す必要があります。例えば、アプリケーションのエントリーポイントや、メインのアプリケーションエレメントのconstructorで設定するようにします(常に最初にロードされるエレメントで呼び出す必要があります)。

passiveタッチジェスチャーを使用すればスクロールのパフォーマンスは向上しますが、アプリケーション内のいずれかのエレメントのジェスチャーでpreventDefaultが呼び出される可能性があれば問題が発生するかもしれません。