記錄小程式touchmove事件中setData優化過程

jionchen發表於2019-02-28

出現場景:

在做一個小球跟隨手指移動的效果時候,由於在touchmove事件中頻繁呼叫setData改變小球的位移實現,在開發工具和IOS平臺還算流暢,但在安卓機下手機預覽出現極其卡頓的互動,簡直是不堪入目。

可微信web開發者工具開啟 片段程式碼https://developers.weixin.qq.com/s/B9UHyvmo7t6t

問題根源:

setData每秒呼叫高達50左右造成的。 引用官方的話就是:

a、touchmove 事件從檢視層(Webview)拋到邏輯層(App Service)

b、邏輯層(App Service)處理 touchmove 事件,再通過 setData 來改變 元件 的位置

一次 touchmove 的響應需要經過 2 次的邏輯層和渲染層的通訊以及一次渲染,通訊的耗時比較大。此外 setData 渲染也會阻塞其它指令碼執行,導致了整個使用者互動的動畫過程會有延遲。

記錄小程式touchmove事件中setData優化過程

如何優化?

1.使用movable-view

movable-view + movable-area可實現移動效果很流暢,但是也有侷限性不能滿足複雜的需求,例如現在需求需要是兩個小球使用兩個手指能同時控制小球移動,則無法實現,還需要配合setData來實現,使用了setData必然會出現卡頓

2.推薦方案:拋棄setData,使用wxs來寫互動

從基礎庫 2.4.4 開始支援 WXS響應事件 直接上程式碼,本人菜鳥,程式碼寫的很亂:

index.js

const app = getApp()

Page({
  data: {
    balls: [1, 2, 3, 4, 5, 5, 6, 7]
  }
})

複製程式碼

index.wxml

//引入wxs檔案
<wxs module="index" src="./index.wxs"></wxs>
<view class='wrap' catchtouchstart='{{index.touchstart}}' catchtouchmove='{{index.touchmove}}' catchtouchend='{{index.touchend}}'>
  <view class="demo hide" wx:for="{{ balls }}"></view>
</view>

複製程式碼

index.wxs

var allTouchs = [], len = 0, instances = [], instanceLen, isMoveEnd = false


function reset(ownerInstance) {
  //重置
  for (var i = 0; i < instanceLen; i++) {
    instances[i].setStyle({
      'transform': '',
      'display': 'none'
    })
  }
}

function touchstart(event, ownerInstance) {
  if (isMoveEnd) return
  //獲取當前移動的手指
  var bounds = event.touches, boundsLen = bounds.length
  allTouchs = event.touches, len = event.touches.length
  instances = ownerInstance.selectAllComponents('.demo'), instanceLen = instances.length

  for (var i = 0; i < instanceLen; i++) {
    var instance = instances[i], bound = bounds[i]

    if (i < boundsLen) {
      //更新
      instance.disabled = false
      instance.identifier = bound.identifier
      instance.setStyle({
        'transform': 'translateX(' + bound.pageX + 'px) translateY(' + bound.pageY + 'px)',
        'display': 'block'
      })
    } else {
      instance.setStyle({
        'transform': '',
        'display': 'none'
      })
      instance.disabled = true
      instance.identifier = ''
    }
  }
}

function touchmove(event, ownerInstance) {
  //獲取當前移動的手指
  var bounds = event.changedTouches, boundsLen = bounds.length, bound = null, instance = null, allTouch = null

  for (var i = 0; i < instanceLen; i++) {
    instance = instances[i]
    for (var j = 0; j < boundsLen; j++) {
      bound = bounds[j]
      if (instance.identifier === bound.identifier) {
        //更新
        instance.setStyle({
          'transform': 'translateX(' + bound.pageX + 'px) translateY(' + bound.pageY + 'px)',
          'display': 'block'
        })
      }
    }
  }
}

function touchend(event, ownerInstance) {
  isMoveEnd = true
  //獲取當前移動的手指
  var bounds = event.changedTouches, boundsLen = bounds.length, bound = null, instance = null, allTouch = null

  for (var i = 0; i < instanceLen; i++) {
    instance = instances[i]
    for (var j = 0; j < boundsLen; j++) {
      bound = bounds[j]
      if (instance.identifier === bound.identifier) {
        //更新
        instance.setStyle({
          'transform': '',
          'display': 'none'
        })
        //移除
        instances[i].disabled = true
        instances[i].identifier = ''
      }
    }
  }

  var tmp = instances.filter(function (item) {
    return !item.disabled
  })
  if (tmp.length < 1) {
    //重置
    reset(ownerInstance)
  }
  isMoveEnd = false
}
module.exports = {
  touchmove: touchmove,
  touchend: touchend,
  touchstart: touchstart
}
複製程式碼

微信web開發者工具開啟小程式程式碼片段https://developers.weixin.qq.com/s/wLxQuwm1786m

instance物件支援的方法:

記錄小程式touchmove事件中setData優化過程
常用的就是獲取元件(類似於獲取dom節點),和設定樣式和class,呼叫app service方法 除了以上方法,還有強大的觸發器函式的使用,可監聽appservice 層中的data變化,具體demo請檢視官方文件 官方文件

經過實機測試,不出出現卡頓效果

注意事項

官方bug:

記錄小程式touchmove事件中setData優化過程

個人總結bug:
1.wxs不能使用ES6+語法,否則會報錯(勾選了ES6轉ES5也沒用)

記錄小程式touchmove事件中setData優化過程

2.console.log()不能直接列印物件,需要JSON.stringify

3.當然不能呼叫app service環境中的方法和wx.xxxx方法

相關文章