iPhoneのSafariで左右フリックを検出するjQueryプラグイン作った

はじめに

iPhoneSafari上で動くアプリを作っているのですが、左右方向のフリックを検出をしたくなったのでjQueryプラグイン(landscapeFlickSensor)を作りました。せっかくなので公開します。ソースは一番下です。
JavaScriptjQueryは最近かじり始めたばかりなので間違いやお作法に沿ってない部分があったらぜひ教えてください。

できること

iPhoneSafari上で画面をフリックしたときに指定の関数を呼び出すことができます。

一応iPadでも動いてるっぽいことを確認しましたが、どうなんでしょう・・・androidとかは手元に無いのでさっぱり分からんです。

できないこと、制約

フリック、スワイプの区別

個人的に、タップと同時に画面をなぞることをフリック、画面を掃くような動作をスワイプと呼んでいます*1
このプラグインはFlickSensorとか言っていますが、実は速度の検知は行なっていないのでどんなに遅く(早く)画面をなぞってもフリックとみなされます。

上下のフリック

個人的に必要がなかったのと、上下はスクロールに利用するため上下のフリック検出は実装していません*2

軌跡の検知

フリックの検知は始点と終点でしか行なっていません。そのためどんな奇跡を描いても始点と終点の位置関係でフリックと検知されます。

PC上でのマウスジェスチャ

PCでのマウスジェスチャーの検出はできません。

使い方

プラグインの読み込み

jquery.jsとlandscapeFlickSensorを読み込んで下さい(landscapeFlickSensorのソースは最下部です)*3

<script type="text/javascript" src="/resources/js/jquery.js" ></script>
<script type="text/javascript" src="/resources/js/jquery.landscapeFlickSensor.js"></script>
検知部のhtml

フリックを検知したい部分をタグで囲んで下さい*4

<div id="flickPane">
    <p id="message">ここをフリックする</p>
</div>
プラグインの適用

検知部にプラグインを適用します。その際左右フリック時の動作やオプションを同時に渡します(全て省略可)。

<script type="text/javascript">
<!--
    $(document).ready(function() {
        $('#flickPane').landscapeFlickSensor({
            distanceThreshold : 30,    /* フリックとみなす最低距離 */
            degreeThreshold   : 10.0,  /* フリックとみなす最大角度(度) */
            onRightFlick : function() { /* 右フリック持の動作 */ },
            onLeftFlick  : function() { /* 左フリック時の動作 */ }
        });
    });
//-->
</script>
フリックとみなす最低距離(distanceThreshold)

フリックの始点と終点の距離がこの指定値以上の場合にフリックとみなされます。省略した場合30が設定されます*5

フリックとみなす最大角度(degreeThreshold)

フリックの始点と終点のなす角がこの角度に収まる場合にフリックとみなされます。省略した場合は10度が設定されます。

右フリック持の動作(onRightFlick)

右フリックをしたときに呼び出される関数を記述します。引数はありません。省略した場合は空の関数が実行されます。

左フリック持の動作(onLeftFlick)

左フリックをしたときに呼び出される関数を記述します。引数はありません。省略した場合は空の関数が実行されます。

使用例

左右にフリックすると画面上にメッセージを表示する例を上げます。

画面イメージ

初期表示

右スワイプ時

左スワイプ時

サンプルソース
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8" />

    <!-- iPhoneでの画面サイズの固定 -->
    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no" />

    <!-- jqueryとlandscapeFlickSensorの読み込み -->
    <script type="text/javascript" src="/resources/js/jquery.js" ></script>
    <script type="text/javascript" src="/resources/js/jquery.landscapeFlickSensor.js"></script>

    <script type="text/javascript">
    <!--
        $(document).ready(function() {
            $('#flickPane').css("min-height", $(document).height()); //反応範囲を全範囲にする

            /* フリックした時の動作 */
            $('#flickPane').landscapeFlickSensor({
                onRightFlick : function () {
                    $('#message').text('右フリック');
                },
                onLeftFlick : function() {
                    $('#message').text('左フリック');
                }
            });
        });
    //-->
    </script>
</head>
<body>

    <div id="flickPane">
        <p id="message">ここをフリックする</p>
    </div>

</body>
</html>

ソースコード

/**
 * jquery.landscapeFlickSensor.js
 * iPhoneで左右のフリックを検出するプラグイン。
 *
 * 商用・非商用、改変、報告、ライセンス表示等何も制限しないので好きに使って下さい。
 * もちろん何も責任は取りません。
 *
 * @author tatesuke
 */
(function($){
     $.fn.landscapeFlickSensor=function(config){
         var opts = $.extend({}, $.fn.landscapeFlickSensor.defaults, config);

         return this.each(function(i){
            var startX = -1;
            var startY = -1;

            $(this).bind('touchstart', function() {
                startX = event.changedTouches[0].pageX;
                startY = event.changedTouches[0].pageY;
            });

            $(this).bind('touchend', function() {
                var endX = event.changedTouches[0].pageX;
                var endY = event.changedTouches[0].pageY;

                var dx = endX - startX;
                var dy = endY - startY;
                var distance = Math.sqrt((dx * dx) + (dy * dy));
                var degree = Math.abs(toDegree(Math.atan2(dy, dx)));

                if (opts.distanceThreshold <= distance) {
                    if (isRightFlick(degree)) {
                        opts.onRightFlick();
                    } else if (isLeftFlick(degree)) {
                        opts.onLeftFlick();
                    }
                }

            });

            function toDegree(radian) {
                return 180.0 * radian / Math.PI;
            }

            function isRightFlick(degree) {
                return (0.0 <= degree) && (degree <= opts.degreeThreshold);
            }

            function isLeftFlick(degree) {
                return ((180.0 - opts.degreeThreshold) <= degree) && (degree <= 180);
            }

         });
     };

     $.fn.landscapeFlickSensor.defaults = {
             distanceThreshold : 30,    /* フリックとみなす最低距離(多分px) */
             degreeThreshold   : 10.0,  /* フリックとみなす最大角度(度) */
             onRightFlick : function(){ /* 右フリック時の処理はこれを上書く */ },
             onLeftFlick : function() { /* 左フリック時の処理これを上書く */ },
     };

})(jQuery);

*1:正式な名称は知りません

*2:名前からしても水平方向だけですし。

*3:パスやファイル名は適宜読み替えて下さい

*4:divでもformでもなんでもいいです

*5:単位は多分pxじゃないでしょうか。ごめんなさい調べてません