在做一個小球跟隨手指移動的效果時候,由于在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 渲染也會阻塞其它腳本執行,導致了整個用戶交互的動畫過程會有延遲。
如何優化?
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對象支持的方法:
常用的就是獲取組件(類似于獲取dom節點),和設置樣式和class,調用app service方法 除了以上方法,還有強大的觸發器函數的使用,可監聽appservice 層中的data變化,具體demo請查看官方文檔 官方文檔
經過實機測試,不出出現卡頓效果
注意事項
官方bug:
個人總結bug:
1.wxs不能使用ES6+語法,否則會報錯(勾選了ES6轉ES5也沒用)
2.
console.log()
不能直接打印對象,需要JSON.stringify
3.當然不能調用
app service
環境中的方法和wx.xxxx方法
赞赏