Canvas定时环形绘制异常:多段同时绘制,求平滑单环方案
Canvas环形动画问题排查与解决方案
问题原因
- 循环逻辑失效:遍历
data数组时,仅保留了最后一项的myVal和myName,导致动画帧反复处理最后一个数据段,drawArc不断累加startAngle,重复绘制多段弧。 - 进度控制混乱:
newX的计算与环形实际绘制进度无关,错误用单个数据项数值作为判断条件,无法形成平滑的整体进度推进。 - 未清除画布:每帧绘制前未清除画布,导致所有绘制的弧叠加显示,视觉上出现多段混乱效果。
解决方案
- 全局进度控制:计算从0到1的动画进度值,对应环形从起始角度到完整一圈的绘制过程。
- 分段顺序绘制:按数据项顺序累计占比,根据当前进度计算每个分段的绘制范围,逐步推进环形动画。
- 每帧清屏:绘制前清除画布,确保每次只显示当前进度的环形状态。
- 修正角度计算:统一基于总占比计算每个分段的起始和结束角度,避免角度累加错误。
修正后的代码
HTML部分
<canvas id="myCanvas" width="800" height="900"></canvas>
JavaScript部分
window.requestAnimFrame = (function(callback) { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; })(); var canvas = document.getElementById('myCanvas'); var ctx = canvas.getContext('2d'); var data = ['Red,250', 'Blue,530', 'Orange,390', 'Green,190', 'Purple,450', 'Brown,600']; // 转换数据为对象数组,方便处理 var segments = data.map(item => { const [color, value] = item.split(','); return { color, value: parseInt(value) }; }); var myTotal = segments.reduce((sum, seg) => sum + seg.value, 0); const centerX = 100; const centerY = 100; const radius = canvas.height / 8; const lineWidth = 2.5; const startAngle = -Math.PI / 2; // 从顶部开始 // 动画参数 const duration = 3000; // 动画总时长(毫秒) let startTime; function drawCurrentProgress(progress) { ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除画布 let currentAngle = startAngle; const totalProgress = Math.min(progress, 1); // 限制进度不超过1 let accumulatedValue = 0; for (let seg of segments) { const segRatio = seg.value / myTotal; const segProgress = Math.min(totalProgress - accumulatedValue / myTotal, segRatio); if (segProgress > 0) { const endAngle = currentAngle + 2 * Math.PI * segProgress; ctx.beginPath(); ctx.arc(centerX, centerY, radius, currentAngle, endAngle, false); ctx.lineWidth = lineWidth; ctx.strokeStyle = seg.color; ctx.stroke(); currentAngle = endAngle; } accumulatedValue += seg.value; if (accumulatedValue / myTotal >= totalProgress) break; } } function animate() { const elapsed = Date.now() - startTime; const progress = elapsed / duration; drawCurrentProgress(progress); if (progress < 1) { requestAnimFrame(animate); } } // 启动动画 setTimeout(() => { startTime = Date.now(); animate(); }, 100);
内容的提问来源于stack exchange,提问作者Hannington Mambo
相关产品推荐
相关产品推荐

