如何使用Java Swing制作支持自定义值的滚动动画数字计数器
Swing 平台滚轮式动画数字计数器实现指南
核心实现逻辑
完全匹配垂直滚动数字、无生硬跳变的效果,不需要引入第三方依赖,基于Swing原生能力即可搭建,整体逻辑分为三层:
- 动画驱动层:使用Swing内置的
javax.swing.Timer作为动画帧触发源,按60fps(每16ms一帧)的频率触发UI更新,所有操作都在事件 dispatch 线程(EDT)执行,从根源上避免Swing常见的UI线程安全问题。 - 视觉渲染层:把显示的数字拆分为独立的数位滚轮,每个数位单独渲染垂直排列的0-9数字序列,通过调整垂直偏移量实现滚动效果,替代直接替换文本的生硬跳变。
- 数值控制层:对外暴露统一的
setTargetValue()公开方法,调用时自动记录当前旧值、计算新旧值的差值、启动对应时长的滚动动画,直到数值稳定到目标值后自动停止动画释放资源。
关键实现要点
- 动画插值:对滚动进度应用ease-out 缓动函数,实现滚动启动快、接近目标值时逐渐减速的效果,和示例里的机械滚轮手感一致,比线性匀速滚动的观感自然很多。
- 数位自适应:根据目标数值的位数动态增减内部的数位滚轮数量,比如从99变到100时自动补出百位的滚轮,不需要手动固定最大显示位数。
- 边界裁剪:给每个数位滚轮设置固定的可视区域大小,渲染时只显示当前偏移量对应的单个数字,避免滚动过程中相邻数字溢出到可视区造成视觉混乱。
- 样式统一:使用等宽字体渲染所有数字,保证每个数位的宽度固定,不会因为数字本身的宽度差异导致布局抖动。
核心参考代码片段
// 60fps 动画定时器,所有回调运行在EDT线程 Timer rollTimer = new Timer(16, e -> { // 计算当前动画进度,范围0~1 float progress = Math.min(currentFrame / totalFrames, 1.0f); // 三次方缓动,实现滚动到目标值时逐渐减速的效果 float easedProgress = 1 - (float)Math.pow(1 - progress, 3); // 更新每个滚轮的垂直偏移并触发重绘 rollerList.forEach(roller -> roller.updateOffset(easedProgress)); repaint(); // 动画播放完成后停止定时器,避免资源空耗 if (progress >= 1) { ((Timer) e.getSource()).stop(); } });
常见踩坑提醒
- 不要在自定义业务线程中直接修改UI组件状态,更不要在EDT中使用
Thread.sleep()做帧延时,会直接导致整个Swing界面卡死无响应。 - 绘制数字时开启文本抗锯齿,避免滚动过程中数字边缘发虚、出现锯齿残影。
- 提前为数字设置合适的上下留白,不然滚动过程中数字的上下边缘会被可视区域裁切,影响观感。
内容的提问来源于stack exchange,提问作者Chauncey Lee
相关产品推荐
相关产品推荐

