用Compose在桌面上做个带粒子动画和像素字体的数字时钟

展示时间的方式一般要么是用钟表形式,要么是用数字形式,我在之前的文章中已经用过Compose实现了一个钟表的时钟,但是数字时钟还没有做过,主要是实现个数字时钟太容易,调用个时间api获取当前时间然后渲染到一个文本组件上,功能就完成了,没啥好做的,但如果在这简易的功能上做一些扩展,比如在切换时间的时候加个粒子动效的过度,比如使用像素字体来展现数字,比如让它能常驻在桌面上充当一个真正的时钟角色,那么这个数字时钟就看起来不那么单调了,下面就开始来看看如何实现这个时钟

粒子动效

整个过程比较关键的就是实现粒子动效,由于水平有限没法将这个效果做的太酷炫,只能先实现个简易版的,也就是随机在一个范围内绘制若干个粒子,首先创建个函数ParticleClock,在内部添加一个Canvas以及获取这个Canvas的大小

当画布创建出来后,就可以通过mSize来获取画布的宽高,接着再创建个变量count来表示单次绘制的粒子数量,然后创建个pointList用来保存单次需要绘制的所有粒子

SingleParticle封装着每一个粒子的xy坐标

生成一组粒子的操作也就是循环count次,每一次新建个SingleParticle,里面的xy坐标取宽高的随机值,代码如下

创建好了粒子之后,就要开始绘制了,由于我们的数字时钟采用像素字体,而每一个像素其实就是一个小的矩形,所以我们的粒子也用矩形来绘制,这样当粒子动效结束过度到数字时钟的时候就不会显的很违和,绘制代码如下

现在界面上就有五十个随机绘制的小矩形

这里稍作优化,现在每个粒子的宽高都是固定的大小,但是我们希望粒子的大小可以随着窗口的变化,粒子数量的变化而改变,所以这里需要将每个矩形的宽高更改为mSize.width/countmSize.height/count,更改后的代码以及效果图如下

现在绘制的粒子都是静态的,如果想让粒子可以动态绘制的话,就必须通过重组每次新建一个pointList并塞入新的一组粒子,这样每次绘制粒子的位置就都不一样了,为此这里需要创建一个状态变量index,然后每单位时间更改一下这个值

这里使用LaunchedEffect定时更改index值,这样index更新后,pointList内部的粒子也刷新了,我们看一下效果

更改粒子的绘制高度

大概的一个粒子动效就有了,现在需要再粒子动画的过程中逐渐改变粒子的绘制高度,直到高度的变化值达到一个定值的时候再将时钟展示出来,这个变化值就由index记录着,而定值就设置为mSize.height/3,这样当上下高度都缩小mSize.height/3后,中间剩余的mSize.height/3高度就可以用来展示时间,我们在LaunchedEffect里面更改一下代码

这里当index的值到达mSize.height/3之后,将index的值重置为0,这样变小的绘制范围又重新恢复到原样,此外由于这边的粒子绘制高度发生了变化,在给pointList赋值的时候,每一个SingleParticle的y值的取值范围也要对应的改变,代码如下

这里除了rangeY的取值范围与index绑定在了一起之外,rangeX的取值也变成了50与mSize.width.toInt() - 50之间,让粒子的绘制范围与两边留有一些空隙,那么现在粒子的绘制范围可以跟着index的变化而改变了,效果如下

显示时间

粒子的动效做的差不多了,现在要开始将时钟展现出来,要达到的效果就是当index值达到mSize.height/3的时候,隐藏粒子,展示时钟,那么这里需要一个状态值来控制这两个视图的隐藏展示

index值达到mSize.height/3的时候showNumber设置为true,并且延迟个一段时间再将showNumber变成false,那么这个延迟的时间就是时钟的展示时间,一样在LaunchedEffect中加入这些逻辑

那么现在就开始时钟的绘制,由于我们的时钟的文字使用的是像素字体,这个在之前的一篇文章中有讲过如何制作像素字体的数字,所以这里直接将那边的代码拷了一份过来,在创建像素字体的函数中添加对时间中间的冒号符号的支持

再创建一个函数ClockItem用做创建时钟上的时分秒,也就是判断下传进来的数字的位数,然后决定使用一个DigitalNumber或者是两个DigitalNumber来展示

最后在创建个表示时钟的函数ClockDigital,除了接受些必要的参数如Modifier之外,还接收当前时间对象LocalTime.now(),内部分别时候三个ClockItem代表时分秒,两个DigitalNumber表示冒号

时钟部分的代码就是这些,我们将函数ClockDigital与绘制粒子的Canvas都包在一个Box组件内部,用变量showNumber来控制展示哪个组件

整体的一个逻辑代码就完成了,来看看效果如何

可以很容易的发现个bug,也就是同一个时间它出现了两次,造成这个bug的原因主要是因为没有将粒子的动画时间控制好,理论上来说由于时间的展示时间为500毫秒,那么粒子的动画时间也应该是500毫秒,然后这个500毫秒也包含了创建数据以及渲染数据的时间,所以index从0变成大于mSize.height/3要比500毫秒小,经过调试最终index每十毫秒的增值如下所示

看下效果

窗口透明

最后一步了,现在整个数字时钟也完成了,我们需要让它摆放在桌面上某一个位置让它在那边静静地跳,要做的就是去掉边框,让窗口透明,代码如下

transparentundecorated属性是让窗口透明,alwaysOnTop是可选项,目的是让窗口永远浮在其他窗口上面,一直可见,在运行一下项目,在桌面的底部就出现了我们的数字时钟了

总结

一个带粒子动画和像素字体的数字时钟完成了,多多少少以后这个也能在桌面上当作一个时钟工具了,或者到了中午将它整成全屏直接作为一个屏保,让它在那边跳,然后你去吃饭也是可以的,这个大家有兴趣的可以自己拿去调一下样式,比如外围加个边框,或者多一些颜色搭配,这个就看个人喜好了~

相关推荐
橙子家7 小时前
浏览器缓存之【身份与会话管理】:Cookies 和 Private state tokens
前端
最新资讯动态8 小时前
HDC 2026 | 对话鲸鸿动能:存量时代,品牌如何夺回营销“主动权”?
前端
最新资讯动态8 小时前
游戏出海,从产品走向体系
前端
最新资讯动态8 小时前
20人团队跑出百万DAU、大厂也来抢量:谁在鸿蒙生态跑出加速度
前端
最新资讯动态8 小时前
千万开发者背后,鸿蒙商业化的B面
前端
爱勇宝10 小时前
AI 时代:智商决定起点,情商决定走多远
前端·ai编程
kyriewen10 小时前
用了半年 Claude Code 后,我尝试关掉它写了一周代码——结果比想象中严重
前端·javascript·ai编程
IT_陈寒11 小时前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
徐小夕12 小时前
万字拆解 JitWord:企业级实时协同文档底层架构 + 大模型 AI 融合完整实践
前端·vue.js·github
一份执念12 小时前
uni-app 小程序分包限制处理与主包体积优化实战
前端·微信小程序