WTF小程式之animation
目錄
小程式的 animation 有一套怪異的 API,既不符合 css 的 keyframes,又不符合 DOM 的 API,可以說是一個四不像,所以很久以來,我是對這個 API 是有點排斥的,但是,在對 cover-view 中進行動畫的時候,還非得用這個 API 不可。因為對 cover-view 進行變換存在著一些 BUG
animation 例項由 wx.createAnimation
建立得到,支援的變換方法有:
Object.getOwnPropertyNames(wx.createAnimation().__proto__) ["constructor", "export", "step", "matrix", "matrix3d", "rotate", "rotate3d", "rotateX", "rotateY", "rotateZ", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "skew", "skewX", "skewY", "translate", "translate3d", "translateX", "translateY", "translateZ", "opacity", "backgroundColor", "width", "height", "left", "right", "top", "bottom"]
拋開這些控制變換的方法不談,我們要認識下面這兩個對傳統 DOM 來說相對陌生的方法:export
方法和 step
方法。
step 方法在一系列變換之後呼叫,並且接受 4 個可選引數:duration
, timingFunction
, delay
,transformOrigin
,其他三個引數跟 css 沒啥區別,值得一提的是 duration 這個引數,它可以取值為 0,這有什麼用呢?可以用來重置動畫到最初的狀態:
this.animation.rotate(0, 0) .scale(1) .translate(0, 0) .skew(0, 0) .step({duration: 0}) // 設定 duration: 0 來使動畫回到初始狀態
那麼這個 step 方法是做什麼用的呢?它就好比是武術中的一 "招",在這一招之內,所有的動作都是同步進行的。當 step 被呼叫時,之前呼叫的所有變換是同時進行的,step 呼叫後再變換就會被放到下一 "招" 中。試比較下面的兩段程式碼的不同:
// 在一招之內,移動 100 的同時後空翻 180 this.animation.rotate(180).translateX(100).step({duration: 800}) // 第一招先移動 100,第二招後空翻 180 this.animation.rotate(180).step({duration:400}).translateX(100).step({duration: 400})
儘管上面兩個動畫都用時 800ms,但是它們表現是不一致的,見下圖:
兩個 step一個 step
export 方法返回一個具有 actions 屬性的物件,其中 actions 是一個物件陣列。這個物件描述了一個 step 裡所有的變換以及所用的時間等資訊。還是拿上面的例子來說,下圖分別為一個 step 和兩個 step 的 actions 物件:可以看出,呼叫 step 的次數和 actions 陣列的長度是相等的。
在呼叫 export 方法之後,通過將這個具有 action 屬性的物件 setData 到 animationData 上面,最終的內部實現可能是,小程式通過改變元素的 style 的 transition 值,最終實現一步一步的動畫。(在開發者工具可以看出對應元素的 style 發生了變化)
到這裡,我們可以發現,其實所謂的 animation,是由一系列的 transition 依次觸發,組合而成,而一個 step 是一個 transition。也就不難理解,在沒有重置動畫的情況下,為什麼再去 export 不會觸發動畫,因為此時元素在上一次動畫的作用下,元素的 style 已經被設定為最終狀態,也就不會有過渡動畫產生。如果想再次觸發動畫,必須使用 step({duration:0})
對動畫進行重置。
在小程式的 api 下,如何實現 infinate 動畫呢,這就要用到 transitionend
事件了。官方文件告訴我們,這個事件會在 WXSS transition 或 wx.createAnimation 動畫結束後觸發,所以我們可以在一個 step 結束之後重置動畫,然後再次 export,實現 infinate 動畫,直接看程式碼 (mpvue):
// wxml
<cover-image src="../img/line.png" :animation="animationData" @transitionend="reAnimation" ></cover-image>
// js
onReady () {
this.animation = wx.createAnimation({
duration: 8000,
timingFunction: 'linear',
delay: 0,
transformOrigin: '50% 50% 0'
})
this.animation.rotate(360).step()
this.animationData = this.animation.export()
},
reAnimation () {
this.animation.rotate(0).step({duration: 0}) // 重置動畫
this.animationData = this.animation.export()
setTimeout(() => {
this.animation.rotate(360).step()
this.animationData = this.animation.export()
}, 60)// 播放下一次動畫
},
(本文完,不對之處還望指出)