Android 使用cos和sin繪制復合曲線動畫
前言
前兩周在開發新需求的時候,設計給瞭一份類似這樣的動畫:
看著不難,即使一遍看不懂,嘿嘿,不還有設計稿。
作為一個平時很少寫動畫的 Android 開發仔,看到一段段的緩入緩出曲線的設計稿時,我的心情是這樣的:
雖然,Android 動畫默認的插值器 AccelerateDecelerateInterpolator 有這樣緩入緩出的效果:
我總不能一整個動畫給它拆成4段動畫來寫,還別說,我第一次寫的代碼還真的是這麼幹的。
第一次分析
本著能少寫一行絕不多寫一字的原則,詢問瞭大佬同事的意見,大佬大手一揮:PathInterpolator(後證實有問題)。
簡單看瞭一下使用方式,需要使用 Path,再看瞭一眼,好傢夥,有可能會用到貝塞爾曲線,放棄~
為瞭能夠快速的解決問題,就使用瞭上面談到的方案:
private fun animateTagView(tagView: TextView) { // [0,200]區間的動畫 val valueAnimatorOne = ValueAnimator.ofInt(0, 200) valueAnimatorOne.addUpdateListener { val per = it.animatedValue as Int / 200f tagView.rotation = 4 * per tagView.scaleX = (1 - 0.1 * per).toFloat() tagView.scaleY = (1 - 0.1 * per).toFloat() } valueAnimatorOne.duration = 200 // [200,560]區間的動畫 val valueAnimatorTwo = ValueAnimator.ofInt(200, 560) valueAnimatorTwo.addUpdateListener { val per = (it.animatedValue as Int - 200) / 360f tagView.rotation = 3 - 11 * per tagView.scaleX = (0.9 + 0.1 * per).toFloat() tagView.scaleY = (0.9 + 0.1 * per).toFloat() } valueAnimatorTwo.duration = 360 // [560,840]區間的動畫 val valueAnimatorThree = ValueAnimator.ofInt(560, 840) valueAnimatorThree.addUpdateListener { val per = (it.animatedValue as Int - 560) / 280f tagView.rotation = -8 + 12 * per tagView.scaleX = (1 - 0.2 * per).toFloat() tagView.scaleY = (1 - 0.2 * per).toFloat() } valueAnimatorThree.duration = 280 // [840,1000]的動畫 val valueAnimatorFour = ValueAnimator.ofInt(840, 1000) valueAnimatorFour.addUpdateListener { val per = (it.animatedValue as Int - 840) / 160f tagView.rotation = 4 - 4 * per tagView.scaleX = (0.8 + 0.2 * per).toFloat() tagView.scaleY = (0.8 + 0.2 * per).toFloat() } valueAnimatorFour.duration = 160 // 使用AnimatorSet串行執行動畫 val animationSet = AnimatorSet() animationSet.playSequentially(valueAnimatorOne, valueAnimatorTwo, valueAnimatorThree, valueAnimatorFour) tagView.post { tagView.pivotX = 0f tagView.pivotY = ad_tag_two.measuredHeight.toFloat() animationSet.start() } }
整個動畫被我拆成瞭[0,200]、[200,560]、[560,840]和[840,1000]四段屬性動畫,因為產品說隻需要播放一次,所以使用 AnimatorSet 將動畫組裝起來,就可以解決問題。
第二次分析
第一次得到的方案雖然能夠解決問題,如果遇到循環播放,AnimatorSet 就不行瞭,有沒有其他方案呢?
趁著周末的時間,學瞭一下 PathInterpolator,發現這個玩意也解決不瞭問題,或者說不好解決問題,雖然可以用三階貝塞爾曲線分段畫出上述曲線,但 PathInterpolator 要求起點和終點分別在 (0,0) 和 (1,1)。
既然插值器不行,可以試試估值器,但一個估值器也解決不瞭旋轉和縮放兩種動畫,看來得靠 AnimatorUpdateListener 去解決問題。
回頭想一下,插值器是將均勻的時間片段轉化成加速或者減速的行為,我們也可以將均勻的時間片段轉化成對應的曲線,隻要做好兩點:
使用線性的插值器 LinearInterpolator。
將上面的曲線拆分,通過不同的 sin 或者 cos 方法表達。
以旋轉動畫為例,拆成的 sin 函數:
另外一段動畫的函數可以參考代碼:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val tvContent = findViewById<TextView>(R.id.tv_content) val valueAnimatorOne = ValueAnimator.ofFloat(0.0f, 1.5f) valueAnimatorOne.addUpdateListener { // 通過對應的sin和cos設置rotation和scale val per = it.animatedValue as Float var rotation: Float = 0f var scale: Float = 0f if(per >= 0 && per < 0.2f){ rotation = sin((per / 0.2f) * Math.PI.toFloat() - Math.PI.toFloat() / 2) * 1.5f + 1.5f scale = cos(per / 0.2f * Math.PI.toFloat()) * 0.05f + 0.95f } if(per >= 0.2f && per < 0.56f){ rotation = sin(Math.PI.toFloat() / 2 + Math.PI.toFloat() * ( per - 0.2f) / 0.36f) * 5.5f - 2.5f scale = cos((per - 0.2f) / 0.36f * Math.PI.toFloat() + Math.PI.toFloat()) * 0.05f + 0.95f } if(per >= 0.56f && per < 0.84f){ rotation = sin(Math.PI.toFloat() * (per - 0.56f) / 0.28f - Math.PI.toFloat() / 2) * 6f - 2f scale = cos((per - 0.56f) / 0.28f * Math.PI.toFloat()) * 0.1f + 0.9f } if(per in 0.84f..1f){ rotation = sin(Math.PI.toFloat() / 2 + Math.PI.toFloat() * (per - 0.84f) / 0.16f ) * 2f + 2f scale = cos((per - 0.84f) / 0.16f * Math.PI.toFloat() + Math.PI.toFloat()) * 0.1f + 0.9f } // 設置停止時間 if(per > 1f && per <= 1.5f){ rotation = 0f scale = 1.0f } tvContent.rotation = rotation tvContent.scaleX = scale tvContent.scaleY = scale } // 設置線性插值器 valueAnimatorOne.interpolator = LinearInterpolator() // 動畫時間 valueAnimatorOne.duration = 1500 // 無線循環 valueAnimatorOne.repeatCount = -1 tvContent.post { // 設置中心點 tvContent.pivotX = 0f tvContent.pivotY = tvContent.measuredHeight.toFloat() valueAnimatorOne.start() } }
整個代碼還是比較簡單的,旋轉動畫曲線由 sin 得出,縮放由 cos 得出,最後改一下中心點。
總結
本次的動畫案例不難,在面對復合緩入緩出曲線的情形,我們可以拆成一段段,用 sin 或者 cos 去描述,這樣的好處是可以隻使用一個屬性動畫,且可以循環播放。
如果你有更好的方案,歡迎評論區交流。
以上就是Android 使用cos和sin繪制復合曲線動畫的詳細內容,更多關於Android 繪制復合曲線動畫的資料請關註WalkonNet其它相關文章!