在普通的網頁開發中,動畫效果可以通過css3來實現大部分需求,在小程序開發中同樣可以使用 css3 ,同時也可以通過 api 方式來實現。
css3
api
指路: 小程序animatiom動畫API
小程序中,通過調用 api 來創建動畫,需要先創建一個實例對象。這個對象通過 wx.createAnimation 返回, animation 的一系列屬性都基於這個實例對象。
wx.createAnimation
animation
let animation = wx.createAnimation({ duration: 2000, delay: 0, timingFunction: "linear", }); 複製代碼
這個 animation 就是通過 wx.createAnimation 之後返回的實例。在創建過程中,可以給這個實例添加一些屬性,如以上代碼所示,等同於 css3 中 animation:$name 2s linear 的寫法。
animation:$name 2s linear
實例創建完成之後,基於該實例,添加需要的動態效果,動態類型可以查閱文檔得知,以最常見的移動,旋轉為例:
animation.translate($width, 0).rotate($deg); 複製代碼
.step() 表示一組動畫的結束
.step()
animation.step(); 複製代碼
動畫效果添加完成了,如何給想要的dom添加動效呢。這裡需要用到 .export() 導齣動畫隊列,賦值給某個dom對象。
.export()
this.setData({ moveOne: animation.export() }) 複製代碼 <view animation="{{moveOne}}"></view> 複製代碼
以下將通過2組動畫,來對比一下 css3 與 api 實現方式的不同。
下圖有兩組動畫,分別為 api 方式(上)與 css3 方式(下)完成的效果,點擊move按鈕,動畫啟動。
以下分別為 css3 與 api 的核心代碼:
<!-- wxml --> <view class=border> <view class=css-block {{isMove && "one"}}></view> <view class=css-block {{isMove && "two"}}></view> <view class=css-block {{isMove && "three"}}></view> <view class=css-block {{isMove && "four"}}></view> </view> 複製代碼 // scss @mixin movePublic($oldLeft,$oldTop,$left,$top) { from { transform:translate($oldLeft,$oldTop); } to { transform:translate($left,$top); } }
@mixin blockStyle($color,$name) { background: $color; animation:$name 2s linear infinite alternate; } .one { @include blockStyle(lightsalmon,onemove); }
@keyframes onemove { @include movePublic(50rpx,-25rpx,-150rpx,0rpx); }
.two { @include blockStyle(lightblue,twomove); }
@keyframes twomove { @include movePublic(0rpx,25rpx,-50rpx,0rpx); }
.three { @include blockStyle(lightgray,threemove); }
@keyframes threemove { @include movePublic(0rpx,25rpx,50rpx,0rpx); }
.four { @include blockStyle(grey,fourmove); }
@keyframes fourmove { @include movePublic(-50rpx,-25rpx,150rpx,0rpx); } 複製代碼 // js moveFunction(){ this.setData({ isMove: true }) } 複製代碼
css3 中通過動態改變 class 類名來達到動畫的效果,如上代碼通過 one 、 two 、 three 、 four 來分別控制移動的距離,通過sass可以避免代碼過於冗餘的問題。 (糾結如何在小程序中使用 sass 的童鞋請看這裡哦: wechat-mina-template )
class
one
two
three
four
sass
moveClick(){ this.move(-75,-12.5,25,moveOne); this.move(-25,12.5, 0,moveTwo); this.move(25, 12.5,0,moveThree); this.move(75, -12.5,-25,moveFour); this.moveFunction(); // 該事件觸發css3模塊進行移動 },
// 模塊移動方法 move: function (w,h,m,ele) { let self = this; let moveFunc = function () { let animation = wx.createAnimation({ duration: 2000, delay: 0, timingFunction: "linear", });
animation.translate(w, 0).step() self.setData({ [ele]: animation.export() }) let timeout = setTimeout(function () { animation.translate(m, h).step(); self.setData({ // [ele] 代表需要綁定動畫的數組對象 [ele]: animation.export() }) }.bind(this), 2000) } moveFunc(); let interval = setInterval(moveFunc,4000) } 複製代碼
效果圖可見,模塊之間都是簡單的移動,可以將他們的運動變化寫成一個公共的事件,通過向事件傳值,來移動到不同的位置。其中的參數 w,h,m,ele 分別表示發散水平方向移動的距離、聚攏時垂直方向、水平方向的距離以及需要修改 animationData 的對象。
w,h,m,ele
animationData
通過這種方法產生的動畫,無法按照原有軌跡收回,所以在事件之後設置了定時器,定義在執行動畫2s之後,執行另一個動畫。同時 動畫只能執行一次 ,如果需要循環的動效,要在外層包裹一個重複執行的定時器到。
查看源碼,發現 api 方式是通過 js 插入並改變內聯樣式來達到動畫效果,下面這張動圖可以清晰地看出樣式變化。
js
列印出賦值的 animationData , animates 中存放了動畫事件的類型及參數; options 中存放的是此次動畫的配置選項, transition 中存放的是 wx.createAnimation 調用時的配置, transformOrigin 是默認配置,意為以對象的中心為起點開始執行動畫,也可在 wx.createAnimation時進行配置。
animates
options
transition
transformOrigin
上面的模塊移動動畫不涉及邏輯交互,因此新嘗試了一個音樂播放動畫,該動畫需要實現暫停、繼續的效果。
兩組不同的動畫效果對比,分別為 api (上)實現與 css3 實現(下):
以下分別是 css3 實現與 api 實現的核心代碼:
<!-- wxml --> <view class=music musicTwo musicRotate {{playTwo ? " ": "musicPaused"}} bindtap=playTwo> <text class="iconfont has-music" wx:if="{{playTwo}}"></text> <text class="iconfont no-music" wx:if="{{!playTwo}}"></text> </view> 複製代碼 // scss .musicRotate{ animation: rotate 3s linear infinite; }
@keyframes rotate{ from{ transform: rotate(0deg) } to{ transform: rotate(359deg) } }
.musicPaused{ animation-play-state: paused; } 複製代碼 // js playTwo(){ this.setData({ playTwo: !this.data.playTwo },()=>{ let back = this.data.backgroundAudioManager; if(this.data.playTwo){ back.play(); } else { back.pause(); } }) } 複製代碼
通過 playTwo 這個屬性來判斷是否暫停,並控制 css 類的添加與刪除。當為 false 時,添加 .musicPaused 類,動畫暫停。
playTwo
css
false
.musicPaused
<!-- wxml --> <view class=music bindtap=play animation="{{play && musicRotate}}"> <text class="iconfont has-music" wx:if="{{play}}"></text> <text class="iconfont no-music" wx:if="{{!play}}"></text> </view> 複製代碼 // js play(){ this.setData({ play: !this.data.play },()=>{ let back = this.data.backgroundAudioManager; if (!this.data.play) { back.pause(); // 跨事件清除定時器 clearInterval(this.data.rotateInterval); } else { back.play(); // 繼續旋轉,this.data.i記錄了旋轉的程度 this.musicRotate(this.data.i); } }) }, musicRotate(i){ let self = this; let rotateFuc = function(){ i++; self.setData({ i:i++ }); let animation = wx.createAnimation({ duration: 1000, delay: 0, timingFunction: "linear", }); animation.rotate(30*(i++)).step() self.setData({ musicRotate: animation.export() }); } rotateFuc(); let rotateInterval = setInterval( rotateFuc,1000 ); // 全局定時事件 this.setData({ rotateInterval: rotateInterval }) } 複製代碼
通過 api 實現的方式是通過移除 animationData 來控制動畫,同時暫停動畫也需要清除定時器,由於清除定時器需要跨事件進行操作,所以定了一個全局方法 rotateInterval 。
rotateInterval
api 方式定義了旋轉的角度,但旋轉到該角度之後便會停止,如果需要實現重複旋轉效果,需要通過定時器來完成。因此定義了變數i,定時器每執行一次便加1,相當於每1s旋轉30°,對 animation.rotate() 中的度數動態賦值。暫停之後繼續動畫,需要從原有角度繼續旋轉,因此變數i需要為全局變數。
animation.rotate()
下圖可以看出, api 方式旋轉是通過不斷累加角度來完成,而非 css3 中循環執行。
通過上述兩個小例子對比,無論是便捷度還是代碼量,通過 css3 來實現動畫效果相對來說是更好的選擇。 api 方式存在較多侷限性:
綜合以上,推薦通過 css3 來完成動畫效果。
本文發佈於薄荷前端週刊,歡迎Watch & Star