效果图:
此效果难点:y轴是倒着走的。 其它思想与上一篇进度条的基本一样
htm代码:
<!DOCTYPE Html>
<html>
<head>
<title>Line Chart Demo</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
</head>
<body>
<div class="leftChart">
<header>转诊人数统计(人次)</header>
<section>
<canvas id="canvas1">您的浏览器不支持canvas,请换高版本的浏览器!</canvas>
<ul>
<li><i style="background: #de6062;"></i><span>用户</span></li>
<li><i style="background: #cbdbfe;"></i><span>血压</span></li>
<li><i style="background: #537acf;"></i><span>血糖</span></li>
</ul>
</section>
</div>
<script type="text/javascript" src="chart.js"></script>
<script>
/*折线图*/
var xkedu = [
'1月','2月','3月','4月'
]
var lineColors = [
'#de6062',
'#cbdbfe',
'#537acf'
]
var multiData = {values:[
{ value0:[
{x:"1月1周",y:10},
{x:"1月2周",y:10},
{x:"1月3周",y:10},
{x:"1月4周",y:10},
{x:"2月1周",y:10},
{x:"2月2周",y:20},
{x:"2月3周",y:15},
{x:"2月4周",y:10},
{x:"3月1周",y:5},
{x:"3月2周",y:0},
{x:"3月3周",y:0},
{x:"3月4周",y:0},
{x:"3月1周",y:5},
{x:"3月2周",y:0},
{x:"3月3周",y:0},
{x:"3月4周",y:0},
]},
{ value1:[
{x:"1月1周",y:10},
{x:"1月2周",y:15},
{x:"1月3周",y:20},
{x:"1月4周",y:25},
{x:"2月1周",y:30},
{x:"2月2周",y:20},
{x:"2月3周",y:35},
{x:"2月4周",y:40},
{x:"3月1周",y:50},
{x:"3月2周",y:60},
{x:"3月3周",y:0},
{x:"3月4周",y:10},
{x:"3月1周",y:15},
{x:"3月2周",y:0},
{x:"3月3周",y:10},
{x:"3月4周",y:5},
]},
{ value2:[
{x:"1月1周",y:31},
{x:"1月2周",y:40},
{x:"1月3周",y:55},
{x:"1月4周",y:65},
{x:"2月1周",y:78},
{x:"2月2周",y:50},
{x:"2月3周",y:85},
{x:"2月4周",y:50},
{x:"3月1周",y:10},
{x:"3月2周",y:40},
{x:"3月3周",y:30},
{x:"3月4周",y:60},
{x:"3月1周",y:85},
{x:"3月2周",y:0},
{x:"3月3周",y:40},
{x:"3月4周",y:10},
]}
]
}
/*
参考地址:http://www.108js.com/article/article7/70231.html?id=1365
*@param0: canvas 的id
*@param1: json 数据
*@param2: 坐标距离画布的间隙padding
*@param3: 每条线的颜色
*@param4: 点的颜色
*@param5: 是否绘制背景线
*@param6: 是否是多条数据
*/
//先定义数据线的名字,再绘制数据
LineChart.setData("canvas1",multiData,30,lineColors,"#000",true,true,xkedu);
</script>
</body>
</html>
script:
// 折线图
var LineChart={
setData:function(canId,data,padding,lineColors,dotColor,isBg,isMultiData, xkedu){
this.lineColors = lineColors;
this.dotColor = dotColor;
this.can = document.getElementById(canId);
this.ctx = this.can.getContext("2d");
this.isBg = isBg;
this.isMultiData = isMultiData;
this.drawXY(data,0,padding,this.can,xkedu, lineColors);
},
//是否是多条数据线
isMultiData:function(data){
if(data.values.length>1){
this.isMultiData = true;
}
},
//绘制XY坐标值及坐标线 背景线
drawXY:function(data,key,padding,can,xkedu, lineColors){
this.ctx.font = '14px';
var perwidth = 30;//x 轴每一个数据占据的宽度
can.width = this.getCoordX(padding,perwidth,data.values[0].value0.length); //canvas的宽度
var maxY = this.getMax(data,0,this.isMultiData);//获得Y轴上的最大值
var ylimt = this.getYLimtByMaxData(maxY).ylimt; // y轴 刻度
var yPixel = this.getYPixel(maxY,can.height,padding, ylimt).pixel; // y轴 刻度之间的间距
can.height = yPixel * (ylimt.length - 1) + padding; // y轴图像真实的高度 解决:yPixel改变, 总体高度改变问题
var yCanMove = 8; //整体向下走 解决到顶部的问题
// // 一个x刻度对应4个值
for( var i=0;i<xkedu.length;i++ ){
var dotxlimt = this.getCoordX(padding,perwidth,2) - this.getCoordX(padding,perwidth,1); // 每个点之间横坐标的差值
var x_x = perwidth + perwidth * 4 * i + padding;// x刻度的横坐标
var x_y = can.height-padding+20 + yCanMove;
this.ctx.fillStyle = '#fff';
this.ctx.fillText(xkedu[i], x_x, x_y, 100);
}
// 每个x刻度对应着一个值
// for( var i=0;i<data.values[key]["value"+key].length;i++ ){
// ptindex = i+1;
// var x_x = this.getCoordX(padding,perwidth,ptindex);
// var x_y = can.height-padding+20;
// this.ctx.fillText(data.values[key]["value"+key][i].x,x_x,x_y,perwidth);
// }
this.ctx.fillStyle = '#fff';
this.ctx.textAlign = "right"//y轴文字靠右写
this.ctx.textBaseline = "middle";//文字的中心线的调整
for(var i=0;i<ylimt.length;i++){
// y轴坐标 数值和坐标要倒着放
var unit = this.getYLimtByMaxData(maxY).unit;
this.ctx.fillText(ylimt[i],padding-10,ylimt[ylimt.length-1-i]/unit*yPixel + yCanMove);
// 背景横线
this.ctx.lineWidth="1";
this.ctx.strokeStyle="#595e6b";
var y = ylimt[ylimt.length-1-i]/unit*yPixel + yCanMove;
this.ctx.moveTo(padding,y);
this.ctx.lineTo(perwidth * (data.values[0].value0.length -1) + padding, y);
this.ctx.lineJoin = 'round';
this.ctx.stroke();
}
this.ctx.lineWidth="1";
this.ctx.strokeStyle="#595e6b";// 刻度颜色
this.ctx.beginPath();
// 画y轴那条线
this.ctx.moveTo(padding,yCanMove);
this.ctx.lineTo(padding,can.height-padding + yCanMove);
// 画右侧闭合那条线
this.ctx.moveTo(perwidth * (data.values[0].value0.length -1) + padding, yCanMove);
this.ctx.lineTo(perwidth * (data.values[0].value0.length -1) + padding,can.height-padding + yCanMove);
this.ctx.stroke();
this.ctx.closePath();
this.drawData(data,0,padding,perwidth,yPixel,this.isMultiData,lineColors, unit, ylimt, can, yCanMove);
},
//绘制数据线和数据点
drawData:function(data,key,padding,perwidth,yPixel,isMultiData,lineColors, unit, ylimt, can, yCanMove){
if(!isMultiData){
var keystr = "value"+key;
this.ctx.beginPath();
this.ctx.lineWidth="2";
this.ctx.strokeStyle=lineColors;
/*下面绘制数据线*/
var startX = this.getCoordX(padding,perwidth,0);
this.ctx.beginPath();
this.ctx.lineWidth="3";
for( var i=0;i<data.values[key][keystr].length;i++ ){
var x = this.getCoordX(padding,perwidth,i);
// y轴坐标计算:y轴总体的高度(can.height-padding) 减去 应该在的坐标 等于 显示在图上的坐标
var y = can.height-padding - ((can.height-padding) * data.values[key][keystr][i].y / ylimt[ylimt.length -1]) + yCanMove;
this.ctx.lineTo(x,y);
this.ctx.lineJoin = 'round';
}
this.ctx.stroke();
this.ctx.closePath();
/*下面绘制数据线上的点*/
this.ctx.beginPath();
// this.ctx.fillStyle=this.dotColor;
this.ctx.fillStyle = lineColors;
for( var i=0;i<data.values[key][keystr].length;i++ ){
// 圆点x轴开始的位置
var x = this.getCoordX(padding,perwidth,i);
var y = can.height-padding - ((can.height-padding) * data.values[key][keystr][i].y / ylimt[ylimt.length -1]) + yCanMove;
this.ctx.moveTo(x,y);
this.ctx.arc(x,y,2,0,Math.PI*2,true);//绘制数据线上的点
this.ctx.fill();
}
this.ctx.closePath();
}else{//如果是多条数据线
for( var i=0;i<data.values.length;i++ ){
LineChart.drawData(data,i,padding,perwidth,yPixel,false,lineColors[i],unit, ylimt, can, yCanMove);
}
}
},
//宽度
getPixel:function(data,key,width,padding){
var count = data.values[key]["value"+key].length;
return (width-20-padding)/(count+(count-1)*1.5);
},
//横坐标X 随ptindex 获得
getCoordX:function(padding,perwidth,ptindex){//下标从1开始 不是从0开始
return perwidth*ptindex+padding;
},
//y轴的之间间距
getYPixel:function(maxY,height,padding, ylimt){
return {pixel:(height-padding)/(ylimt.length-1)};
},
// 获取y轴刻度、unit params:数据的最大值
getYLimtByMaxData: function(maxY) {
var ylimt = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// var ylimt = [0, 2, 4, 6, 8, 10];
var unit = 10;
if (maxY > 0 && maxY <= 10) {
unit = 1;
} else if(maxY <= 100) {
unit = 10;
} else if(maxY <= 1000) {
unit = 100;
} else if(maxY <= 10000) {
unit = 1000;
} else if(maxY <= 100000) {
unit = 10000;
} else{
alert('最大数据值不在0至100000之间');
}
for (var i = 0; i < ylimt.length; i++) {
ylimt[i] = ylimt[i] * unit;
};
return {ylimt:ylimt, unit:unit};
},
getMax:function(data,key,isMultiData){
if(!isMultiData){
var maxY = data.values[key]["value"+key][0].y;
var length = data.values[key]["value"+key].length;
var keystr = "value"+key;
for( var i=1;i<length;i++ ){
if(maxY<data.values[key][keystr][i].y) maxY=data.values[key][keystr][i].y;
}
return maxY;//返回最大值 如果不是多数据
}else{
var maxarr=[];
var count = data.values.length;//多条数据的数据长度
for(var i=0;i<count;i++){
maxarr.push(LineChart.getMax(data,i,false));
}
var maxvalue = maxarr[0];
for(var i=1;i<maxarr.length;i++){
maxvalue = (maxvalue<maxarr[i])?maxarr[i]:maxvalue;
}
return maxvalue;
}//如果是多条数据
},
}