av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

瀏覽:2日期:2022-09-20 14:59:05
前言

前兩周在開(kāi)發(fā)新需求的時(shí)候,設(shè)計(jì)給了一份類(lèi)似這樣的動(dòng)畫(huà):

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

看著不難,即使一遍看不懂,嘿嘿,不還有設(shè)計(jì)稿。

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

作為一個(gè)平時(shí)很少寫(xiě)動(dòng)畫(huà)的 Android 開(kāi)發(fā)仔,看到一段段的緩入緩出曲線的設(shè)計(jì)稿時(shí),我的心情是這樣的:

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

雖然,Android 動(dòng)畫(huà)默認(rèn)的插值器 AccelerateDecelerateInterpolator 有這樣緩入緩出的效果:

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

我總不能一整個(gè)動(dòng)畫(huà)給它拆成4段動(dòng)畫(huà)來(lái)寫(xiě),還別說(shuō),我第一次寫(xiě)的代碼還真的是這么干的。

第一次分析

本著能少寫(xiě)一行絕不多寫(xiě)一字的原則,詢問(wèn)了大佬同事的意見(jiàn),大佬大手一揮:PathInterpolator(后證實(shí)有問(wèn)題)。

簡(jiǎn)單看了一下使用方式,需要使用 Path,再看了一眼,好家伙,有可能會(huì)用到貝塞爾曲線,放棄~

為了能夠快速的解決問(wèn)題,就使用了上面談到的方案:

private fun animateTagView(tagView: TextView) { // [0,200]區(qū)間的動(dòng)畫(huà) 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]區(qū)間的動(dòng)畫(huà) 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]區(qū)間的動(dòng)畫(huà) 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]的動(dòng)畫(huà) 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串行執(zhí)行動(dòng)畫(huà) val animationSet = AnimatorSet() animationSet.playSequentially(valueAnimatorOne, valueAnimatorTwo, valueAnimatorThree, valueAnimatorFour) tagView.post { tagView.pivotX = 0f tagView.pivotY = ad_tag_two.measuredHeight.toFloat() animationSet.start() }}

整個(gè)動(dòng)畫(huà)被我拆成了[0,200]、[200,560]、[560,840]和[840,1000]四段屬性動(dòng)畫(huà),因?yàn)楫a(chǎn)品說(shuō)只需要播放一次,所以使用 AnimatorSet 將動(dòng)畫(huà)組裝起來(lái),就可以解決問(wèn)題。

第二次分析

第一次得到的方案雖然能夠解決問(wèn)題,如果遇到循環(huán)播放,AnimatorSet 就不行了,有沒(méi)有其他方案呢?

趁著周末的時(shí)間,學(xué)了一下 PathInterpolator,發(fā)現(xiàn)這個(gè)玩意也解決不了問(wèn)題,或者說(shuō)不好解決問(wèn)題,雖然可以用三階貝塞爾曲線分段畫(huà)出上述曲線,但 PathInterpolator 要求起點(diǎn)和終點(diǎn)分別在 (0,0) 和 (1,1)。

既然插值器不行,可以試試估值器,但一個(gè)估值器也解決不了旋轉(zhuǎn)和縮放兩種動(dòng)畫(huà),看來(lái)得靠 AnimatorUpdateListener 去解決問(wèn)題。

回頭想一下,插值器是將均勻的時(shí)間片段轉(zhuǎn)化成加速或者減速的行為,我們也可以將均勻的時(shí)間片段轉(zhuǎn)化成對(duì)應(yīng)的曲線,只要做好兩點(diǎn):

使用線性的插值器 LinearInterpolator。將上面的曲線拆分,通過(guò)不同的 sin 或者 cos 方法表達(dá)。以旋轉(zhuǎn)動(dòng)畫(huà)為例,拆成的 sin 函數(shù):

Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)

另外一段動(dòng)畫(huà)的函數(shù)可以參考代碼:

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 { // 通過(guò)對(duì)應(yīng)的sin和cos設(shè)置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 } // 設(shè)置停止時(shí)間 if(per > 1f && per <= 1.5f){ rotation = 0f scale = 1.0f } tvContent.rotation = rotation tvContent.scaleX = scale tvContent.scaleY = scale } // 設(shè)置線性插值器 valueAnimatorOne.interpolator = LinearInterpolator() // 動(dòng)畫(huà)時(shí)間 valueAnimatorOne.duration = 1500 // 無(wú)線循環(huán) valueAnimatorOne.repeatCount = -1 tvContent.post { // 設(shè)置中心點(diǎn) tvContent.pivotX = 0f tvContent.pivotY = tvContent.measuredHeight.toFloat() valueAnimatorOne.start() }}

整個(gè)代碼還是比較簡(jiǎn)單的,旋轉(zhuǎn)動(dòng)畫(huà)曲線由 sin 得出,縮放由 cos 得出,最后改一下中心點(diǎn)。

總結(jié)

本次的動(dòng)畫(huà)案例不難,在面對(duì)復(fù)合緩入緩出曲線的情形,我們可以拆成一段段,用 sin 或者 cos 去描述,這樣的好處是可以只使用一個(gè)屬性動(dòng)畫(huà),且可以循環(huán)播放。

如果你有更好的方案,歡迎評(píng)論區(qū)交流。

以上就是Android 使用cos和sin繪制復(fù)合曲線動(dòng)畫(huà)的詳細(xì)內(nèi)容,更多關(guān)于Android 繪制復(fù)合曲線動(dòng)畫(huà)的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 闷骚老干部cao个爽 欧美区一区二 | 欧美在线视频播放 | 国产成人免费观看 | 日韩a在线| 精品视频999 | 欧美精品一区二区三区四区 | 亚洲欧美一区二区三区四区 | 国产福利一区二区 | 日韩欧美精品一区 | 日韩毛片免费看 | 啪啪毛片| 午夜婷婷| 国产a一级 | 99视频+国产日韩欧美 | 成人午夜视频在线观看 | 在线中文字幕av | 亚洲激情五月 | 婷婷第四色 | 国产成人精品视频 | 日韩视频在线免费观看 | av片在线观看 | 成人高潮片免费网站 | 天堂中文av | 在线观看h片| 亚洲性视频 | 综合一区二区三区 | 国产深夜福利 | 视频在线一区 | 青青草精品视频 | 欧美精品一二三区 | 国产午夜免费 | 久久av片 | 欧美成人小视频 | 亚洲一区二区三区视频 | 福利在线 | 亚洲精品久久久久avwww潮水 | 五月天毛片 | 黄色大片在线免费观看 | 成人免费黄色大片 | 精品国产一区二区在线观看 | 日韩精品在线看 |