1. 程式人生 > >Android 憶童年 DVD機待機 loading 動畫

Android 憶童年 DVD機待機 loading 動畫

週末逛「即刻」開到這個訊息,然後心生一計,我可以自己做一個 DVD 的 loading效果啊。

什麼?你不知道 DVD 是啥?呵呵,暴露年齡了。這是一個具有年代感的東西。你沒看到過說明要麼你年輕,年輕好啊。要麼你家窮,我那會兒反正是租的碟片在同學家放,自己家買是不可能的了,一輩子都不可能。

正式開始實現之前,開始簡單的建模及分析。

整體效果就是:一個圓或者矩形在一個大的矩形(類比電視螢幕)上面運動,撞到螢幕邊緣就開始反彈,這裡類比於光的反射。直到它成功切入一個角落,那麼就停止運動。

轉化一下其實就是我需要在兩點連線的軌跡上面作圓或者其他圖形,這兩點必須滿足在大的矩形的任意兩邊上(可以是相鄰的,也可以是隔開的,單不能是同一邊)。

那麼對應到 Android 中就轉換成兩個問題,第一 如何通過已知點和角度和相關約束條件計算出另外一個點的座標。第二 如何拿到兩點相連的軌跡。

針對問題一,很明確,這裡需要使用到勾股定理那些知識,或者準確的說,這裡就是需要用到「正切函式」。

針對問題二,這裡就需要使用到 Path 和 PathMeasure 這兩個類。在 Path 中我們可以通過 moveTo() lineTo() 兩個方法實現兩點連線。然後呼叫 pathMeasure.setPath(path, false) 方式使 Path 和 PathMeasure 相關聯。接著介紹 PathMeasure 一個超級厲害的方法: pathMeasure.getPosTan(distance, positionArray, tanArray)

這個方法第一個引數指定一段長度,接著傳入兩個陣列,positionArray[2] 和 tanArray[2] 。 最後positionArray 返回的就是對應 distance 後的終點座標,到這裡,終點座標問題解決。更厲害的是後面,給到你 X 和 Y 對應的正切值。那麼我們這裡需要使用的正切值就是 tanValue = Math.abs(tanArray[1] / tanArray[0])

兩個大問題解決了,在討論一些小問題,比如說運動軌跡到底有多少種? 

大致就是這個情況,唉,第一次用「預覽」畫圖,大家能看明白就好。這裡具體有八種情況的軌跡。按大類分就是四大類,分別對應各個象限的情況。每個象限又有上下兩種運動方向,所以就是有八大類。

軌跡有八種,但是要再具體的話,每一種又可以再拆分出一種情況。那就是上面提到的,這兩點是相鄰的或者是隔開的。

最後直接剛上一組程式碼,對應上圖的相關情況。

private fun handUp() {
    if (currentX == startX()) {// normal
        val resultX = startX() + (currentY - startY()) / tanValue
        val dx = resultX - endX()
        if (resultX > endX()) {
            Log.e("calculate", "上升 handUp:越界情況")
            val dy = tanValue * (endX() - currentX)
            path.lineTo(endX(), currentY - dy)
        } else {
            Log.e("calculate", "上升 handUp:正常情況")
            path.lineTo(resultX, startY())
        }
        needRevert = dx > 0
    } else if (currentX == endX()) {// normal revert
        val resultX = (endY() - currentY) / tanValue
        val dx = resultX - currentX
        if (currentX - resultX < startX()) {
            Log.e("calculate", " handUp 下降:越界情況")
            path.lineTo(startX(), rectF.height() - dx * tanValue)
            needRevert = true
        } else {
            Log.e("calculate", " handUp 下降:正常情況")
            path.lineTo(currentX - resultX, rectF.height() - ovalY())
            needRevert = false
        }
    } else {
        Log.e(
            "calculate",
            " handUp 異常情況:currentX =$currentX startX=${startX()} endx:${endX()} startX=${startX()} endx:${endX()}"
        )
        animator.cancel()
        state = STATE_ERROR
    }
}
複製程式碼

最後效果就是這樣的,目前支援「圓形」和「橢圓形」

原始碼地址:DVD loading by Joe