電子スタンプの接触面をJavaScriptで作る


#電子スタンプとは

電子スタンプとは、まるで本物の判子のようにスマホの上に押すとスタンプの跡がつく技術です。
スタンプの位置や角度に関係なく押すと跡がつき、現在はスタンプラリーや入場スタンプとして活躍しています。
会社に電子スタンプがあったので、どの様にスタンプを判別しているのか想像し、スタンプを押す画面を作ってみました。
（あくまで推測で作成したものであり、実物とは違います。）
裏面を見ると接触点が４点存在します。
スタンプを押した時、この４点の接触をJavaScriptのタッチイベントで座標を取り、反応させたいと思います。

<img width="" alt="スタンプ裏.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691821/e675fa51-95f4-66d2-0406-72c06df2c949.png">


#サンプルコード
```html:sample.html
<html lang="ja">
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8">
        <meta name="viewport" content="width=340">
        <title>Sample</title>
        <link rel="stylesheet" href="css/sample.css" type="text/css" media="all">
        <script type="text/javascript" src="js/sample.js"></script>
    </head>
    <body>
        <h1>スタンプ判定機</h1>
        <div id="block">
            ここのブロック上でスタンプを押してください。
        <div id="img"></div>
        </div>
        <div id="result">ここに接触箇所の数が出ます。</div>
        <div id="disp">ここに座標が出ます。</div>
        <div id="length">ここに座標を繋いだ線の長さが出ます。</div>
        <div id="area">ここに座標を繋いだ面積が出ます。</div>
    </body>
</html>
```

```css:sample.css

h1 {
    color : #505055;
    border-bottom : 1px dotted #ffafaf;
    font-size : 18pt;
    width : 400px;
}
#block {
    border:1px solid black;
    background-color : #ffe;
    width : 320px;
    height : 420px;
}
#img {
    margin : 60px auto;
    text-align : center;
}
```

```javascript:sample.js
window.onload = function(){
    document.getElementById("block").ontouchstart = function(){
        var n = event.touches.length;
        document.getElementById("result").innerHTML = n+"つの接触点";
        var s = "";             // 変数sを初期化
        var p = "";
        var array_x = [];
        var array_y = [];
        // 引数のtouchesプロパティは配列の要素数（触れている指の数）だけ繰り返し処理
        for (var i = 0; i < event.touches.length; i++) {
            var t = event.touches[i];            // 触れている指に関する情報を取得
            array_x.push(t.clientX);
            array_y.push(t.clientY);
            s += "[" + i + "]";
            s += "x=" + t.clientX + ",";
            s += "y=" + t.clientY + "<br>";
        }
        document.getElementById("disp").innerHTML = s;  // 生成した文字列を画面に表示
        if (event.touches.length == 4){
            length_01 = Math.sqrt( Math.pow( array_x[0] - array_x[1], 2 ) + Math.pow( array_y[0] - array_y[1], 2 ) ) ; //接触点と接触点の長さをだす 
            length_02 = Math.sqrt( Math.pow( array_x[0] - array_x[2], 2 ) + Math.pow( array_y[0] - array_y[2], 2 ) ) ;
            length_03 = Math.sqrt( Math.pow( array_x[0] - array_x[3], 2 ) + Math.pow( array_y[0] - array_y[3], 2 ) ) ;
            length_12 = Math.sqrt( Math.pow( array_x[1] - array_x[2], 2 ) + Math.pow( array_y[1] - array_y[2], 2 ) ) ;
            length_13 = Math.sqrt( Math.pow( array_x[1] - array_x[3], 2 ) + Math.pow( array_y[1] - array_y[3], 2 ) ) ;
            length_23 = Math.sqrt( Math.pow( array_x[2] - array_x[3], 2 ) + Math.pow( array_y[2] - array_y[3], 2 ) ) ;

            length_01 = Math.round(length_01);
            length_02 = Math.round(length_02);
            length_03 = Math.round(length_03);
            length_12 = Math.round(length_12);
            length_13 = Math.round(length_13);
            length_23 = Math.round(length_23);
            p += length_01 + ",";
            p += length_02 + ",";
            p += length_03 + ",";
            p += length_12 + ",";
            p += length_13 + ",";
            p += length_23 ;
  
            document.getElementById("length").innerHTML = p;  // 生成した文字列を画面に表示
            function heron(a, b, c){
                var s = (a + b + c) / 2;
                return Math.sqrt(s*(s-a)*(s-b)*(s-c));
            }
            var area_012 = heron(length_01,length_12,length_02);
            var area_013 = heron(length_01,length_13,length_03);
            var area_023 = heron(length_02,length_23,length_03);
            var area_123 = heron(length_12,length_23,length_13);
            var area = (area_012 + area_013 + area_023 + area_123) / 2;

            document.getElementById("area").innerHTML = area;  // 生成した文字列を画面に表示
            if (7000 <= area && 9000 >= area){
                  document.getElementById("img").innerHTML = "<img src='img.jpg'>";
            }else{
                  document.getElementById("img").innerHTML = "<img src='img2.jpg'>";
            }
        }else{
            document.getElementById("length").innerHTML = "";
            document.getElementById("area").innerHTML = "";
            document.getElementById("img").innerHTML = "";
        }
    }
}
```

#タッチイベントについて


タッチイベントは以下のサイトを参考に作成しました。<br>
HTML5とJavaScriptでＰＣ・スマホのマルチタッチ対応してやんよ!!!
[http://tokidoki-web.com/2015/08/html5%E3%81%A8javascript%E3%81%A7%EF%BD%90%EF%BD%83%E3%83%BB%E3%82%B9%E3%83%9E%E3%83%9B%E3%81%AE%E3%83%9E%E3%83%AB%E3%83%81%E3%82%BF%E3%83%83%E3%83%81%E5%AF%BE%E5%BF%9C%E3%81%97%E3%81%A6%E3%82%84/](http://tokidoki-web.com/2015/08/html5%E3%81%A8javascript%E3%81%A7%EF%BD%90%EF%BD%83%E3%83%BB%E3%82%B9%E3%83%9E%E3%83%9B%E3%81%AE%E3%83%9E%E3%83%AB%E3%83%81%E3%82%BF%E3%83%83%E3%83%81%E5%AF%BE%E5%BF%9C%E3%81%97%E3%81%A6%E3%82%84/)<br>
新JavaScript例文辞典 同時にタッチした数を調べる
[http://www.openspc2.org/reibun/javascript2/event/touch/0005/index.html](http://www.openspc2.org/reibun/javascript2/event/touch/0005/index.html)<br><br><br>
以下がタッチイベント部分のコードになります。

```javascript:
 document.getElementById("block").ontouchstart = function(){
```

html内のblockというidをつけたHTML要素を取り出し、スマホでその部分を触れた瞬間に関数で座標を取得します。

```javascript:
 for (var i = 0; i < event.touches.length; i++) {
```

event.touch.lengthで触れている接触点の数を取り出せます。タッチできるのは最大で５つまでです。

```javascript:
 var t = event.touches[i];
 array_x.push(t.clientX);
 s += "x=" + t.clientX + ",";
```

event.touches[0],event.touches[1],…はそれぞれの接触点を表していきます。
event.touches[i].clientXで座標を出せます。今回は計算の為の配列と文字列への追加で2回使っています。



#接触点の判定について

電子スタンプは、４点の位置の違いによってスタンプのパターンを作っています。位置の違いを出す場合、スタンプの押す位置や角度によって座標が変わる問題とevent.touches[i]にどの点が選ばれるかは完全にランダムという問題があります。
その為今回は全ての辺の長さを出し、ヘロンの公式において４点からできる図形の面積を求める方法をとりました。
ヘロンの公式とは3辺の長さより3角形の面積を求める公式です。


<img width="800" alt="ヘロンの公式.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691821/0b06fdca-2861-d6f5-4cdb-5e68e98c2188.png">



四角形を作る4点から3点を選び作れる三角形のパターンは4パターンあり、全てのパターンの合計は四角形の面積の2倍となります。


<img width="750" alt="四角形の面積.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691821/1f303184-7bb1-0ac5-4e5a-ff0941900ecf.png">


以上の方法で今回のスタンプの接触点によってできる4点を、面積によって判別しようとしました。
しかし実際に面積を出してみるとスタンプの接触部分の面積が広く、スタンプを押すごとにそれぞれの接触点で触れたと判定される箇所が変わり、接触した座標の距離とそれによってできる面積の広さが変わりました。
よって判定の際には、面積の広さに幅を持たせています。

#使用例
![スタンプ成功.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691821/02dbc089-265e-392a-f6b4-b770def885cc.png)
![スタンプ失敗.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691821/fb08fd0b-bed4-0fb5-3491-38d43d4eaa7b.png)


#まとめ

今回は自分なりに再現した仕組みで、最終的に面積で判断しています。しかし判定する面積の範囲が広いので何とも言えない結果になりました。もしかしたら線の長さや角度で判定してるのかもしれません。ただ電子スタンプはどこに押しても判定されるはずなので4点の相対的な数値で判定はしてるはず…。個人的には点と点の距離が変わっていたのは驚きですね、接触部分は丸い円型になっているのですが、ちょうど中心で反応するので変わらないと思っていました。