歡迎你們訪問個人我的博客java
在dribbble閒逛的時候發現的一個有意思的星球運動的動畫,恰好最近時間尚可,就簡單實現了一下中間運動的部分,又是由於時間的緣由,開頭位移的部分沒有完成.android
老辦法,先分解動畫的構成.整個動畫能夠看作是一個自旋的星球從右上角由小變大的移動到屏幕的中央的.git
星球的位移及縮放不說(實際上是最近有需求,暫時沒時間完善),主要完善了星球的旋轉及尾部的處理.github
最底層是背景的星星閃爍,每次在星球必定範圍內隨機出現,並縮放就好canvas
最開始設計尾部效果的時候,是在沒列中設計了兩端線.再不斷的運行及移動.可是實現起來很亂.最後採用了先繪製全部尾部展現的內容,而後在用和背景同樣的顏色部分遮蓋並移動此部分造成視覺上的效果的方法.(也能夠設置PorterDuff模式來展現).設計過程當中的效果以下dom
星球的設計,星球的自己使用簡單的遮蓋和貝塞爾曲線就能完成一個較爲滿意的星球背景.函數
重點是星球地表的設計以及星球自轉下的地表樣式的移動.解決的方法是是先繪製三個重複並連續的地表樣式,經過移動整個地表樣式模擬星球的轉動.最後經過PorterDuff來控制展現的部分和星球的位置重合.優化
未開啓PorterDuff模式時繪製的樣式以下:動畫
開啓PorterDuff模式後再指定位置展現指定形狀的圖形以下:spa
最後再移動設置好的星球地貌就能夠模擬出星球轉動的效果了
private fun drawStarts(canvas: Canvas, perIndexInAll: Float) {
//背景的星星在星球附近的必定範圍內隨機出現
val maxRand = 800
canvas.translate(-maxRand / 2F , -maxRand / 2F)
val Random = Random(perIndexInAll.toInt().toLong())
//繪製背景的星星
for (index in 0..4){
drawStart(canvas , Random.nextFloat() * maxRand , Random.nextFloat() * maxRand , perIndex)
}
canvas.translate(maxRand / 2F , maxRand / 2F)
}
//繪製背景的星星內容
//繪製背景的星星內容
private fun drawStart(canvas: Canvas, x: Float, y: Float, per: Float) {
var per = per
//這個部分是爲了讓星星實現從小到大後再從大到小的變更
if (per >= 1.0F){
per -= 1F
}
if (per <= 0.5F){
per *= 2
}else{
per = (1 - per) * 2
}
canvas.save()
canvas.translate(x , y)
canvas.scale(per , per)
val paint = Paint()
paint.color = 0xff78D8DF.toInt()
val startLength = 30F
val startOffset = startLength / 3F
//經過路徑描繪星星的形狀
val path = Path()
path.moveTo(0F , startLength)
path.lineTo(startOffset , startOffset )
path.lineTo(startLength , 0F)
path.lineTo(startOffset , -startOffset )
path.lineTo(0F , -startLength)
path.lineTo(-startOffset , -startOffset )
path.lineTo(-startLength , 0F)
path.lineTo(-startOffset , startOffset )
path.lineTo(0F , startLength)
canvas.drawPath(path , paint)
paint.color = viewBackgroundColor
//經過縮小繪製星星內部形狀
canvas.scale(0.3F , 0.3F)
canvas.drawPath(path , paint)
canvas.restore()
}
複製代碼
private fun drawGas(canvas: Canvas, index: Float) {
canvas.save()
canvas.rotate(45F)
val gasWidth = 18F
val baseR = baseR * 0.7F
val absBaseR = baseR / 5F
val paint = Paint()
paint.strokeWidth = gasWidth
paint.style = Paint.Style.STROKE
paint.color = 0xff2F3768.toInt()
val paintArc = Paint()
paintArc.color = 0xff2F3768.toInt()
val gasLength = baseR * 2F
canvas.save()
val gsaL = gasWidth / 2F * 3
var maxGasLength = (gasLength + gsaL ) / 2
var index = index
canvas.scale(1F , -1F)
//繪製星球后面的氣流狀況
//捨不得那麼多定義好的變量
//又不想寫個參數不少的函數,就這麼實現了
canvas.save()
canvas.translate(baseR , baseR * 1.2F)
canvas.translate(0F , absBaseR)
//drawLines函數一個繪製兩頭帶半圓的線段
drawLines(0F, maxGasLength, canvas, paint)
drawWhite( maxGasLength * index, gasWidth , gsaL * 2 , canvas)
drawWhite( maxGasLength * (index - 1 ) * 1.1F, gasWidth , gsaL * 2 , canvas)
drawWhite( maxGasLength * (index + 1 ) * 1.1F, gasWidth , gsaL * 2 , canvas)
canvas.restore()
index = index + 0.3F
//.....沒有寫函數就不上重複的代碼了
val rectf = RectF(-baseR , -baseR , baseR ,baseR)
canvas.drawArc(rectf , 0F , 180F , false , paint)
canvas.drawLine(baseR ,0F , baseR , -baseR, paint)
canvas.drawLine(-baseR ,0F , -baseR , -baseR, paint)
canvas.restore()
}
//繪製尾部空白部分
private fun drawWhite(offset: Float, gasWidth: Float, gsaL : Float , canvas: Canvas) {
val r = gasWidth / 2F
canvas.save()
canvas.translate( 0F , offset - 2 * gsaL )
val pointPaint = Paint()
pointPaint.strokeWidth = 20F
pointPaint.color = Color.RED
//經過貝塞爾曲線繪製半圓效果
val path = Path()
path.moveTo(-r , gsaL)
path.cubicTo(
- r * C , gsaL - r,
r * C , gsaL - r,
r , gsaL
)
path.lineTo(r , - gsaL)
path.cubicTo(
r * C , - gsaL + r,
-r * C , - gsaL + r,
-r , - gsaL
)
path.lineTo(-r , gsaL * 1.5F)
val paint = Paint()
paint.color = viewBackgroundColor
canvas.drawPath(path , paint)
canvas.restore()
}
複製代碼
private fun drawPlanet(canvas: Canvas , index : Float) {
//設置原圖層
val srcB = makeSrc(index)
//設置遮罩層
//遮罩層只有一和星球大小同樣的圓
val dstB = makeDst(index)
val paint = Paint()
canvas.saveLayer(-baseR, -baseR, baseR , baseR, null, Canvas.ALL_SAVE_FLAG)
//繪製遮罩層
canvas.drawBitmap(dstB, -baseR / 2F, -baseR / 2F , paint)
//設置遮罩模式爲SRC_IN顯示原圖層中原圖層與遮罩層相交部分
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(srcB, width / -2F, height / -2F , paint)
paint.xfermode = null
}
//設置源圖層
fun makeSrc(index :Float): Bitmap {
val bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bm)
canvas.translate(width.toFloat() / 2F , height.toFloat() / 2F)
val paint = Paint()
paint.color = 0xff57BEC6.toInt()
paint.style = Paint.Style.FILL
val rectf = RectF(-baseR / 2F, -baseR / 2F, baseR / 2F, baseR / 2F)
canvas.drawArc(rectf , 0F , 360F , true , paint)
canvas.save()
//繪製星球背景
paint.color = 0xff78D7DE.toInt()
var baseR = baseR * 0.9.toFloat()
val rectf2 = RectF(-baseR / 2F, -baseR / 2F, baseR / 2F, baseR / 2F)
canvas.translate(baseR / 6F , baseR / 6F)
canvas.drawArc(rectf2 , 0F , 360F , true , paint)
canvas.restore()
canvas.rotate(-45F)
canvas.save()
val bottomBaseR = baseR / 0.9F / 2
val path = Path()
path.moveTo(-bottomBaseR , 0F)
path.cubicTo(-bottomBaseR , bottomBaseR * 2, bottomBaseR , bottomBaseR * 2, bottomBaseR , 0F)
path.cubicTo(
bottomBaseR * C,bottomBaseR ,
-bottomBaseR * C,bottomBaseR ,
-bottomBaseR , 0F
)
//繪製星球背景的陰影效果
paint.color = 0xffAAEEF2.toInt()
paint.style = Paint.Style.FILL
canvas.drawPath(path , paint)
//繪製星球的地貌
drawPoints(index , canvas)
canvas.restore()
paint.strokeWidth = 30F
paint.color = 0xff2F3768.toInt()
paint.style = Paint.Style.STROKE
canvas.drawArc(rectf , 0F , 360F , true , paint)
return bm
}
private fun drawPoints(index: Float, canvas: Canvas) {
val paintB = Paint()
val paintS = Paint()
paintS.style = Paint.Style.FILL
paintS.color = 0xffE7F2FB.toInt()
paintB.style = Paint.Style.FILL
paintB.color = 0xff2F3768.toInt()
val baseRB = baseR / 2F / 3
val baseRS = baseR / 2F / 3 / 3
val rectfB = RectF(-baseRB, -baseRB, baseRB, baseRB)
val rectfS = RectF(-baseRS, -baseRS, baseRS, baseRS)
val pointPaint = Paint()
pointPaint.color = Color.BLACK
pointPaint.strokeWidth = 50F
val coverWidth = baseR
//經過移動座標原點模擬星球的自轉效果
canvas.translate(-coverWidth / 2F , coverWidth * 1.5F)
val index = index
canvas.translate(0F , coverWidth * index )
//重複繪製三次星球的地貌使得星球的自轉無縫鏈接
for (i in 0..2){
canvas.save()
canvas.translate(coverWidth / 3F / 2 , -coverWidth / 3F * 2)
canvas.drawArc(rectfB , 0F , 360F , true , paintB)
canvas.drawArc(rectfS , 0F , 360F , true , paintS)
canvas.restore()
canvas.save()
canvas.translate(coverWidth / 3F *2 , -coverWidth / 3F)
canvas.drawArc(rectfB , 0F , 360F , true , paintB)
canvas.drawArc(rectfS , 0F , 360F , true , paintS)
canvas.restore()
canvas.save()
canvas.translate(coverWidth / 3F *2 , -coverWidth / 8F * 7 + -coverWidth / 10F )
canvas.drawArc(rectfS , 0F , 360F , true , paintB)
canvas.restore()
canvas.save()
canvas.translate(coverWidth / 3F *2 , -coverWidth / 8F * 7 - -coverWidth / 10F )
canvas.drawArc(rectfS , 0F , 360F , true , paintB)
canvas.restore()
canvas.translate(0F , -coverWidth)
}
}
複製代碼
相關代碼能夠訪問個人GitHub來獲取,歡迎你們start或者提供建議.