<canvas id="cv"></canvas>
dataX = [1,2,3,4,5];//x轴数据
dataY = [50,84,67,21,94];//y轴数据
reportIdList = [01,02,03,04,05];//点击获取到的小圆圈的id数据
meanValue= 20;//标准线
xaxis= 28;//天数
var cv = document.getElementById("cv");
if (cv.width < window.innerWidth){
cv.width = window.innerWidth-300;
}else{
cv.width=917
}
cv.height = 500;
var ctx = cv.getContext("2d");
//2、获取画布的宽度和高度
const WIDTH = cv.width;
const HEIGHT = cv.height;
//定义坐标轴相对于画布的内边距
var padding = 20;//初始化内边距
var paddingLeft = 50;// 至少大于绘制文字的宽度
var paddingBottom = 30;// 至少大于绘制文字的高度
//x轴的天数
var month = {
x: paddingLeft,
y: cv.height - paddingBottom + 5
};
var origin = { // 原点坐标值(x轴与y轴交叉点)
x: paddingLeft,
y: HEIGHT - paddingBottom
};
var axisX = { // X轴的起点坐标值
x: WIDTH - padding,
y: HEIGHT - paddingBottom
};
var axisY = { // y轴的起点坐标值
x: paddingLeft,
y: padding-10
};
//封装一个折线图的函数
var arrowWidth = 10; //箭头的宽度
var xLength = cv.width - 2*padding - arrowWidth; //x轴的长度
ctx.beginPath();//控制绘制的折线不受坐标轴样式属性的影响
//绘制坐标轴
ctx.beginPath();
ctx.moveTo(axisY.x, axisY.y);
ctx.lineTo(origin.x, origin.y);
ctx.lineTo(axisX.x, axisX.y);
ctx.strokeStyle = '#888A92'
ctx.stroke();
//绘制坐标轴的箭头
ctx.beginPath();
ctx.moveTo(axisY.x - 5, axisY.y + 10);
ctx.lineTo(axisY.x, axisY.y);
ctx.lineTo(axisY.x + 5, axisY.y + 10);
ctx.strokeStyle = '#888A92'
ctx.stroke();
ctx.beginPath();
ctx.moveTo(axisX.x - 10, axisX.y - 5);
ctx.lineTo(axisX.x, axisX.y);
ctx.lineTo(axisX.x - 10, axisX.y + 5);
ctx.strokeStyle = '#888A92'
ctx.stroke();
for (var i = 0; i <= xaxis; i++) {
ctx.textBaseline = "top";
ctx.fillText(i, month.x, month.y);
month.x += (axisX.x - origin.x) / (xaxis+1);
}
//绘制y轴
var max = Math.max.apply(Math, dataY);
if(max<meanValue) max=meanValue
let c=parseInt(max / 10)
var axisVal = (origin.y - axisY.y) / (max /c + 1);
var money = {
x: axisY.x - 5,
y: axisY.y + axisVal-13,
jin: max
};
// 遍历最高值/间隔
ctx.textAlign = "right";
for (var i = 0; i < 11; i++) {
ctx.fillText(parseInt(money.jin), money.x, money.y);
money.y += axisVal;
money.jin -= c;
}
//中断(坐标轴和折线的)连接
ctx.stroke();
ctx.beginPath();
for(let i = 0; i <dataX.length; i++){
//x轴的坐标
let pointX;
pointX = (cv.width - 2*padding - 20)/(xaxis+1)*dataX[i]+padding+(axisX.x - origin.x) / (xaxis+1);
//y轴的坐标
let pointY = origin.y - (origin.y - (axisY.y + axisVal)) * dataY[i] / max-10;
ctx.textBaseline = "bottom"
ctx.textAlign = "center"
if (i === 0) {
ctx.moveTo(pointX, pointY);
} else {
ctx.lineTo(pointX, pointY);
}
ctx.strokeStyle = '#409EFF';
//绘制线
ctx.fillText(dataY[i], pointX, pointY,'red');
}
ctx.stroke();
//绘制小圆点
for(let i = 0; i <dataX.length; i++){
//x轴的坐标
let pointX;
pointX = (cv.width - 2*padding - 20)/(xaxis+1)*dataX[i]+padding+(axisX.x - origin.x) / (xaxis+1);
//y轴的坐标
let pointY = origin.y - (origin.y - (axisY.y + axisVal)) * dataY[i] / max-10;
ctx.beginPath();
ctx.arc(pointX, pointY,3,0,2*Math.PI);
if(dataY[i]<meanValue){
ctx.strokeStyle="#000";
ctx.fillStyle="#FF3D3D";//填充颜色,默认是黑色
}else{
ctx.strokeStyle="#000";
ctx.fillStyle="#3DABFF";//填充颜色,默认是黑色
}
ctx.fill();//画实心圆
ctx.closePath();
}
//设置节点数据
for (let i = 0; i < (dataX.length); i++) {
//x轴的坐标
let pointX = (cv.width - 2*padding-20 )/(xaxis+1)*dataX[i]+padding+(axisX.x - origin.x) / (xaxis+1)
//y轴的坐标
let pointY = origin.y - (origin.y - (axisY.y + axisVal)) * dataY[i] / max-15;
ctx.beginPath();
let xs;
xs=pointX
drawText(dataY[i],xs, pointY, 'normal 12px MicrosoftYaHei', '#3DABFF');
ctx.fill();
}
ctx.stroke(); // 进行绘制
if(meanValue){
// 线
let y=origin.y - (origin.y - (axisY.y + axisVal)) * meanValue / max-10
ctx.beginPath(); // 开始绘制
ctx.lineWidth="2"; // 指定线条宽度
ctx.strokeStyle="#FF4949"; // 指定线条颜色
ctx.moveTo(50,y); // 指定线条的起始点
ctx.lineTo(878,y);// 指定线条的结束点
ctx.stroke(); // 进行绘制
drawText(meanValue, cv.width-20, y+5, 'normal 12px MicrosoftYaHei', '#FF4949');
}
drawText('天数', cv.width/2, cv.height, 'normal 12px MicrosoftYaHei', '#000');
// 文字
let name='抗压强度(MPa)'; // 文本内容
let xs = 15;
let ys=cv.height/3; // 文字开始的坐标
let letterSpacing = 5; // 设置字间距
for(let i = 0; i < name.length; i++){
const str = name.slice(i,i+1).toString();
if(str.match(/[A-Za-z0-9]/)&&(ys<576)){ // 非汉字 旋转
ctx.save();
ctx.translate(xs+5,ys)
ctx.rotate(Math.PI/180*90);
ctx.textBaseline = 'top';
drawText(str,0,0,'normal 12px MicrosoftYaHei','#000');
ctx.restore();
ys+=ctx.measureText(str).width+letterSpacing; // 计算文字宽度
}else if(str.match(/[\u4E00-\u9FA5]/)&&(ys<576)){
ctx.save();
ctx.textBaseline = 'top';
drawText(str,xs,ys,'normal 12px MicrosoftYaHei','#000');
ctx.restore();
ys+=ctx.measureText(str).width+letterSpacing; // 计算文字宽度
}
}
// 文字公共方法
function drawText(text, x, y, font, color) {
ctx.font= font;
ctx.fillStyle= color;
ctx.fillText(text, x, y);
}
// 点击小圆点事件
cv.addEventListener('click', (event)=>{
let canvas = cv ;
//获取用户大大鼠标点击的点位坐标
var p = {};
p.x = event.clientX - canvas.getBoundingClientRect().left-32;
p.y = event.clientY - canvas.getBoundingClientRect().top;//为了兼容IE
var active='';
// 求点到圆心的距离,用到了勾股定理
for(let i = 0; i <dataX.length; i++){
//x轴的坐标
let pointX = (cv.width - 2*padding-arrowWidth)/(xaxis+1)*dataX[i]+10;
//y轴的坐标
let pointY = origin.y - (origin.y - (axisY.y + axisVal)) * dataY[i] / max-15;
var dis = Math.sqrt((p.x - pointX) * (p.x - pointX) + (p.y - pointY) * (p.y - pointY));//Math.sqrt()求平方跟
if((i==0 && dis<=15) || (i!=0 && dis <= 6)){
active=i
this.activeArc(active)
}
}
}, false)
//点击小圆点事件
activeArc(idx){
},
参考文章:canvas画折线图
相比原文,本文增加了Y轴均分以及点击小圆点事件,并且天和天之间可以有多个数据