干しもの:H 5プラットフォームのジェスチャーライブラリを実現します。

8005 ワード

二つの文章の「移動端のタッチ事件」に続きます。http://www.lizhiqianduan.com/blog/index.php/2018/06/07/mobile-multi-touch/ を選択します
及び「ジェスチャーの判断条件」http://www.lizhiqianduan.com/blog/index.php/2018/06/27/condition-of-guesture/この二つの文章を読んだ読者の友達はもう自分でジェスチャーライブラリを作ったかもしれません。
この文章は私が自分で書いたジェスチャー・ボックスを共有します。
ここは私が書いた例示的なリンクです。http://www.lizhiqianduan.com/products/ycc/examples/multi-touch/このリンクはモバイルデバイスで確認する必要があります。
新しいGestureクラスのバインディングHTML要素
前のブログを見た読者は、ジェスチャーは、あるHTML要素のtouchstart、touchmove、touchendイベントを通じてシミュレートされています。
これらのイベントのコールバックは、タッチされているHTML要素を示すオブジェクトとしてタッチされています。
したがって、このジェスチャーライブラリはHTMLをバインディングする必要があります。その後、すべてのジェスチャーはこのHTML要素でトリガされます。大体次の通りです
 /*
 * @param option
 * @param option.target      HTML  
 * @extends Ycc.Listener
 * @constructor
 */
Ycc.Gesture = function (option) {
   Ycc.Listener.call(this);

   this.option = option;      
};
Ycc.Gesture.prototype = new Ycc.Listener()
このYccは私が今書いているプロジェクトです。ここで私たちのGesture類はリマスタークラスを引き継いでいます。
このListener類の主な機能は、イベントの傍受とトリガであり、我々のGesture類の傍受とGestureをトリガするためのカスタムイベントです。
このように定義してから、ジェスチャーを監督して触発するのはとても便利です。次のようにするだけでいいです。
var demo = new Ycc.Gesture({target:document.body});
demo.ontap = function (touch) {
   //todo ...
};
demo.ondoubletap = function (touch) {
   //todo ...
};
demo.onzoom = function (touch) {
   //todo ...
};
demo.onrotate = function (touch) {
   //todo ...
};
Gestureクラス初期化
私たちのクラスがあったら、クラスの初期化関数が必要です。私が設計した大体の構造は以下の通りです。
/*  
 *     
 * @private
 */
Ycc.Gesture.prototype._init = function () {
   var self = this;
   var tracer = new Ycc.TouchLifeTracer(
       {target:this.option.target}
   );
   tracer.onlifestart = function (life){
       // todo ...
   };
   tracer.onlifechange = function (life){
       // todo ...
   };
   tracer.onlifeend = function (life){
       // todo ...
   };
};
ここにはYcc.TouchLifeTracerがあります。タッチポイントのライフサイクルの追跡モジュールです。
その主な機能はHTML要素に接触する各タッチポイントに対して、接触開始から接触終了までの追跡である。
その実現については、前の文章でも言及しましたが、ここではよく分からない読者はこの文章の冒頭の二つの文章を見てください。
次に、各ジェスチャーの実現について簡単に説明します。
tapジェスチャーの実現
上のこの追跡モジュールがあります。私たちのGesture類の実現はずっと容易です。
各ライフサイクルでジェスチャーの判定条件に応じてイベントをトリガするだけでいいです。
tapジェスチャーの判定条件は以下の通りです。
1、            
2、          ,   300ms
3、             
それなら、私達のトラッキングに対応して実装すればいいです。初期化関数_initの内容は大体以下の通りです。
// Gesture  
var self = this;
//         
var prevent = {
    tap:false
};

tracer.onlifestart = function (life) {
  //   1:        ,   tap  
  if(tracer.currentLifeList.length>1){
      prevent.tap = true;
  };
};
tracer.onlifechange = function (life) {
  //   1:        ,   tap  
  if(tracer.currentLifeList.length>1){
      prevent.tap = true;
    };

  //   2:           ,   10px,    tap
  var firstMove = life.startTouchEvent;
    var lastMove = Array.prototype.slice.call(life.moveTouchEventList,-1)[0];
  if(Math.abs(lastMove.pageX-firstMove.pageX)>10 || Math.abs(lastMove.pageY-firstMove.pageY)>10){
    prevent.tap=true;
  }   

};
tracer.onlifeend = function (life) {
  //   1:    ,   0
    if(tracer.currentLifeList.length===0){
        //   3:tap       300ms
       if(!prevent.tap && life.endTime-life.startTime<300){
           //   tap
           self.triggerListener('tap',life.endTouchEvent);
        }
    }
};
doubletapジェスチャーの実現
doubletapジェスチャーの判定条件は以下の通りです。
1、    tap  
2、  tap   x、y          ,   10px
3、  tap               ,   300ms
それはtapの上で創立したので、tapを触発する時doubletap条件を判断するだけでいいです。
その初期化関数_initの内容は大体以下の通りです。
// Gesture  
var self = this;
//         
var prevent = {
  tap:false
};
//          
var preLife,curLife;

tracer.onlifestart = function (life) {
  if(tracer.currentLifeList.length>1){
    prevent.tap = true;
  };
};
tracer.onlifechange = function (life) {
  if(tracer.currentLifeList.length>1){
    prevent.tap = true;
  };

  var firstMove = life.startTouchEvent;
  var lastMove = Array.prototype.slice.call(life.moveTouchEventList,-1)[0];
  if(Math.abs(lastMove.pageX-firstMove.pageX)>10 || Math.abs(lastMove.pageY-firstMove.pageY)>10){
    prevent.tap=true;
  }   

};
tracer.onlifeend = function (life) {
    if(tracer.currentLifeList.length===0){
       if(!prevent.tap && life.endTime-life.startTime<300){
          //   tap
          self.triggerListener('tap',life.endTouchEvent);

          //          
          //      300ms ,          10px ,    doubletap  
                    if(preLife 
                        && life.endTime-preLife.endTime<300 
                        && Math.abs(preLife.endTouchEvent.pageX-life.endTouchEvent.pageX)<10
                        && Math.abs(preLife.endTouchEvent.pageY-life.endTouchEvent.pageY)<10)
                    {
                       //   doubletap
                       self.triggerListener('doubletap',life.endTouchEvent);
                       preLife = null;
                       return this;
                    }
                    preLife = life;      
              }
    }
};
ローテートとズームズームズームズームズームズームズームズームを実現しました。
彼らの判断条件は同じです。ここに置いて話します。
1、             ,                    
2、         
その初期化関数_initの内容は大体以下の通りです。
// Gesture  
var self = this;
//         
var prevent = {
  tap:false
};
//          
var preLife,curLife;

tracer.onlifestart = function (life) {
    //   1:       
  if(tracer.currentLifeList.length>1){
    prevent.tap = true;

    //   、            
    preLife = tracer.currentLifeList[0];
    curLife = tracer.currentLifeList[1];
    return this;
  };
};
tracer.onlifechange = function (life) {
    //   2:       
  if(tracer.currentLifeList.length>1){
    prevent.tap = true;
    //            
    var rateAndAngle = self.getZoomRateAndRotateAngle(preLife,curLife);

    //   zoom  
    if(Ycc.utils.isNum(rateAndAngle.rate)){
       self.triggerListener('zoom',rateAndAngle.rate);
    }
    //   rotate  
    if(Ycc.utils.isNum(rateAndAngle.angle)){
       self.triggerListener('rotate',rateAndAngle.angle);
    }
  };   
};
tracer.onlifeend = function (life) {
    //  lifeend  
};
上のコードの中で、一番不思議なのはgetZoomRateAndRotateAngle関数かもしれません。
主な機能は、2つの接触点の位置情報に基づいて回転角度と拡大率を取得することである。
それはただ数学的な方法を使っただけです。
次のように
ズーム比=現在の距離/初期距離
回転角度=初期ベクトルと現在のベクトルの角度
その大まかな実現は以下の通りである。
Ycc.Gesture.prototype.getZoomRateAndRotateAngle = function (preLife, curLife) {

   //     
   var x0=preLife.startTouchEvent.pageX,
      y0=preLife.startTouchEvent.pageY,
      x1=curLife.startTouchEvent.pageX,
      y1=curLife.startTouchEvent.pageY;

   var preMoveTouch = preLife.moveTouchEventList.length>0?preLife.moveTouchEventList[preLife.moveTouchEventList.length-1]:preLife.startTouchEvent;
   var curMoveTouch = curLife.moveTouchEventList.length>0?curLife.moveTouchEventList[curLife.moveTouchEventList.length-1]:curLife.startTouchEvent;

   //     
   var x0move=preMoveTouch.pageX,
      y0move=preMoveTouch.pageY,
      x1move=curMoveTouch.pageX,
      y1move=curMoveTouch.pageY;

   //     
   var vector0 = new Ycc.Math.Vector(x1-x0,y1-y0),
   //     
      vector1 = new Ycc.Math.Vector(x1move-x0move,y1move-y0move);

   //     
   var angle = Math.acos(vector1.dot(vector0)/(vector1.getLength()*vector0.getLength()))/Math.PI*180;

   return {
      //       
      rate:vector1.getLength()/vector0.getLength(),

      //     ,       
      angle:angle*(vector1.cross(vector0).z>0?-1:1)
   };
};
数学の基礎がよくない読者の友達は、考えなくてもいいです。そのままコピーしてください。
その他のジェスチャー
省略する
他のジェスチャーは比較的簡単で、私達の判断条件によって、ライフサイクル追跡ができて、とても便利に実現できます。
ここではもう共有しません。興味のある方はYccプロジェクトのソースコードのジェスチャーモジュールを参照してください。https://github.com/lizhiqianduan/ycc/tree/develop
締めくくりをつける
まだたくさんのジェスチャーがあります。私たちの倉庫にはないです。読者はこの考え方によって自分で広げられます。条件が明確であると判断すれば、文章という考え方に沿って実現しやすいです。
添付:
Ycc.Gesture完全コード:https://github.com/lizhiqianduan/ycc/blob/develop/src/Ycc.Gesture.class.js​