同事问我为什么电脑屏幕上会有那么多球在飘
记得以前用的Windows电脑里面,有一个屏保程序就是在屏幕上出现很多飘来飘去的球,当球碰到电脑边缘的时候,会反弹到相反的方向,然后最近就琢磨着能不能使用Compose DeskTop也实现一个这样的效果,那以后我的Mac屏幕上也能出现好多小球,那简直是泰裤辣~
设计思路
我们把整体动效拆分一下总共有五步,每一步都不是很难
- 第一步:使用循环动画不断改变小球位移的x坐标与y坐标,x坐标的变化范围是0到窗口宽度的最大值,y坐标的变化范围是0到窗口高度的最大值
- 第二步:判断当x,y坐标到达自己的最大值的边界值的时候,将各自的变化范围的初始值与最终值互相对换一下,达到往相反方向移动的效果
- 第三步:通过改变
tween
函数的durationMillis
和easing
属性,来改变小球的位移速度与位移路线- 第四步:将小球的动画需要的属性作为函数的入参,达到可以在上层定制小球动画的效果
- 第五步:将窗口的宽度与高度更改成屏幕的宽高,背景色改成透明
让球动起来
首先我们来把球的样式做出来,球本身就是个圆形,我们使用Surface
组件就可以完成,里面再包一个Box
组件,这样做的目的是因为Surface
没有办法设置渐变的背景色,我们如果想要让圆形看起来立体一些,就需要让背景色带点渐变,所以渐变的工作就交给里面的Box
组件来完成
然后就可以把这个球放到我们的窗口里面去了,在这之前我们先创建三个常量,分别是窗口的宽高最大值以及小球的大小
然后把这三个常量分别设置给Window
组件以及ball
组件,代码与效果就如下图所示
接下去就是让这个球动起来了,我们通过改变球的位移坐标来实现球体的移动,这里给位移坐标的x,y分别设置一个无限循环动画,动画的初始值是为0,目标值为窗口的宽高,动画时间设置为5秒,然后让这个动画过程线性改变,实现过程如下所示
我们给Surface组件添加了offset
操作符,让它接收mainx
与mainy
的变化值,我们这个球就动起来了
改变位移方向
现在已经让球动起来了,接下来就是要考虑如何让球“碰壁”以后反弹,由于我们的初始位置在窗口左上角,所以我们可以先做碰到下面以后的反弹以及碰到右边以后的反弹,也就是当x坐标到达或者接近x轴位移的最大值,或者y坐标到达或者接近y轴的位移最大值以后,我们将mainx
与mainy
的初始值与目标值对调一下,这样就能往相反方向移动了,注意这里说的是位移最大值,不是窗口的宽高,因为球位移坐标是从球的左上角开始计算的,当碰到窗口边界的时候,其实位移距离是窗口的宽高减去小球的直径大小,所以我们再加上两个常量作为位移的最大值,方便后面计算时使用
然后如果想要在无限循环动画里面改变初始值与目标值,我们就要使用Animatable
来切换,所以这里再创建四个Animatable
的变量,分别代表x,y轴的初始值与目标值
创建好了以后,就直接把mainx
与mainy
的初始值与目标值替换成了新建的四个Animatable
变量,这样当我们去切换它们的值以后,mainx
与mainy
的变化范围也发生了改变,而Animatable
的切换函数snapTo
是一个挂起函数,所以还需要一个协程作用域,我们这里使用rememberCoroutineScope
函数来创建,那么小球碰到窗口下边与右边的反弹代码就有了
这边判断到达边界的条件不是mainx.value.value == offsetx.value
的原因是因为通过打印日志发现,mainx
或者mainy
的变化值不会一直刚好是offsetX.value
或者offsetY.value
,所以只能把判断当两个值接近的时候当作小球移动到边界的条件,我们运行下看看反弹效果
动图上看不出来,实际效果其实达到边界时候有点细微的抖动,这也跟我们刚刚那个边界值的判断条件有关,不过也不影响功能,我们按照这个方式把碰到左边与上边的代码也加上,一个完整的球体移动动画就做好了
现在已经能够实现小球碰到窗口四周反弹的效果了,但是实现方式还是比较繁琐的,又是协程又是切换又是看边界值的,我们其实还有更简单的办法,因为不管是x轴的值还是y轴的值,它的变化范围始终在两个值之间,差别就是每次起始位置不同,那么这就是一个反复的过程,而我们这个循环动画其实就可以设置反复模式,使用repeatMode
属性,值取RepeatMode.Reverse
就可以了,我们试一下
我们看到现在我们把那四个Animatable
都去掉了,mainx
与mainy
的初始值与目标值又回到了固定值,区别就是增加了repeatMode
,现在我们在看下实现效果咋样
看起来好像差别不大,但其实碰到边界后的效果比之前要好多了,因为不用去关心那一点误差,而且也可以随意设置动画时间,之前为了让动画的变化值不要变化的太大,所以动画时间我是最小只能设置成5秒,现在的动效看起来就舒服多了
改变速度与路线
动画速度的话我们刚刚其实已经实现了,通过改变动画时间durationMills
来实现,但是由于我们的easing
设置的是LinearEasing
线性变化,所以小球的位移路线永远都是沿着一根直线移动的,我们可以通过改变easing
的值,来改变小球的位移路线,比如现在我将easing
改成FastOutSlowInEasing
得到的效果就是这样的
这球一下子就变得好像“有智商”了一样,感觉要“撞了”就马上减速,然后换个方向继续飘
属性作为参数,让小球可定制
想要定制小球动画的话,首先要确定好哪些属性可以拿出来定制,通过上面的开发,我们这个小球动画可以被定制的属性有以下几个
- ballSize:小球的大小
- ballColor:小球的颜色
- xTime:小球位移x轴上的动画时间
- yTime:小球位移y轴上的动画时间
- xAnimateEasing:小球x轴上动画的变化速度
- yAnimateEasing:小球y轴上动画的变化速度
这样子的话我们ball
函数的参数列表就如下所示
然后再将代码中的对应位置用参数来代替
我们这里把计算最大位移的步骤也移到函数里面了,这样就可以根据不同的小球大小来计算各自的位移距离,我们这个ball
函数到这里算是完成了,现在我们就可以想弄几个小球就弄几个小球了,比如我这边就弄了这么几个小球
下面就是一堆小球的效果
我们再改下小球的样式,将小球弄成背景有点透明的样子,让飘动的小球看起来像是气泡一样,改完以后的小球代码如下
然后再将调用ball
函数的地方,ballColor
的入参也改成带点透明值
如效果图所示,是不是有那么点意思了呢,现在我们进行最后一步。
将窗口透明,宽高增大为全屏
想要将窗口弄成透明的话,可以使用Window
组件的transparent
和undecorated
属性,代码如下
然后把screenWidth
与screenHeight
大小设置成全屏大小就可以了,我们使用ToolKit
来获取屏幕宽高
还差一步,因为到了这里就算screenWidth
和screenHeight
设置成全屏宽高了,但实际上所在的窗口并没有真正的全屏,它跟屏幕左边留有一点距离,然后右边延伸至屏幕外边了,所以我们需要让整个窗口居中显示,使用WindowPosition
,这个是WindowState
里面的一个参数,我们在WindowPosition
中设置成居中对齐就可以了
最终我们得到的效果就是这样的
总结
整体效果实现起来还是蛮容易的,总共代码加一块也不到一百行,感觉把Window
设置成透明以后,DeskTop开发变得好玩多了,大家有兴趣的也可以尝试下,所有元素都可以按照自己喜好来定制,去设计属于自己的屏保程序
链接:https://juejin.cn/post/7241567583504941111
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。