气泡图
这里做的气泡图是柱形图的一种变体形式,但是基本思想其实差不多,只不过柱形的高矮换成的半径的大小,当然圆心的坐标也要随之改变,圆心的纵坐标要加上跟半径同比例的缩放系数才能保证圆的底部都在同一条水平线上
图一
function ybChart(data, index) {
var dataMax = 0
for (let i = 0; i < data.length; i++) {
data[i].riskNumber = parseFloat(data[i].riskNumber)
if (data[i].riskNumber >= dataMax) {
dataMax = data[i].riskNumber
}
}
var canvas = document.querySelector("#cav7");
var zeroPoint = [0, canvas.clientHeight - 10]; //坐标轴0点坐标
var cav = canvas.getContext("2d");
cav.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); //绘制前先清空画布
var canvasBGHeight = canvas.clientHeight - 20; //背景高度
var startRowLinePosition = canvas.clientWidth / data.length/2; //圆球底横X坐标
var circlePointX = canvas.clientWidth / data.length/2; //圆球圆心标准坐标X
var circlePointY = canvas.clientHeight * 3 / 5; //圆球圆心标准坐标Y
var circleRadio = canvas.clientWidth/data.length/2; //圆球标准半径
//画布背景
cav.drawImage(
ybBG,
0,
10,
canvas.clientWidth,
canvasBGHeight
);
for (let i = 0; i < data.length; i++) {
//圆球底横
cav.beginPath();
cav.moveTo(startRowLinePosition - 10, zeroPoint[1] - 3);
cav.lineTo(startRowLinePosition + 10, zeroPoint[1] - 3);
cav.lineWidth = 3;
cav.setLineDash([1, 0]);
cav.strokeStyle = "#fff";
cav.stroke();
// 绘制圆球
var normalBall = cav.createLinearGradient(
circlePointX - (circleRadio * data[i].riskNumber) / dataMax,
circlePointY + circleRadio - (circleRadio * data[i].riskNumber) / dataMax,
circlePointX + (circleRadio * data[i].riskNumber) / dataMax,
circlePointY + circleRadio - (circleRadio * data[i].riskNumber) / dataMax
);
normalBall.addColorStop(0, "#5128FF");
normalBall.addColorStop(1, "#9F77FF");
var lightHighBall = cav.createLinearGradient(
circlePointX - (circleRadio * data[i].riskNumber) / dataMax,
circlePointY + circleRadio - (circleRadio * data[i].riskNumber) / dataMax,
circlePointX + (circleRadio * data[i].riskNumber) / dataMax,
circlePointY + circleRadio - (circleRadio * data[i].riskNumber) / dataMax
);
lightHighBall.addColorStop(0, "#B444FC");
lightHighBall.addColorStop(1, "#F14B6F");
var circleRadioYPosition =
circlePointY + circleRadio - (circleRadio * data[i].riskNumber) / dataMax;
cav.beginPath();
cav.arc(
circlePointX,
circleRadioYPosition,
(circleRadio * data[i].riskNumber) / dataMax,
0,
Math.PI * 2,
false
);
cav.fillStyle = i == index ? lightHighBall : normalBall;
cav.fill();
//圆球底竖
if (i == index) {
cav.beginPath();
cav.moveTo(startRowLinePosition, zeroPoint[1] - 3);
cav.lineTo(startRowLinePosition, 10);
cav.lineWidth = 2;
cav.setLineDash([3, 2]);
cav.strokeStyle = "#fff";
cav.stroke();
//圆球中横线
cav.beginPath();
cav.moveTo(0, circleRadioYPosition);
cav.lineTo(canvas.clientWidth, circleRadioYPosition);
cav.lineWidth = 2;
cav.setLineDash([3, 2]);
cav.strokeStyle = "#fff";
cav.stroke();
//绘制交汇圆球
cav.beginPath();
cav.arc(circlePointX, circleRadioYPosition, 4, 0, Math.PI * 2, false);
cav.fillStyle = "#fff";
cav.fill();
//圆球外环
cav.beginPath();
cav.arc(
circlePointX,
circleRadioYPosition,
(circleRadio * data[i].riskNumber) / dataMax + 5,
0,
Math.PI * 2,
false
);
cav.strokeStyle = "#fff";
cav.lineWidth = 1;
cav.setLineDash([1, 0]);
cav.stroke();
if (
circleRadioYPosition + floatBG.height >=
canvasBGHeight
) {
var floatBGPositionY =
circleRadioYPosition - floatBG.height;
} else {
var floatBGPositionY =
circleRadioYPosition - floatBG.height / 2;
}
var floatBGWidth =
(floatBG.width * data[i].riskName.length) / 10 + 20;
if (index < 3) {
var floatBGPositionX = circlePointX + 5;
} else {
var floatBGPositionX = circlePointX - floatBGWidth - 5;
}
var floatLineColor = cav.createLinearGradient(
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 10,
floatBGPositionX + floatBGWidth + 5,
floatBGPositionY + floatBG.height / 2 - 10
);
floatLineColor.addColorStop(0, "rgba(255, 255, 255,0)");
floatLineColor.addColorStop(1, "rgba(255, 255, 255,1)");
} else {
cav.beginPath();
cav.moveTo(startRowLinePosition, zeroPoint[1] - 3);
cav.lineTo(startRowLinePosition, zeroPoint[1] - 70);
cav.lineWidth = 2;
cav.setLineDash([1, 0]);
cav.strokeStyle = "#fff";
cav.stroke();
}
circlePointX += canvas.clientWidth / data.length;
startRowLinePosition += canvas.clientWidth / data.length;
}
//弹出框
cav.drawImage(
floatBG,
floatBGPositionX,
floatBGPositionY,
floatBGWidth,
80
);
//弹框分割线
cav.beginPath();
cav.moveTo(
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 10
);
cav.lineTo(
floatBGPositionX + floatBGWidth - 10,
floatBGPositionY + floatBG.height / 2 - 10
);
cav.setLineDash([2, 1]);
cav.strokeStyle = floatLineColor;
cav.stroke();
//弹框文字
cav.textAlign = "start";
cav.textBaseline = "bottom";
cav.font = "16px MeicrosoftYahei";
cav.fillStyle = "#fff";
cav.fillText(
data[index].riskName,
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 15
);
cav.textAlign = "start";
cav.textBaseline = "top";
cav.font = "bold 36px Akrobat-Black";
cav.fillStyle = "#fff";
cav.fillText(
data[index].riskNumber + "%",
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 5
);
}
图二
这个图标跟上面那个其实差不多,只不过这里的圆心Y坐标的计算公式不一样
画布高度+圆心半径*数据比值-数据比值*画布高度+顶部预留高度
JS
function floatBallChart(data, index) {
var dataMax = 0
for (let i = 0; i < data.length; i++) {
data[i].riskNumber = parseFloat(data[i].riskNumber)
if (data[i].riskNumber >= dataMax) {
dataMax = data[i].riskNumber
}
}
var canvas = document.querySelector("#cav10");
var zeroPoint = [0, canvas.clientHeight - 10]; //坐标轴0点坐标
var cav = canvas.getContext("2d");
cav.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); //绘制前先清空画布
var canvasBGHeight = canvas.clientHeight - 20; //背景高度
var startRowLinePosition = canvas.clientWidth / data.length*3/4+20; //圆球底横X坐标
var circlePointX = canvas.clientWidth / data.length*3/4+20; //圆球圆心标准坐标X
var circleRadio = canvas.clientWidth/data.length-10 //圆球标准半径
//画布背景
cav.drawImage(
ybBG,
0,
10,
canvas.clientWidth,
canvasBGHeight
);
for (let i = 0; i < data.length; i++) {
// 绘制圆球
//圆球圆心纵坐标
var circleRadioYPosition =
canvasBGHeight+circleRadio*data[i].riskNumber / dataMax - data[i].riskNumber / dataMax*canvasBGHeight+20;
var normalBall = cav.createLinearGradient(
circlePointX - (circleRadio * data[i].riskNumber) / dataMax,
circleRadioYPosition,
circlePointX + (circleRadio * data[i].riskNumber) / dataMax,
circleRadioYPosition
);
normalBall.addColorStop(0, "#9F77FF");
normalBall.addColorStop(1, "#9F77FF");
var lightHighBall = cav.createLinearGradient(
circlePointX - (circleRadio * data[i].riskNumber) / dataMax,
circleRadioYPosition,
circlePointX + (circleRadio * data[i].riskNumber) / dataMax,
circleRadioYPosition
);
lightHighBall.addColorStop(0, "#B444FC");
lightHighBall.addColorStop(1, "#F14B6F");
cav.beginPath();
cav.arc(
circlePointX,
circleRadioYPosition,
(circleRadio * data[i].riskNumber) / dataMax,
0,
Math.PI * 2,
false
);
cav.fillStyle = i == index ? 'rgba(222, 72, 154,0.3)' : 'rgba(119, 78, 255,0.3)';
cav.fill();
//圆球底竖
if (i == index) {
cav.beginPath();
cav.moveTo(startRowLinePosition, zeroPoint[1] - 3);
cav.lineTo(startRowLinePosition, 10);
cav.lineWidth = 2;
cav.setLineDash([3, 2]);
cav.strokeStyle = "#fff";
cav.stroke();
//圆球中横线
cav.beginPath();
cav.moveTo(0, circleRadioYPosition);
cav.lineTo(canvas.clientWidth, circleRadioYPosition);
cav.lineWidth = 2;
cav.setLineDash([3, 2]);
cav.strokeStyle = "#fff";
cav.stroke();
//绘制交汇圆球
cav.beginPath();
cav.arc(circlePointX, circleRadioYPosition, 4, 0, Math.PI * 2, false);
cav.fillStyle = "#fff";
cav.fill();
//圆球外环
cav.beginPath();
cav.arc(
circlePointX,
circleRadioYPosition,
(circleRadio * data[i].riskNumber) / dataMax + 5,
0,
Math.PI * 2,
false
);
cav.strokeStyle = "#fff";
cav.lineWidth = 1;
cav.setLineDash([1, 0]);
cav.stroke();
if (
circleRadioYPosition + floatBG.height >=
canvasBGHeight
) {
var floatBGPositionY =
circleRadioYPosition - floatBG.height;
} else {
var floatBGPositionY =
circleRadioYPosition - floatBG.height / 2;
}
var floatBGWidth =
(floatBG.width * data[i].riskName.length) / 10 + 20;
if (index < 3) {
var floatBGPositionX = circlePointX + 5;
} else {
var floatBGPositionX = circlePointX - floatBGWidth - 5;
}
var floatLineColor = cav.createLinearGradient(
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 10,
floatBGPositionX + floatBGWidth + 5,
floatBGPositionY + floatBG.height / 2 - 10
);
floatLineColor.addColorStop(0, "rgba(255, 255, 255,0)");
floatLineColor.addColorStop(1, "rgba(255, 255, 255,1)");
}
circlePointX += canvas.clientWidth / data.length*3/4;
startRowLinePosition += canvas.clientWidth / data.length*3/4;
}
//弹出框
cav.drawImage(
floatBG,
floatBGPositionX,
floatBGPositionY,
floatBGWidth,
80
);
//弹框分割线
cav.beginPath();
cav.moveTo(
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 10
);
cav.lineTo(
floatBGPositionX + floatBGWidth - 10,
floatBGPositionY + floatBG.height / 2 - 10
);
cav.setLineDash([2, 1]);
cav.strokeStyle = floatLineColor;
cav.stroke();
//弹框文字
cav.textAlign = "start";
cav.textBaseline = "bottom";
cav.font = "16px MeicrosoftYahei";
cav.fillStyle = "#fff";
cav.fillText(
data[index].riskName,
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 15
);
cav.textAlign = "start";
cav.textBaseline = "top";
cav.font = "bold 36px Akrobat-Black";
cav.fillStyle = "#fff";
cav.fillText(
data[index].riskNumber + "%",
floatBGPositionX + 10,
floatBGPositionY + floatBG.height / 2 - 5
);
}