如图所示,需求就是这样,于是就是受挫了一个,
先这样,然后在哪样,最好就是这样了
思路:
1、根据数据计算出最大的圆,然后固定坐标在中间靠左些
2、其他圆由画布中心向四周随机发散分布,位置随机
3、圆不能重叠,也不能超出画布
4、开始用canvas画圆
// 检查是否发生重叠
isOverlapping(x, y, radius) {
for (let bubble of this.bubbles) {
const distance = Math.sqrt(Math.pow(x - bubble.x, 2) + Math.pow(y - bubble.y, 2));
if (distance < (radius + bubble.radius)) {
return true; // 如果距离小于两个半径之和,表示重叠
}
}
return false;
},
// 检查圆是否超出画布
isOutOfBounds(x, y, radius) {
return (
x - radius < 0 || // 左边界
x + radius > this.canvasWidth || // 右边界
y - radius < 0 || // 上边界
y + radius > this.canvasHeight // 下边界
);
},
// 绘制气泡
drawBubbles(data) {
const canvas = this.$refs.bubbleCanvas;
const ctx = canvas.getContext("2d");
// 清空画布
ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
const maxValue = Math.max(...data.map(d => d.value));
const centerX = this.canvasWidth / 2;
const centerY = this.canvasHeight / 2;
const maxRadiusOffset = Math.min(this.canvasWidth, this.canvasHeight) / 4;
// 按value从大到小排序
const sortedData = data.sort((a, b) => b.value - a.value);
this.bubbles = []; // 清空气泡信息,重新生成
// 绘制所有圆
sortedData.forEach((bubbleData, index) => {
const radius = (bubbleData.value / maxValue) * (this.canvasWidth / 6);
let x, y;
if (index === 0) {
// 最大圆居中
x = centerX - radius;
y = centerY;
} else {
let attempts = 0;
do {
const angle = Math.random() * 2 * Math.PI;
const distanceFromCenter = Math.random() * maxRadiusOffset + radius;
x = centerX + distanceFromCenter * Math.cos(angle);
y = centerY + distanceFromCenter * Math.sin(angle);
attempts++;
if (attempts > 100) break;
} while (this.isOverlapping(x, y, radius) || this.isOutOfBounds(x, y, radius));
if (attempts > 100) return;
}
// 保存每个气泡的数据
this.bubbles.push({x, y, radius, data: bubbleData, color: this.getRandomColor()});
// 绘制气泡
this.drawBubble(ctx, x, y, radius, this.bubbles[this.bubbles.length - 1].color, bubbleData.label, bubbleData.value);
});
// 绘制图例
this.drawLegend(ctx);
},
// 绘制单个气泡
drawBubble(ctx, x, y, radius, color, label, value) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = color;
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = '#ffffff';
ctx.stroke();
ctx.fillStyle = "#ffffff";
ctx.font = "bold 14px Arial";
ctx.textAlign = "center";
ctx.fillText(label, x, y - 10);
ctx.fillText(value, x, y + 10);
},
// 绘制图例
drawLegend(ctx) {
const legendX = this.canvasWidth - 120;
const legendY = 20;
const boxSize = 20;
const spacing = 10;
ctx.font = "14px Arial";
ctx.textAlign = "left";
this.bubbles.forEach((bubble, index) => {
// 绘制颜色矩形
ctx.fillStyle = bubble.color;
ctx.fillRect(legendX, legendY + index * (boxSize + spacing), boxSize, boxSize);
// 绘制文本
ctx.fillStyle = "#000000";
ctx.fillText(bubble.data.label, legendX + boxSize + 10, legendY + index * (boxSize + spacing) + 15);
});
}