WebAPIs-06:動畫函式封裝 + 觸屏事件

落花流雨發表於2020-10-19

1.1. 動畫函式封裝

1.1.1 緩動效果原理

緩動動畫就是讓元素運動速度有所變化,最常見的是讓速度慢慢停下來

思路:

  1. 讓盒子每次移動的距離慢慢變小,速度就會慢慢落下來。
  2. 核心演算法: (目標值 - 現在的位置) / 10 做為每次移動的距離步長
  3. 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
  4. 注意步長值需要取整
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    div {
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }

    span {
      position: absolute;
      left: 0;
      top: 200px;
      display: block;
      width: 150px;
      height: 150px;
      background-color: purple;
    }
  </style>
</head>

<body>
  <button>點選夏雨荷才走</button>
  <span>夏雨荷</span>
  <script>
    /*
    勻速動畫 就是 盒子是當前的位置 +  固定的值 10
    緩動動畫就是  盒子當前的位置 + 變化的值(目標值 - 現在的位置) / 10)
    
      緩動動畫函式封裝obj目標物件 target 目標位置
      思路:
        1. 讓盒子每次移動的距離慢慢變小, 速度就會慢慢落下來。
        2. 核心演算法:(目標值 - 現在的位置) / 10 做為每次移動的距離 步長
        3. 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
    */
    function animate(obj, target) {
      // 先清除以前的定時器,只保留當前的一個定時器執行
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        // 步長值寫到定時器的裡面
        var step = (target - obj.offsetLeft) / 10;
        if (obj.offsetLeft == target) {
          // 停止動畫 本質是停止定時器
          clearInterval(obj.timer);
        }
        // 把每次加1 這個步長值改為一個慢慢變小的值  步長公式:(目標值 - 現在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

      }, 15);
    }
    var span = document.querySelector('span');
    var btn = document.querySelector('button');

    btn.addEventListener('click', function () {
      // 呼叫函式
      animate(span, 500);
    })
  </script>
</body>

</html>

1.1.2 動畫函式多個目標值之間移動

可以讓動畫函式從 800 移動到 500。

當我們點選按鈕時候,判斷步長是正值還是負值

​ 1.如果是正值,則步長往大了取整

​ 2.如果是負值,則步長 向小了取整

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    div {
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }

    span {
      position: absolute;
      left: 0;
      top: 200px;
      display: block;
      width: 150px;
      height: 150px;
      background-color: purple;
    }
  </style>
</head>

<body>
  <button class="btn500">點選夏雨荷到500</button>
  <button class="btn800">點選夏雨荷到800</button>
  <span>夏雨荷</span>
  <script>
    /*
    勻速動畫 就是 盒子是當前的位置 +  固定的值 10
    緩動動畫就是  盒子當前的位置 + 變化的值(目標值 - 現在的位置) / 10)
    
      緩動動畫函式封裝obj目標物件 target 目標位置
      思路:
        1. 讓盒子每次移動的距離慢慢變小, 速度就會慢慢落下來
        2. 核心演算法:(目標值 - 現在的位置) / 10 做為每次移動的距離 步長
        3. 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
    */
    function animate(obj, target) {
      // 先清除以前的定時器,只保留當前的一個定時器執行
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        /*
          步長值寫到定時器的裡面
          把我們步長值改為整數 不要出現小數的問題
           var step = Math.ceil((target - obj.offsetLeft) / 10);
        */
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          // 停止動畫 本質是停止定時器
          clearInterval(obj.timer);
        }
        // 把每次加1 這個步長值改為一個慢慢變小的值  步長公式:(目標值 - 現在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
      }, 15);
    }
    var span = document.querySelector('span');
    var btn500 = document.querySelector('.btn500');
    var btn800 = document.querySelector('.btn800');

    btn500.addEventListener('click', function () {
      // 呼叫函式
      animate(span, 500);
    })
    btn800.addEventListener('click', function () {
      // 呼叫函式
      animate(span, 800);
    })
  </script>
</body>

</html>

1.1.3 動函式新增回撥函式

回撥函式原理:函式可以作為一個引數。將這個函式作為引數傳到另一個函式裡面,當那個函式執行完之後,再執行傳進去的這個函式,這個過程就叫做回撥。

回撥函式寫的位置:定時器結束的位置。

1.1.4 動畫完整版程式碼:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    div {
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }

    span {
      position: absolute;
      left: 0;
      top: 200px;
      display: block;
      width: 150px;
      height: 150px;
      background-color: purple;
    }
  </style>
</head>

<body>
  <button class="btn500">點選夏雨荷到500</button>
  <button class="btn800">點選夏雨荷到800</button>
  <span>夏雨荷</span>
  <script>
    /*
      緩動動畫函式封裝obj目標物件 target 目標位置
      思路:
        1. 讓盒子每次移動的距離慢慢變小, 速度就會慢慢落下來
        2. 核心演算法:(目標值 - 現在的位置) / 10 做為每次移動的距離 步長
        3. 停止的條件是: 讓當前盒子位置等於目標位置就停止定時器
    */
    function animate(obj, target, callback) {
      // console.log(callback);  callback = function() {}  呼叫的時候 callback()
      // 先清除以前的定時器,只保留當前的一個定時器執行
      clearInterval(obj.timer);
      obj.timer = setInterval(function () {
        /*
          步長值寫到定時器的裡面,把我們步長值改為整數 不要出現小數的問題
          var step = Math.ceil((target - obj.offsetLeft) / 10);
        */
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
          // 停止動畫 本質是停止定時器
          clearInterval(obj.timer);
          // 回撥函式寫到定時器結束裡面
          if (callback) {
            // 呼叫函式
            callback();
          }
        }
        // 把每次加1 這個步長值改為一個慢慢變小的值  步長公式:(目標值 - 現在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';
      }, 15);
    }
    var span = document.querySelector('span');
    var btn500 = document.querySelector('.btn500');
    var btn800 = document.querySelector('.btn800');

    btn500.addEventListener('click', function () {
      // 呼叫函式
      animate(span, 500);
    })
    btn800.addEventListener('click', function () {
      // 呼叫函式
      animate(span, 800, function () {
        span.style.backgroundColor = 'red';
      });
    })
  </script>
</body>

</html>

1.2. 常見網頁特效案例

1.2.2. 節流閥

防止輪播圖按鈕連續點選造成播放過快。

節流閥目的:當上一個函式動畫內容執行完畢,再去執行下一個函式動畫,讓事件無法連續觸發。

核心實現思路:利用回撥函式,新增一個變數來控制,鎖住函式和解鎖函式。

開始設定一個變數var flag= true;

If(flag){flag = false; do something} 關閉水龍頭

利用回撥函式動畫執行完畢, flag = true 開啟水龍頭

1.2.3. 案例:返回頂部

  1. 帶有動畫的返回頂部
  2. 此時可以繼續使用我們封裝的動畫函式
  3. 只需要把所有的left 相關的值改為 跟 頁面垂直滾動距離相關就可以了
  4. 頁面滾動了多少,可以通過 window.pageYOffset 得到
  5. 最後是頁面滾動,使用 window.scroll(x,y)
  //1. 獲取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        // banner.offestTop 就是被捲去頭部的大小 一定要寫到滾動的外面
        var bannerTop = banner.offsetTop
            // 當我們側邊欄固定定位之後應該變化的數值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 獲取main 主體元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 頁面滾動事件 scroll
        document.addEventListener('scroll', function() {
                // console.log(11);
                // window.pageYOffset 頁面被捲去的頭部
                // console.log(window.pageYOffset);
                // 3 .當我們頁面被捲去的頭部大於等於了 172 此時 側邊欄就要改為固定定位
                if (window.pageYOffset >= bannerTop) {
                    sliderbar.style.position = 'fixed';
                    sliderbar.style.top = sliderbarTop + 'px';
                } else {
                    sliderbar.style.position = 'absolute';
                    sliderbar.style.top = '300px';
                }
                // 4. 當我們頁面滾動到main盒子,就顯示 goback模組
                if (window.pageYOffset >= mainTop) {
                    goBack.style.display = 'block';
                } else {
                    goBack.style.display = 'none';
                }

            })
            // 3. 當我們點選了返回頂部模組,就讓視窗滾動的頁面的最上方
        goBack.addEventListener('click', function() {
            // 裡面的x和y 不跟單位的 直接寫數字即可
            // window.scroll(0, 0);
            // 因為是視窗滾動 所以物件是window
            animate(window, 0);
        });

1.2.4. 案例:筋頭雲案例

  1. 利用動畫函式做動畫效果
  2. 原先筋斗雲的起始位置是0
  3. 滑鼠經過某個小li,把當前小li的offsetLeft 位置做為目標值即可
  4. 滑鼠離開某個小li,就把目標值設為 0
  5. 如果點選了某個小li, 就把li當前的位置儲存起來,做為筋斗雲的起始位置
 window.addEventListener('load', function() {
            // 1. 獲取元素
            var cloud = document.querySelector('.cloud');
            var c_nav = document.querySelector('.c-nav');
            var lis = c_nav.querySelectorAll('li');
            // 2. 給所有的小li繫結事件 
            // 這個current 做為筋斗雲的起始位置
            var current = 0;
            for (var i = 0; i < lis.length; i++) {
                // (1) 滑鼠經過把當前小li 的位置做為目標值
                lis[i].addEventListener('mouseenter', function() {
                    animate(cloud, this.offsetLeft);
                });
                // (2) 滑鼠離開就回到起始的位置 
                lis[i].addEventListener('mouseleave', function() {
                    animate(cloud, current);
                });
                // (3) 當我們滑鼠點選,就把當前位置做為目標值
                lis[i].addEventListener('click', function() {
                    current = this.offsetLeft;
                });
            }
        })

1.3. 觸屏事件

1.3.1. 觸屏事件概述

移動端瀏覽器相容性較好,我們不需要考慮以前 JS 的相容性問題,可以放心的使用原生 JS 書寫效果,但是移動端也有自己獨特的地方。比如觸屏事件 touch(也稱觸控事件),Android和 IOS 都有。

touch 物件代表一個觸控點。觸控點可能是一根手指,也可能是一根觸控筆。觸屏事件可響應使用者手指(或觸控筆)對螢幕或者觸控板操作。

1.3.2. 觸控事件物件(TouchEvent)

TouchEvent 是一類描述手指在觸控平面(觸控式螢幕、觸控板等)的狀態變化的事件。這類事件用於描述一個或多個觸點,使開發者可以檢測觸點的移動,觸點的增加和減少,等等

touchstart、touchmove、touchend 三個事件都會各自有事件物件。

觸控事件物件重點我們看三個常見物件列表:
在這裡插入圖片描述
因為平時我們都是給元素註冊觸控事件,所以重點記住 targetTocuhes

1.3.3.案例:移動端拖動元素

  1. touchstart、touchmove、touchend可以實現拖動元素

  2. 但是拖動元素需要當前手指的座標值 我們可以使用 targetTouches[0] 裡面的pageX 和 pageY

  3. 移動端拖動的原理: 手指移動中,計算出手指移動的距離。然後用盒子原來的位置 + 手指移動的距離

  4. 手指移動的距離: 手指滑動中的位置 減去 手指剛開始觸控的位置

    拖動元素三步曲:

    (1) 觸控元素 touchstart: 獲取手指初始座標,同時獲得盒子原來的位置

    (2) 移動手指 touchmove: 計算手指的滑動距離,並且移動盒子

    (3) 離開手指 touchend:

    注意: 手指移動也會觸發滾動螢幕所以這裡要阻止預設的螢幕滾動 e.preventDefault();

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    div {
      position: absolute;
      left: 0;
      width: 100px;
      height: 100px;
      background-color: pink;
    }
  </style>
</head>

<body>
  <div></div>
  <script>
    /*
      (1) 觸控元素 touchstart:  獲取手指初始座標,同時獲得盒子原來的位置
      (2) 移動手指 touchmove:  計算手指的滑動距離,並且移動盒子
      (3) 離開手指 touchend:
    */
    var div = document.querySelector('div');
    var startX = 0; //獲取手指初始座標
    var startY = 0;
    var x = 0; //獲得盒子原來的位置
    var y = 0;
    div.addEventListener('touchstart', function (e) {
      // 獲取手指初始座標
      startX = e.targetTouches[0].pageX;
      startY = e.targetTouches[0].pageY;
      // 盒子原來的位置
      x = this.offsetLeft;
      y = this.offsetTop;
    });

    div.addEventListener('touchmove', function (e) {
      //  計算手指的移動距離 = 手指移動之後的座標 - 手指初始的座標
      var moveX = e.targetTouches[0].pageX - startX;
      var moveY = e.targetTouches[0].pageY - startY;
      // 盒子移動的距離 =  盒子原來的位置 + 手指移動的距離
      this.style.left = x + moveX + 'px';
      this.style.top = y + moveY + 'px';
      e.preventDefault(); // 阻止螢幕滾動的預設行為
    });
  </script>
</body>

</html>

相關文章