jQueryで座標を取得してみる

どうも!前回からちょっと間が空いてしまいましたが、すっかり花粉の季節ですね(><); 皆さまいかがお過ごしでしょうか?私はマスクとメガネの日々を過ごしております。マスクとメガネ、二つも耳に掛かっていると本当にストレスなんですよね(´・ω・`)…
さて、今回はjQueryで座標を取得してみようの巻です。
マウスカーソル(タッチデバイスでは指)を画面上で滑らせると、その軌跡に沿って★が描かれるというものです。
このような物を作ってみます。↓

zahyo-wp01

目次
1.座標を取得するには〜offsetとclientX、clientY
2.変数を定義する
3.clone関数で要素にappendToする
4.setTimeout関数とは
5.タッチイベントとマウスイベント
6.まとめ
7.ソース

まず簡単なhtmlファイルを用意しました。
★が入った、元になるdiv id=”box”を用意し、★を描くキャンバスとなる、div id=”box2″と、座標が取得できているか確認用に座標の位置を表示するためのdiv id=”count”(中にXとYそれぞれの座標をカウントするためのdivが二つ)という構成です。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>zahyo</title>
<link rel="stylesheet" href="zahyo-wp.css" type="text/css">
</head>
 <body>
  <h1>画面上をなぞってみよう</h1>
  <div id="box">★</div>
  <div id="box2"></div>
  <div id="count">
    <div class="zahyou">
     <h3>x座標</h3>
     <p id="x-pos"></p>
    </div>
    <div class="zahyou">
     <h3>y座標</h3>
     <p id="y-pos"></p>
    </div>
  </div>
 </body>
</html>

1.座標を取得するには〜offsetとclientX、clientY

座標を取得する、とざっくり言ってもどこが基準のどの部分の?と最初は悩んでしまうと思います。座標は取得したい場所によってプロパティも使い分けなくてはなりません。
今回の座標の取得に使うプロパティはふたつ、
要素の座標を取得するにはoffsetというプロパティを使います(ここでの要素とは★を出すキャンバスであるdiv id=”box2″)。
ブラウザのウィンドウ内での座標を取得するにはclientX、clientYというプロパティを使います。

座標の位置と取得方法についてはこちらを参考にさせて頂きました!図解でとてもわかりやすく説明されております。
イベントが発生した時のマウス情報を取得するには?
jQueryでのサイズ、座標の取得方法まとめ

2.変数を定義する

それでは、スクリプトを書いていきましょう。
まず、bodyの閉じタグの直前に下記のjqueryを読み込むscriptタグ↓を書き込んでから、

<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>

その下にもう一つscriptタグを作り、その中にスクリプトを書いていきます。

$(function(){
      var box = $("#box2");
      var boxOffset = box.offset();
      var minX = Math.floor(boxOffset.left);
      var minY = Math.floor(boxOffset.top);
      var maxX = Math.floor(boxOffset.left + box.width());
      var maxY = Math.floor(boxOffset.top + box.height());
      var x = 0;//初期値
      var y = 0;
      var trace = 0;

最初に変数を定義します。
ここで何をやっているのかというと、id=”box2″の座標をoffsetで取得し、var minX以下var maxYまでのところでid=”box2″のキャンバスの範囲を取得します。

3.clone関数で要素にappendToする

いま、★はdiv id=”box”の中に一つだけあります。この★を、マウスカーソル(指)でなぞった位置に次々と出すためにdiv id=”box”をクローンで増やしてあげましょう。

traceFunc = function(){
     if(trace){//trace = 1;の状態。初期値は0
      $("#x-pos").text(x);//#x-posにxの値を書き出す
      $("#y-pos").text(y);//#y-posにxの値を書き出す
     if(x > minX && x < maxX && y > minY && y < maxY){//#box2の枠の範囲のとき
      $("#box").clone().css(//#boxをクローンしてcssを書き換え、bodyに追加する
        {'left': x + 'px','top': y + 'px','color': 'orange'}
        ).appendTo("body");
     }
     setTimeout(traceFunc,100);//traceFunc関数を0.1秒ごとに繰り返す
     }
    }

ここで、traceFuncという関数を作り、座標の位置を数値で表示するための#x-pos、#y-posに.text関数でx、yの値を書き出す命令を出しています。
また、if文の中で最初に定義したvar minXからvar maxYを使って、もしxとyの値が#box2の枠の範囲にある場合、#boxをクローンしてcssを{}内のように書き換え、bodyに追加するように、としています。
つまり、#boxの★は#box2の枠の範囲内であればcloneされるということになります。

4.setTimeout関数とは

先ほど作ったtraceFuncという関数の中にsetTimeout関数がしれっと書かれておりますが、こいつはなんぞや?といいますと、繰り返し(タイマー)処理の関数であります。
処理が終了してから一定時間後に処理を繰り返すというもので、似たような関数でsetIntervalというものもありますが、こちらは処理の開始時点から一定時間後に処理を繰り返すという違いがあります。

  • setTimeout 処理の終了時点から一定時間後に処理を繰り返す
  • setInterval 処理の開始時点から一定時間後に処理を繰り返す

ここでは関数traceFuncの処理を、100(0.1秒)ごとに繰り返す、という風に指定しました。(これを指定しないとマウス(指)が動いている限り、常に膨大な量の値を取得しなければならず、負荷がかかってしまいます ><;)
タイマー処理についてはこちらに詳しいですJavaScriptでsetTimeoutを使う方法

5.タッチイベントとマウスイベント

最近のネットの最新動向では「モバイルファースト」どころか、「モバイルオンリー」になりつつあるとのことですから、マウスとタッチと両方のアクションを考慮することは必須となりました。そこでタッチイベントとマウスイベントをif文で分けてイベント発生時の処理を書いていきます。


      //タッチが始まった時、指が離れた時、指を動かし動かしている時の3パターンの動き
      if('ontouchstart' in document){//タッチイベントが有効な場合
        $("html").on('touchstart',function(){
          event.preventDefault();//画面が動かないようにする
          trace = 1;
          traceFunc();//関数を実行
        });
        $("html").on('touchend',function(){
          trace = 0;
        });
        $("html").on('touchmove',function(){
          x = event.changedTouches[0].clientX;//イベントが発生したタッチされているウィンドウ上の座標
          y = event.changedTouches[0].clientY;//ちなみに[0]を[1]にするとマルチタッチの2本目の指の位置を取得できる
        });
       }
      else {//タッチイベントが有効でない場合(PC)の3パターンの動き
        $("html").on('mouseenter',function(){
          trace = 1;
          traceFunc();
        });
        $("html").on('mouseleave',function(){
          trace = 0;
        });
        $("html").on('mousemove',function(event){
          x = event.clientX;//イベントが発生したウィンドウ上の座標
          y = event.clientY;
        });
       };

ここでのポイントは、タッチデバイスでは指を滑らせると画面が動いてしまうため、それを防止するためにevent.preventDefault();という記述を足して画面が動かないようにしています。また、タッチデバイスではタッチする指の本数も考慮するため、座標の取得もマウスの時の記述よりひとつ増えてchangedTouches[0]([0]は1本の指)を加えます。

あとは、それぞれの動きを下記のように対応させました。

  • touchstart(タッチが開始したら) ⇔ mouseenter(マウスが要素に乗ったら)
  • touchend(タッチが離れたら) ⇔ mouseleave(マウスが離れたら)
  • touchmove(タッチしたまま動かしている時) ⇔ mousemove(マウスを動かしている時)

この他にも、タッチ・マウスイベントはありますので、皆さんもいろいろ試してみてくださいね。
こちらではタッチベントとマウスイベントがわかりやすくまとめられていてとても参考になります。iPhone/Android/PC 対応。jQuery で書くタッチイベント

6.まとめ

完成〜!
(★が出ない時は一度リロードしてみてください。)

今回はどこを基準として、どのプロパティを使って座標を取得すれば良いのか?でかなり悩みました。まだまだ完全に理解していないので説明も未熟なところが多々ございますが、一応、タッチデバイスとPCと両方対応出来たので良しとしますか(笑)
コードももっとサクサクと効率の良い書き方が出来るようになるといいなと願いつつ、次回は今日少し触れたsetTimeout関数を使って何か作ってみようと思います。

それでは皆さん、ごきげんよう!(^^)/~

7.ソース

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>zahyo</title>
<link rel="stylesheet" href="zahyo-wp.css" type="text/css">
</head>
 <body>
  <h1>画面上をなぞってみよう</h1>
  <div id="box">★</div>
  <div id="box2"></div>
  <div id="count">
    <div class="zahyou">
     <h3>x座標</h3>
     <p id="x-pos"></p>
    </div>
    <div class="zahyou">
     <h3>y座標</h3>
     <p id="y-pos"></p>
    </div>
  </div>
   <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
   <script>
   $(function(){
      var box = $("#box2");
      var boxOffset = box.offset();
      var minX = Math.floor(boxOffset.left);
      var minY = Math.floor(boxOffset.top);
      var maxX = Math.floor(boxOffset.left + box.width());
      var maxY = Math.floor(boxOffset.top + box.height());
      var x = 0;//初期値
      var y = 0;
      var trace = 0;
     //タッチが始まった時、指が離れた時、指を動かし動かしている時の3パターンの動き
      if('ontouchstart' in document){//タッチイベントが有効な場合
        $("html").on('touchstart',function(){
          event.preventDefault();//画面が動かないようにする
          trace = 1;
          traceFunc();//後述の関数を実行
        });
        $("html").on('touchend',function(){
          trace = 0;
        });
        $("html").on('touchmove',function(){
          x = event.changedTouches[0].clientX;//イベントが発生したタッチされているウィンドウ上の座標
          y = event.changedTouches[0].clientY;//ちなみに[0]を[1]にするとマルチタッチの2本目の指の位置を取得できる
        });
       }
      else {//タッチイベントが有効でない場合(PC)の3パターンの動き
        $("html").on('mouseenter',function(){
          trace = 1;
          traceFunc();
        });
        $("html").on('mouseleave',function(){
          trace = 0;
        });
        $("html").on('mousemove',function(event){
          x = event.clientX;//イベントが発生したウィンドウ上の座標
          y = event.clientY;
        });
       };


   traceFunc = function(){
     if(trace){//trace = 1;の状態
      $("#x-pos").text(x);//#x-posにxの値を挿入する
      $("#y-pos").text(y);
     if(x > minX && x < maxX && y > minY && y < maxY){//#box2の枠の範囲のとき
      $("#box").clone().css(//#boxをクローンしてcssを書き換え、bodyに追加する
        {'left': x + 'px','top': y + 'px','color': 'orange'}
        ).appendTo("body");
     }
     setTimeout(traceFunc,100);//traceFunc関数を0.1秒ごとに繰り返す
     }
    }
   });

   </script>
 </body>
</html>

  body{
    color:#444;
    background:  #F0FFFF;
    width:100vw;
    margin:0 auto;
    font-family: "Hiragino Kaku Gothic ProN","メイリオ", sans-serif;
  }
  h1{
    font-size:2.5em;
    margin:1em auto;
    text-align:center;
  }
  #box {
    position: absolute;
    color: transparent;
    margin: -0.5em 0 0 -0.5em;
    font-size:3em;
  }
  #box2{
    width:100vw;
    height:60vh;
    overflow: hidden;
    position:relative;
    background: #00679F;
    cursor: pointer;
    box-shadow: 0 0.1em 0.1em rgba(0,0,0, 0.3);
  }
  #box2 #box{
    color:#FDD800;
  }
  #count{
    text-align:center;
  }
  p {
    background: #fff;
    width:11em;
    height: 2em;
    padding:2em;
    text-align: center;
    border-radius:0.3em;
    box-shadow: 0 0.1em 0.1em rgba(0,0,0, 0.3);
  }
  .zahyou{
    display:inline-block;
    margin:0.8em;
  }
  h3{
    font-size:1.5em;
    text-align:center;
  }

コメントを残す