html5 canvas 实现折线图

效果图:
这里写图片描述

此效果难点: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;
        }//如果是多条数据
    }, 
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值