《每週一點canvas動畫》——使用者互動

我仍舊在這裡發表於2019-02-16

使用者互動也許是我們學習canvas動畫中首先需要掌握的部分。畢竟,如果沒有互動或者向動畫中做一些動態的輸入,那麼這跟看電影有什麼區別呢?使用者互動基於事件,一般來說包括:滑鼠事件觸控事件鍵盤事件

1、事件和事件執行

在理解事件之前,你需要明白什麼是listenerhandler
listener(即監聽器)決定當一個事件發生時是否做出反應。handler(即執行者)是一個函式,當事件發生時被呼叫。好了,扯了這麼多直接上程式碼:

    element.addEventListener(type, handler[, useCapture]);

    type: 事件型別
    handler: 事件執行函式
    useCapture: 可選,為布林值false/true, 表示在冒泡/捕獲階段執行

通過方法addEventListener來為某一元素新增事件,具體到我們的canvas上是什麼樣的呢?加入我們現在想要在canvas上繫結一個mousedown事件,具體程式碼如下:

    canvas.addEventListener(`mousedown`, function(event){
        console.log("Mouse pressed on element");
    }, false)

這樣我們就為canvas繫結了滑鼠點選事件,當在canvas上按下滑鼠是就會在控制檯看到列印出 “Mouse pressed on element”。

那麼既然有新增事件(addEventListener),就有移除事件(removeEventListener),使用方式與新增事件幾乎完全一樣:

    element.removeEventListener(type, handler[, useCapture]);
    type: 事件型別
    handler: 事件執行函式
    useCapture: 可選,為布林值false/true, 表示在冒泡/捕獲階段執行

唯一需要注意的是handler,即移除事件的函式,這裡只能寫函式名,而不能像新增事件一樣將整個功能函式全部寫入。也就是說,在新增某個事件的時候,我們可以將需要執行的函式寫在事件監聽之外並命名,這樣如果你想要在後續的程式碼中移除該事件,直接將函式名傳入移除事件的handler中即可。

現在讓我們來實驗下先為canvas新增一個事件,再將其移除

<body>
  <canvas id="canvas" width="500" height="500"></canvas>
  <script></script>
   <script>
       window.onload = function(){
           var canvas = document.getElementById(`canvas`);
           
           //定義的執行函式add
           function add(event){
               console.log("mouse down");
           }
           canvas.addEventListener(`mousedown`, add, false);
           
           //移除事件mousedown
           canvas.removeEventListener(`mousedown`, add, false)
       }
   </script>
</body>

現在你可以看看控制檯是否還能列印出“mouse down”!

2.滑鼠事件

滑鼠事件一共可以分為:

  • mousedown

  • mouseup

  • click

  • dbclick

  • mousewheel

  • mouseover

  • mouseout

每一個滑鼠事件都包含兩個屬性來決定當前滑鼠的位置:pageXpageY。通過pageXpageY,還有canvas元素的偏移位置,我們就能夠計算出滑鼠具體是在canvas元素的什麼位置。為了考慮不同瀏覽器的相容性,以防萬一你可以使用clientXclientY。在這裡,我們建立一個js檔案,名為**utils.js**,這個檔案是我們的一個工具函式,裡面會逐漸加入一些我們重複使用的方法,那麼現在我們向我們的工具函式中新增第一個方法captureMouse,具體程式碼如下:

utils.js檔案

    //將utils定義為window物件下的一個屬性,屬性值為物件
    window.utils = {};

    //在utils物件上定義捕獲座標的方法
    window.utils.captureMouse = function(element){
            //定義一個名為mouse的物件
            var mouse = {x:0,y:0};
            
            //為元素繫結mousemove事件
            element.addEventListener(`mousemove`,function(event){
                var x,y;
                
                //獲取滑鼠位於當前螢幕的位置, 並作相容處理
                if(event.pageX||event.pageY){
                    x = event.pageX;
                    y = event.pageY;
                }else{
                    x = event.clientX + document.body.scrollLeft +document.documentElement.scrollLeft;
                    y = event.clientY + document.body.scrollTop +document.documentElement.scrollTop;
                }
                //將當前的座標值減去元素的偏移位置,即為滑鼠位於當前canvas的位置
                x -= element.offsetLeft;
                y -= element.offsetTop;

                mouse.x = x;
                mouse.y = y;
            },false);
             //返回值為mouse物件
             return mouse;
        }

這個方法將DOM元素作為引數傳入,這樣我們只要將canvas傳入就可以獲取到滑鼠在當前canvas的位置。具體程式碼如下:

    <canvas id="canvas" width=`500` height="500" style="background:#000">
           <p>you browser not support canvas!<p>
       </canvas>
       <script src=`../js/utils.js`></script>
       <script>
           window.onload = function(){
              var canvas = document.getElementById(`canvas`),
                  //將canvas傳入,該方法會返回一個包含屬性x和y的物件
                  mouse = utils.captureMouse(canvas);

              //為canvas繫結mousedown事件,當滑鼠按下的時候列印出當前滑鼠相對於canvas的座標值
              canvas.addEventListener(`mousedown`,function(event){
                console.log("x:" +mouse.x +",y:" + mouse.y);
              });
       </script>

Have a try!!!看看能否成功。

getBoundingClientRect()

其實,關於canvas的滑鼠位置獲取的方法還可以應用它自身的一個方法getBoundingClientRect,這裡做一個介紹,你可以使用,但本系列文章主要使用上面這種更具廣泛性的方法。具體程式碼可以參考如下:

        canvas.addEventListener(`mousedown`,function(event){
                       //event相容處理
                       var event = event || window.event;
                       //相容處理,獲取當前滑鼠相對螢幕的座標
                       var winX = event.clientX+document.body.scrollLeft +document.documentElement.scrollLeft || event.pageX;
                       var winY = event.clientY+document.body.scrollTop +document.documentElement.scrollTop || event.pageY;
                       
                       //定義一個物件
                       var can = {x:0, y:0};
                       //呼叫getBoundingClientRect方法,該方法返回一個物件,包含canvas的left、 top、 width、 height等值
                       
                       var canBox = canvas.getBoundingClientRect();
                       
                   //(winX - canBox.left):與上面的含義一樣,是減去canvas的偏移量
                   //(canvas.width/canBox.width):一般來說canvas.width和canBox.width是一樣的,也就是說這兩個的比值為1.但不排除你會為canvas設定邊框,這是實際的座標位置就會有所變化,比如canvas.width = 500, 你可能設定了一個1px的邊框,那麼canBox.width = 502, 所以比值就不為1了。這樣做只是讓資料更精確。
                   
                       can.x = (winX - canBox.left)*(canvas.width/canBox.width);
                       can.y = (winY - canBox.top)*(canvas.height/canBox.height);
                       
                       //輸出
                       console.log(can.x,can.y);
                   },false);

3、鍵盤事件

鍵盤事件就兩個:

  • keydown

  • keyup

我們同樣可以向繫結滑鼠事件那樣為canvas繫結鍵盤事件。好吧!現在我們來看看,如何將一個鍵盤事件繫結到window(為什麼不直接繫結到canvas上呢?想想)上:

<body >
    <p>任意按下按鍵</p>
   <script>
       window.onload = function(){
       
              //定義鍵盤事件
           function onKeyboard(event){
              switch (event.keyCode){
                  case 38:
                      console.log(`up!`);
                      break;
                  case 40:
                      console.log(`down!`);
                      break;
                  case 37:
                      console.log(`left!`);
                      break;
                  case 39:
                      console.log(`right!`);
                      break;
                  default:
                      console.log(event.keyCode);
           }
        }
        //為window物件繫結鍵盤事件
        window.addEventListener(`keydown`,onKeyboard,false);
       }
    </script>
</body>

試一試,當按下滑鼠的方向鍵是是否在控制檯列印出了相應的資訊!

4、觸控事件

觸控事件包括以下3種:

  • touchstart

  • touchend

  • touchmove

觸控實踐中,手指就充當了滑鼠的作用。同樣我們最為關心的是當前觸控的位置。和captureMouse方法一樣,這裡在我們的工具函式檔案中,需要新增一新的方法來捕獲觸控的位置,名為captureTouch,現在在utils.js檔案中新增如下方法:

utils.js檔案

    window.utils.captureTouch = function (element) {
      var touch = {
                      x: null,
                    y: null,
                    isPressed: false,
                    event: null
                    };
      var body_scrollLeft = document.body.scrollLeft,
          element_scrollLeft = document.documentElement.scrollLeft,
          body_scrollTop = document.body.scrollTop,
          element_scrollTop = document.documentElement.scrollTop,
          offsetLeft = element.offsetLeft,
          offsetTop = element.offsetTop;
          
     // 繫結touchstart事件
      element.addEventListener(`touchstart`, function (event) {
        touch.isPressed = true;
        touch.event = event;
      }, false);
      
     // 繫結touchend事件
      element.addEventListener(`touchend`, function (event) {
        touch.isPressed = false;
        touch.x = null;
        touch.y = null;
        touch.event = event;
      }, false);
      
     //繫結touchmove事件
      element.addEventListener(`touchmove`, function (event) {
        var x, y,
            touch_event = event.touches[0]; //第一次touch

        if (touch_event.pageX || touch_event.pageY) {
          x = touch_event.pageX;
          y = touch_event.pageY;
        } else {
          x = touch_event.clientX + body_scrollLeft + element_scrollLeft;
          y = touch_event.clientY + body_scrollTop + element_scrollTop;
        }
        //剪去偏移量
        x -= offsetLeft;
        y -= offsetTop;

        touch.x = x;
        touch.y = y;
        touch.event = event;
      }, false);
      //返回touch物件
      return touch;
    };

總結

這一節主要介紹使用者與canvas互動的各種事件,重要的是你應該在你自己的工具函式檔案中包含了以下兩個方法:utils.captureTouchutils.captureMouse這兩個方法都是為了獲取當前相對於canvas元素的位置。我們將在後面的章節中頻繁使用。當然,除了這兩個方法,由於我們使用的requestAnimationFrame方法同樣也涉及到相容性的問題,我們將它一同新增到utils.js中,具體程式碼請檢視utils.js檔案。
下一節,三角函式座標旋轉敬請期待!!!

相關文章