移動端拖拽

最近上線了一個動畫效果偏多的小頁面,因爲上線時間緊張,來不及作優化,可是其中也不乏有一些功能點能夠小小的記錄一下。
要達到的效果以下圖,須要在必定時間內不斷向上拖拽圖中的圓(圓會不斷放大到某個倍數),同時,頁面長背景同步以某個和諧的速率下移,拖拽到頁面上方正方形的底部,作碰撞檢測。
圖片描述css

開發的過程當中遇到一些難點:css3

  • 開發時間短的狀況下選擇什麼方案實現

一開始評估的時候,其實沒往拖拽這個重點想,盡往遊戲框架用canvas畫去靠攏,由於動效不少,也涉及到碰撞檢測等等的。原本想快速的看看遊戲框架,並在腦子裏看是否match這個項目的需求,但最後仍是放棄了。怕遇到短期會使人抓狂的坑。最後仍是決定就用原生的touch事件寫,這樣出了bug也好解決一點。
固然,其實這種決策過程也能暴露不少的問題吧,說明也有很是多的進步空間。
總而言之,無論解決方案是什麼,在時間緊張的狀況下,至少兜底的方案得先出來。canvas

  • touchmove事件的一些莫名問題

向上提拉的過程,涉及到三個部分的同步動效。背景、圓圈、圓圈的放大。效果出來要和諧的話,就須要不斷調試。
解決思路一開始想的很簡單,無非是計算出來每一次move的時候手指在屏幕上滑動的距離。根據這個距離,除以必定的係數比例,嗯,一開始是暈着調的。
可是原生的touchmove卡頓嚴重。致使動效一直不理想。touchmove事件彷佛不是一直觸發的。
查到其餘人說緣由是由於瀏覽器

200ms超時致使內核不必定會一直處理touchmove事件,一旦超時會將後續全部的事件轉交給UI處理,致使touchmove不會一直觸發。系統瀏覽器也存在一樣的問題,爲了解決開發者須要,建議開發者在touchstart時調用event.preventDefault,這樣就能夠保證內核會一塊兒觸發touchmove事件了

可是調用了preventDefault依然會卡頓。網絡上也有不少答案說是要加上passive屬性,網絡

Passive Event Listeners是Chrome提出的一個新的瀏覽器特性:Web開發者經過一個新的屬性passive來告訴瀏覽器,當前頁面內註冊的事件監聽器內部是否會調用preventDefault函數來阻止事件的默認行爲,以便瀏覽器根據這個信息更好地作出決策來優化頁面性能。當屬性passive的值爲true的時候,表明該監聽器內部不會調用preventDefault函數來阻止默認滑動行爲,Chrome瀏覽器稱這類型的監聽器爲被動(passive)監聽器。目前Chrome主要利用該特性來優化頁面的滑動性能,因此Passive Event Listeners特性當前僅支持mousewheel/touch相關事件。
引用自: https://juejin.im/post/5b28d6...

clipboard.png

可是發現效果依然不滿意,仍是十分卡頓。框架

嘆了口氣,最後的解決方案就是,仍是去找了一個輕便的手勢庫hammerjs,用到了其中的pan相關的事件。dom

function () {
        // 綁定手勢
        var hammertime = new window.Hammer(document.querySelector('xx'))
        hammertime.get('pan').set({direction: window.Hammer.DIRECTION_ALL}) // 要設定方向纔會開啓垂直方向的移動
        hammertime.on('panstart', this.touchStart)
        // 上拉
        hammertime.on('panup', this.touchMove)
        hammertime.on('panend', this.touchEnd)
      },

hammerjs卻是很方便, 主要用到其中的panup事件,panup的事件對象裏,也暴露出了一些本來須要本身計算的參數,這樣能夠直接拿來使用, 主要使用到y軸的加速度,還有時間差,deltaTime, velocityY函數

clipboard.png

clipboard.png

三個動畫須要和諧,大體調整的比例是4:11,若是圓的位置要保持在屏幕中間的話post

// 作邊界限定,否則會一次增加過快
if (speed > 4) speed = 4
   yuanSpeed= speed * 4 //圓的速率
   background.bottom -= speed * 11 // 背景的速率
    // 總體增大到1.8倍
    yuan.scaleVal += speed / 110 // 圓總體增大
  • 假重力回落

爲了效果更好,須要加上一個若是touchend了,就圓就往回落必定距離的效果。
一開始的思路是在圓的現有高度的基礎上*0.98,乘以某個係數。可是會出現一個問題,當每次提拉的距離小於0.98的時候,會出現往上拽不動的問題。因此,最終解決方法是,將這一次滑動全部的move累加的值記錄下來,touchend的時候再減掉這個累加值的一半:性能

this.totalMove+= yuanSpeed

toucheEnd: function () {
  // 當次距離的一半
  this.yuanHeight-= this.totalMove / 2
}
  • 如何動態計算圓到盒子底部的距離

因爲這個方案涉及不少dom操做,不像canvas那樣有xy座標,方便作碰撞檢測。dom操做在計算的過程當中原本就十分消耗性能。

好比遇到一個問題:

圓的背景圖片到盒子頂部有必定的距離,圓自己會不斷放大,這個空白的距離怎麼計算而且適配全部的屏幕呢?
加入1334設計稿上,這個空白的距離是50px,那麼如何動態計算呢?
解決的辦法也有點笨,但管用吧。就是用圓放大後的高度/原來圓背景圖的高度得出的比例 * 50

var yuanHeight= yuan.getBoundingClientRect().height // getBoundingClientRect能拿到scale後高度。直接用innerHeight不行
var yuanMargin = (yuanHeight / yuanImg.naturalHeight) * 50 // naturalHeight本來的高度

固然整個計算過程,額,有點費勁

// 主要是圓距離屏幕頂部的高度+超出屏幕部分的高度
// 在這個距離的基礎上,減掉或者加上一些其餘距離
// maxOffsetTop 是初始的屏幕頂部超出部分
// bottom是不斷下移的距離
var top = Math.abs(this.maxOffsetTop - this.bottom) - self.boxBottom + yuanMargin + yuan.getBoundingClientRect().top
  • transition動畫閃爍的問題

當碰撞檢測成功或者失敗的時候都有相關的動畫,一開始是直接用css3寫,可是會出現奇怪的第一次閃爍的狀況。
索性仍是換了一種可能看上去不是十分優雅,可是好用的辦法。

animateFn: function () {
    // 圖片張數和動畫時間 01~30 1s
    var time = 33,  // 動畫時間 / 圖片張數
      self = this,
      idx = this.animateIndex % 30 // animateIndex 初始值爲0
      imgDom
    img= $('divImg img')
    setTimeout(function () {
    // 控制圖片顯隱
      $(img[idx]).removeClass('displaynone').siblings().addClass('displaynone')
      self.animateIndex++
      // 動畫1s後結束
      if (self.animateIndex <= 30) {
        // 調用自身
        self.animateFn()
      } else {
        // 動畫結束
      }
    }, time)
  },

固然,以上的解決方法都十分笨拙,確定有不少高性能的辦法能夠打磨得更好,可是呢,我想一想,無論如何,是一個思考過程,仍是須要記錄一下。

相關文章
相關標籤/搜索