项目场景:
要求饼图有一个动态绘制过程,从0°开始旋转一周,一个接一个的显示每一模块,如图所示:
问题描述
一开始通过for循环写的动效效果与需求不符,通过该逻辑呈现的效果是所有模块一起出现,一起增加的效果,如下图所示:
原因分析:
问题效果代码如下:
if (this.chartConfig.canvasSpeed > 0) { //编写好的图表组件,其中动效时间大于0时 canvasSpeed(0~1)进程,0代表未开始,1代表动效结束
//动效画饼图
let sAngle = this.chartConfig.circleData[0].startAngle; //sAngle为第一个模块的初始角度
for (let i = 0; i < this.chartConfig.circleData.length; ++i) {
const dist =
this.chartConfig.circleData[i].endAngle - this.chartConfig.circleData[i].startAngle; //dist为每个模块的角度
const ring = { //饼图每一模块的一些属性(颜色,圆心位置等)
color: this.chartConfig.circleData[i].color,
x: this.chartConfig.centerX,
y: this.chartConfig.centerY,
r: this.chartConfig.radius,
sAngle: sAngle, //每一模块的起始位置
eAngle: sAngle + dist * this.chartConfig.canvasSpeed, //需要根据canvasSpeed(0~1)计算,每一模块现在正在画的角度(起始角度+每一模块角度*动效进程)
lineWidth: this.chartConfig.lineWidth,
counterclockwise: this.chartConfig.counterclockwise,
};
sAngle = ring.eAngle; //让后一模块的起始角度为前一模块的结束角度
this.$draw.drawSimpleRing(this.ctx, ring); //绘制饼图
}
}
因为该方法会直接跑完整个for循环,从而导致所有模块部分会同时一起绘制,而不会同所想要的需求一样,等一个模块画完之后再继续画下一个模块。
解决方案:
换了一种逻辑,通过while循环编写,代码如下:
if (this.chartConfig.canvasSpeed > 0) { //编写好的图表组件,其中动效时间大于0时 canvasSpeed(0~1)进程,0代表未开始,1代表动效结束
//动效画饼图
let angles = new Array(); //定义一个数组,存放每一个模块的角度
for (let i = 0; i < this.chartConfig.circleData.length; ++i) { //通过for循环将每一个模块的角度存入数组
angles.push(
this.chartConfig.circleData[i].endAngle - this.chartConfig.circleData[i].startAngle,
);
}
let angle = 0; //定义angle,为当前模块中画到的角度
let index = 0; //定义index,为模块的脚标
let start = (this.chartConfig.circleData[0].startAngle + 0.5) * 180; //定义初始角度(0°开始)(因为this.chartConfig.circleData[i].startAngle该类型数据的范围为-0.5~2,所以转换为角度需+0.5再*180)
let curAngle = ((speed - 3.8) / (35.2 - 3.8)) * 360; //定义当前旋转到的角度(speed为当前动效的时间,35.2和3.8为该场景中结束和开始的时间,通过比例换算出当前角度)
while (angle < curAngle && index < this.chartConfig.circleData.length) { //当画到的角度小于已经转到的角度,并且没超过模块总数时,执行while循环
if (index == 0) { //在画第一个模块时,初始角度为0°
start = start + 0;
} else { //在画除第一个模块时,初始角度为上一个初始角度+上一个模块的角度
start = start + angles[index - 1] * 180;
}
let end = start + angles[index] * 180; //定义结束角度为起始角度加上该模块的角度(*180是为了数据换算为角度)
angle = end > curAngle ? curAngle : end; //判断当前转到的角度是否小于该模块的结束角度,如果小于则画到当前角度,如果大于则画到结束角度(取小)
const ring = { //每一模块的属性
color: this.chartConfig.circleData[index].color,
x: this.chartConfig.centerX,
y: this.chartConfig.centerY,
r: this.chartConfig.radius,
sAngle: start / 180 - 0.5, //将开始角度换算回数据
eAngle: angle / 180 - 0.5, //将画到的角度换算回数据
lineWidth: this.chartConfig.lineWidth,
counterclockwise: this.chartConfig.counterclockwise,
};
this.$draw.drawSimpleRing(this.ctx, ring); //绘制
if (end < curAngle) { //当前转到的角度如果比结束角度大,则让index++进入下一个模块继续绘制
index++;
}
}
}
该方法用while的方式,通过判断当前角度是否超过当前模块的结束角度来控制正在绘制的模块,只有当当前模块绘制完成时,index才++,才能进入下一个循环,继续下一模块的绘制。避免了所有模块同时绘制的情况。