欢迎你们访问个人我的博客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或者提供建议.