Android自定义View之星球运动

欢迎大家访问我的个人博客

在dribbble闲逛的时候发现的一个有意思的星球运动的动画,刚好最近时间尚可,就简单实现了一下中间运动的部分,又是因为时间的原因,开头位移的部分没有完成.

这是在dribbble中发现的动画

这是我自己实现的效果... 总觉得我这个星球有点胖... 因为胖所以转的慢么这是.速度等细节还有优化的余地

设计过程

老办法,先分解动画的构成.整个动画可以看做是一个自旋的星球从右上角由小变大的移动到屏幕的中央的.

星球的位移及缩放不说(其实是最近有需求,暂时没时间完善),主要完善了星球的旋转及尾部的处理.

最底层是背景的星星闪烁,每次在星球一定范围内随机出现,并缩放就好

最开始设计尾部效果的时候,是在没列中设计了两端线.再不断的运行及移动.但是实现起来很乱.最后采用了先绘制所有尾部展示的内容,然后在用和背景一样的颜色部分遮盖并移动此部分形成视觉上的效果的方法.(也可以设置PorterDuff模式来展示).设计过程中的效果如下

星球的设计,星球的本身使用简单的遮盖和贝塞尔曲线就能完成一个较为满意的星球背景.

重点是星球地表的设计以及星球自转下的地表样式的移动.解决的方法是是先绘制三个重复并连续的地表样式,通过移动整个地表样式模拟星球的转动.最后通过PorterDuff来控制展示的部分和星球的位置重合.

未开启PorterDuff模式时绘制的样式如下:

开启PorterDuff模式后再指定位置展示指定形状的图形如下:

最后再移动设置好的星球地貌就可以模拟出星球转动的效果了

代码实现

背景的星星

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或者提供建议.

android动画 行星,Android自定义View之星球运动相关推荐

  1. Android动画特效之自定义View

      Android动画特效之Animator属性动画实现_Angel-杭州的博客-CSDN博客   我在百忙之中抽出宝贵时间来实现Android动画特效,也就是Android Animator动画效果 ...

  2. Android实现雪花特效自定义view

    一.前言 这个冬天,老家一直没有下雨, 正好圣诞节,就想着制作一个下雪的特效. 圣诞祝福:平安夜,舞翩阡.雪花飘,飞满天.心与心,永相伴. 圣诞节是传统的宗教节日,对于基 督徒,那是庆祝耶稣的诞生,纪 ...

  3. Android动画效果之自定义ViewGroup添加布局动画

    Android动画效果之自定义ViewGroup添加布局动画 前言: 前面几篇文章介绍了补间动画.逐帧动画.属性动画,大部分都是针对View来实现的动画,那么该如何为了一个ViewGroup添加动画呢 ...

  4. android动画应用,Android 动画

     该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解A ...

  5. 自定义View | 仿QQ运动步数进度效果

    项目GitHub地址 思路 固定不动的蓝色大圆弧 动画变动的红色小圆弧 中间的步数文字显示 相关的自定义属性 比如固定不动的大圆弧, 我们不能写死他的蓝色颜色属性, 要提供一个颜色的自定义属性给用户自 ...

  6. 自定义View 仿QQ运动步数进度效果

    1. 概述   我记得QQ之前是有一个,运动步数的进度效果,今天打开QQ一看发现没有了.具体效果我也不清楚了,我就按照自己大概的印象写一下,这一期我们主要是熟悉Paint画笔的使用:    2. 效果 ...

  7. android动画 行星,AndroidAnimation

    Android自定义View 星球运动在dribbble闲逛的时候发现的一个有意思的星球运动的动画,刚好最近时间尚可,就简单实现了一下中间运动的部分,又是因为时间的原因,开头位移的部分没有完成.  这 ...

  8. android炫酷的自定义view,Android自定义View实现炫酷进度条

    本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: 第二步:自定义View: /** * Cre ...

  9. HenCoder Android 开发进阶:自定义 View 1-5 绘制顺序

    这期是 HenCoder 自定义绘制的第 1-5 期:绘制顺序 之前的内容在这里:  HenCoder Android 开发进阶 自定义 View 1-1 绘制基础  HenCoder Android ...

最新文章

  1. Datawhale编程——动态规划DP
  2. 最后一天,最后一刻。。。。。。情理之中,意料之外。。。。。。
  3. JAVA 串口编程 (三)
  4. not enough arguments for format string
  5. 07树莓派下的浏览器
  6. JavaScript 原型对象和原型链
  7. Liunx CentOS6编译安装LAMP
  8. 开课吧python小课学了有用吗-和年薪百万的CFO大佬聊天后,我慌了!
  9. cnblog 闪存刷星星,每一条闪存都是星星
  10. FragmentStatePagerAdapter写法
  11. 光洋触摸屏和PLC通讯错误2225
  12. div、p、span标签如何做到首行缩进两个字符
  13. python爬取奇书网小说
  14. 【寻找最佳小程序】11期:车来了——时时公交就在你身边,到站准确率可控制在90%以上...
  15. hash函数的简单介绍
  16. 家乡主题网页设计代码 旅游主题网页设计 html静态网页设计制作 dw静态网页成品模板素材网页 web前端网页设计与制作 div静态网页设计
  17. 服务器域名 一级域名_HTTP简介:域名系统服务器
  18. angular预览PDF总结
  19. 我的 小海星 要死了
  20. JVM学习第三天(JVM的执行子系统)之开篇Class类文件结构

热门文章

  1. 真实入手点评:rtx3070和rtx2080ti功耗-tx3070和rtx2080ti参数-rtx3070和rtx2080ti数据对比
  2. chrome 插件不可用_Chrome 76的新功能,现已可用
  3. python定义字符数组
  4. Mac mini内置扬声器选项不见了
  5. 钉钉解散重复群的方法
  6. Python界面编程第十四课:Pyside2 (Qt For Python)使用Calendar创建日历
  7. 知今必先通古 一起来了解网络的发展历史
  8. mac 电脑连接小米手机、魅族手机
  9. Python (13) strptime()函数
  10. module ‘idc‘ has no attribute ‘SetReg‘