SwiftUI中如何实现小球沿圆环路径的动画效果?
实现环形进度条的跟随小圆动画
核心思路
不需要复杂的曲线动画API,核心是通过进度百分比计算对应角度,再将小圆位置映射到环形路径的坐标上,同步进度更新即可实现跟随动画。以下分两种常见场景给出实现方案:
场景1:Canvas绘制方案
假设你当前用Canvas实现环形进度条,直接在绘制逻辑中添加小圆的计算与绘制即可:
const canvas = document.getElementById('progress-canvas'); const ctx = canvas.getContext('2d'); const centerX = canvas.width / 2; const centerY = canvas.height / 2; const ringRadius = 80; // 进度环的半径(从圆心到环中线的距离) const ringWidth = 10; // 进度环的宽度 const dotRadius = 7; // 跟随小圆的半径 let percent = 0; // 当前进度(0~1) function drawProgress() { // 清空画布 ctx.clearRect(0, 0, canvas.width, canvas.height); // 1. 绘制灰色背景环 ctx.beginPath(); ctx.arc(centerX, centerY, ringRadius, 0, Math.PI * 2); ctx.strokeStyle = '#e0e0e0'; ctx.lineWidth = ringWidth; ctx.stroke(); // 2. 绘制红色进度环(InnerRing) const startAngle = -Math.PI / 2; // 起始角度:顶部(Canvas默认0度为右侧,顺时针旋转) const endAngle = startAngle + percent * Math.PI * 2; ctx.beginPath(); ctx.arc(centerX, centerY, ringRadius, startAngle, endAngle); ctx.strokeStyle = '#ff3333'; ctx.lineWidth = ringWidth; ctx.stroke(); // 3. 绘制跟随小圆 // 小圆的角度与进度环的结束角度完全一致 const dotAngle = endAngle; // 计算小圆的中心坐标 const dotX = centerX + ringRadius * Math.cos(dotAngle); const dotY = centerY + ringRadius * Math.sin(dotAngle); ctx.beginPath(); ctx.arc(dotX, dotY, dotRadius, 0, Math.PI * 2); ctx.fillStyle = '#ff3333'; ctx.fill(); // 驱动动画:逐步增加进度 if (percent < 1) { percent += 0.008; requestAnimationFrame(drawProgress); } } // 启动动画 drawProgress();
关键细节
- 起始角度用
-Math.PI/2对应Canvas坐标系的顶部,确保进度从顶部开始 - 小圆坐标直接复用进度环的结束角度,完全贴合进度末端
- 用
requestAnimationFrame保证动画平滑性
场景2:DOM+CSS方案
如果你的环形进度条是用DOM元素实现的,通过CSS变量和transform即可实现小圆跟随:
<div class="progress-wrap"> <div class="bg-ring"></div> <div class="inner-ring" style="--progress: 0%"></div> <div class="follow-dot" style="--progress: 0%"></div> </div> <style> .progress-wrap { position: relative; width: 200px; height: 200px; } /* 灰色背景环 */ .bg-ring { width: 100%; height: 100%; border: 10px solid #e0e0e0; border-radius: 50%; box-sizing: border-box; } /* 红色进度环 */ .inner-ring { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 10px solid transparent; border-top-color: #ff3333; border-radius: 50%; box-sizing: border-box; /* 通过CSS变量控制旋转角度:1%对应3.6deg,减去90deg让起始点在顶部 */ transform: rotate(calc(var(--progress) * 3.6deg - 90deg)); transition: transform 0.2s linear; } /* 跟随小圆 */ .follow-dot { position: absolute; width: 14px; height: 14px; border-radius: 50%; background: #ff3333; top: 50%; left: 50%; /* 三步transform实现环形路径跟随: 1. 居中定位 2. 旋转到进度角度 3. 平移到环形路径上 4. 反向旋转保持小圆正立(可选) */ transform: translate(-50%, -50%) rotate(calc(var(--progress) * 3.6deg - 90deg)) translate(85px) rotate(calc(-(var(--progress) * 3.6deg - 90deg))); transition: transform 0.2s linear; } </style> <script> // 动画逻辑:逐步更新进度值 let progress = 0; const innerRing = document.querySelector('.inner-ring'); const dot = document.querySelector('.follow-dot'); function updateProgress() { if (progress <= 100) { innerRing.style.setProperty('--progress', progress); dot.style.setProperty('--progress', progress); progress += 1; setTimeout(updateProgress, 30); } } updateProgress(); </script>
关键细节
- 用CSS变量
--progress统一控制进度环和小圆的动画参数 - 平移距离
85px是根据容器尺寸计算的:容器宽200px,边框10px,环形中线半径为(200-20)/2=90px,减去小圆半径7px得到85px - 最后一步反向旋转可以让小圆始终保持正立,不需要的话可以去掉
内容的提问来源于stack exchange,提问作者Neglected Sanity
相关产品推荐
相关产品推荐

