zoukankan      html  css  js  c++  java
  • canvas图表详解系列(2):折线图

    本章建议学习时间4小时

    学习方式:详细阅读,并手动实现相关代码(如果没有canvas基础,需要先学习前面的canvas基础笔记)

    学习目标:此教程将教会大家如何使用canvas绘制各种图表,详细分解步骤,本次讲解折线图。

    源文件下载地址:https://github.com/sutianbinde/charts

    折线图


    折线图是前端最基本的图表之一,我们的案例展示效果如下

    功能:横轴月份,纵轴访问量,图表会根据月份和访问量的多少自动调整高度和间距,高度会有由低到高的运动效果。点击图表会有刷新重载动画效果。

    实现步骤


    --新建Html文件,写入canvas标签,并且定义绘制图表的方法

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            .a{
                background: #0bfefb;
            }
        </style>
    </head>
    <body>
        <canvas id="chart" height="400" width="600" style="margin:50px"> 你的浏览器不支持HTML5 canvas </canvas>
    
        <script type="text/javascript">
            function goChart(dataArr){
            
                
            }
            
            var chartData = [["2017/01", 50], ["2017/02", 60], ["2017/03", 100], ["2017/04",200], ["2017/05",350], ["2017/06",600]];
            goChart(chartData);
    
    
        </script>
    </body>
    </html>

    --在 goChart方法中定义需要使用的变量 并获取 canvas上下文 ,并且初始化图表

    注:这里我们对高清屏幕显示模糊做了处理,具体的处理方式见代码注释

                // 声明所需变量
                var canvas,ctx;
                // 图表属性
                var cWidth, cHeight, cMargin, cSpace;
                var originX, originY;
                // 折线图属性
                var tobalDots, dotSpace, maxValue;
                var totalYNomber;
                // 运动相关变量
                var ctr, numctr, speed;
            
                // 获得canvas上下文
                canvas = document.getElementById("chart");
                if(canvas && canvas.getContext){
                    ctx = canvas.getContext("2d");
                }
                initChart(); // 图表初始化
            
                // 图表初始化
                function initChart(){
                    // 图表信息
                    cMargin = 60;
                    cSpace = 80;
                    /*这里是对高清屏幕的处理,
                         方法:先将canvas的width 和height设置成本来的两倍(本来希望的宽度为 window的宽度减去100px)
                         然后将style.height 和 style.width设置成本来的宽高
                         这样相当于把两倍的东西缩放到原来的大小,这样在高清屏幕上 一个像素的位置就可以有两个像素的值
                         这样需要注意的是所有的宽高间距,文字大小等都得设置成原来的两倍才可以。
                    */
                    canvas.width = Math.floor( (window.innerWidth-100)/2 ) * 2 ;
                    canvas.height = 740;
                    canvas.style.height = canvas.height/2 + "px";
                    canvas.style.width = canvas.width/2 + "px";
                    cHeight = canvas.height - cMargin - cSpace;
                    cWidth = canvas.width - cMargin - cSpace;
                    originX = cMargin + cSpace;
                    originY = cMargin + cHeight;
            
                    // 折线图信息
                    tobalDots = dataArr.length;
                    dotSpace = parseInt( cWidth/tobalDots );
                    maxValue = 0;
                    for(var i=0; i<dataArr.length; i++){
                        var dotVal = parseInt( dataArr[i][1] );
                        if( dotVal > maxValue ){
                            maxValue = dotVal;
                        }
                    }
                    maxValue += 50;
                    totalYNomber = 10;
                    // 运动相关
                    ctr = 1;
                    numctr = 100;
                    speed = 6;
            
                    ctx.translate(0.5,0.5);  // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
                }
            

    --绘制图表的轴和标记 (接着上一步的代码写在 goChart方法中 )

                
                drawLineLabelMarkers(); // 绘制图表轴、标签和标记
                
                // 绘制图表轴、标签和标记
                function drawLineLabelMarkers(){
                    ctx.font = "24px Arial";
                    ctx.lineWidth = 2;
                    ctx.fillStyle = "#566a80";
                    ctx.strokeStyle = "#566a80";
                    // y轴
                    drawLine(originX, originY, originX, cMargin);
                    // x轴
                    drawLine(originX, originY, originX+cWidth, originY);
            
                    // 绘制标记
                    drawMarkers();
                }
            
                // 画线的方法
                function drawLine(x, y, X, Y){
                    ctx.beginPath();
                    ctx.moveTo(x, y);
                    ctx.lineTo(X, Y);
                    ctx.stroke();
                    ctx.closePath();
                }
            
                // 绘制标记
                function drawMarkers(){
                    ctx.strokeStyle = "#E0E0E0";
                    // 绘制 y 轴 及中间横线
                    var oneVal = parseInt(maxValue/totalYNomber);
                    ctx.textAlign = "right";
                    for(var i=0; i<=totalYNomber; i++){
                        var markerVal =  i*oneVal;
                        var xMarker = originX-5;
                        var yMarker = parseInt( cHeight*(1-markerVal/maxValue) ) + cMargin;
                        
                        ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
                        if(i>0){
                            drawLine(originX+2, yMarker, originX+cWidth, yMarker);
                        }
                    }
                    // 绘制 x 轴 及中间竖线
                    ctx.textAlign = "center";
                    for(var i=0; i<tobalDots; i++){
                        var markerVal = dataArr[i][0];
                        var xMarker = originX+i*dotSpace;
                        var yMarker = originY+30;
                        ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
                        if(i>0){
                            drawLine(xMarker, originY-2, xMarker, cMargin    );
                        }
                    }
                    // 绘制标题 y
                    ctx.save();
                    ctx.rotate(-Math.PI/2);
                    ctx.fillText("访问量", -canvas.height/2, cSpace-10);
                    ctx.restore();
                    // 绘制标题 x
                    ctx.fillText("月份", originX+cWidth/2, originY+cSpace/2+20);
                };
            

     -- 绘制折线图(接着上一步的代码写在 goChart方法中 )

                drawLineAnimate(); // 绘制折线图的动画
                
                //绘制折线图
                function drawLineAnimate(){
                    ctx.strokeStyle = "#566a80";  //"#49FE79";
            
                    //连线
                    ctx.beginPath();
                    for(var i=0; i<tobalDots; i++){
                        var dotVal = dataArr[i][1];
                        var barH = parseInt( cHeight*dotVal/maxValue* ctr/numctr );//
                        var y = originY - barH;
                        var x = originX + dotSpace*i;
                        if(i==0){
                            ctx.moveTo( x, y );
                        }else{
                            ctx.lineTo( x, y );
                        }
                    }
                    ctx.stroke();
            
                    //背景
                    ctx.lineTo( originX+dotSpace*(tobalDots-1), originY);
                    ctx.lineTo( originX, originY);
                    //背景渐变色
                    //柱状图渐变色
                    var gradient = ctx.createLinearGradient(0, 0, 0, 300);
                    gradient.addColorStop(0, 'rgba(133,171,212,0.6)');
                    gradient.addColorStop(1, 'rgba(133,171,212,0.1)');
                    ctx.fillStyle = gradient;
                    ctx.fill();
                    ctx.closePath();
                    ctx.fillStyle = "#566a80";
            
                    //绘制点
                    for(var i=0; i<tobalDots; i++){
                        var dotVal = dataArr[i][1];
                        var barH = parseInt( cHeight*dotVal/maxValue * ctr/numctr );
                        var y = originY - barH;
                        var x = originX + dotSpace*i;
                        drawArc( x, y );  //绘制点
                        ctx.fillText(parseInt(dotVal*ctr/numctr), x+15, y-8); // 文字
                    }
            
                    if(ctr<numctr){
                        ctr++;
                        setTimeout(function(){
                            ctx.clearRect(0,0,canvas.width, canvas.height);
                            drawLineLabelMarkers();
                            drawLineAnimate();
                        }, speed);
                    }
                }
            
                //绘制圆点
                function drawArc( x, y, X, Y ){
                    ctx.beginPath();
                    ctx.arc( x, y, 3, 0, Math.PI*2 );
                    ctx.fill();
                    ctx.closePath();
                }
            

    --当点击canvas的时候重新刷新图表(接着上一步的代码写在 goChart方法中 )

                
                //点击刷新图表
                canvas.onclick = function(){
                    initChart(); // 图表初始化
                    drawLineLabelMarkers(); // 绘制图表轴、标签和标记
                    drawLineAnimate(); // 绘制折线图的动画
                };

    这样我们整个代码就编写完成了,为了代码更便于阅读,我们可以将所有方法放到后面,把调用方法的代码放到前面,经过调整的全部代码如下

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
        <style>
            .a{
                background: #0bfefb;
            }
        </style>
    </head>
    <body>
        <canvas id="chart" height="400" width="600" style="margin:50px"> 你的浏览器不支持HTML5 canvas </canvas>
    
        <script type="text/javascript">
            function goChart(dataArr){
            
            
                // 声明所需变量
                var canvas,ctx;
                // 图表属性
                var cWidth, cHeight, cMargin, cSpace;
                var originX, originY;
                // 折线图属性
                var tobalDots, dotSpace, maxValue;
                var totalYNomber;
                // 运动相关变量
                var ctr, numctr, speed;
            
                // 获得canvas上下文
                canvas = document.getElementById("chart");
                if(canvas && canvas.getContext){
                    ctx = canvas.getContext("2d");
                }
                initChart(); // 图表初始化
                drawLineLabelMarkers(); // 绘制图表轴、标签和标记
                drawLineAnimate(); // 绘制折线图的动画
            
                //点击刷新图表
                canvas.onclick = function(){
                    initChart(); // 图表初始化
                    drawLineLabelMarkers(); // 绘制图表轴、标签和标记
                    drawLineAnimate(); // 绘制折线图的动画
                };
            
                // 图表初始化
                function initChart(){
                    // 图表信息
                    cMargin = 60;
                    cSpace = 80;
                    /*这里是对高清屏幕的处理,
                         方法:先将canvas的width 和height设置成本来的两倍
                         然后将style.height 和 style.width设置成本来的宽高
                         这样相当于把两倍的东西缩放到原来的 1/2,这样在高清屏幕上 一个像素的位置就可以有两个像素的值
                         这样需要注意的是所有的宽高间距,文字大小等都得设置成原来的两倍才可以。
                    */
                    canvas.width = Math.floor( (window.innerWidth-100)/2 ) * 2 ;
                    canvas.height = 740;
                    canvas.style.height = canvas.height/2 + "px";
                    canvas.style.width = canvas.width/2 + "px";
                    cHeight = canvas.height - cMargin - cSpace;
                    cWidth = canvas.width - cMargin - cSpace;
                    originX = cMargin + cSpace;
                    originY = cMargin + cHeight;
            
                    // 折线图信息
                    tobalDots = dataArr.length;
                    dotSpace = parseInt( cWidth/tobalDots );
                    maxValue = 0;
                    for(var i=0; i<dataArr.length; i++){
                        var dotVal = parseInt( dataArr[i][1] );
                        if( dotVal > maxValue ){
                            maxValue = dotVal;
                        }
                    }
                    maxValue += 50;
                    totalYNomber = 10;
                    // 运动相关
                    ctr = 1;
                    numctr = 100;
                    speed = 6;
            
                    ctx.translate(0.5,0.5);  // 当只绘制1像素的线的时候,坐标点需要偏移,这样才能画出1像素实线
                }
            
                // 绘制图表轴、标签和标记
                function drawLineLabelMarkers(){
                    ctx.font = "24px Arial";
                    ctx.lineWidth = 2;
                    ctx.fillStyle = "#566a80";
                    ctx.strokeStyle = "#566a80";
                    // y轴
                    drawLine(originX, originY, originX, cMargin);
                    // x轴
                    drawLine(originX, originY, originX+cWidth, originY);
            
                    // 绘制标记
                    drawMarkers();
                }
            
                // 画线的方法
                function drawLine(x, y, X, Y){
                    ctx.beginPath();
                    ctx.moveTo(x, y);
                    ctx.lineTo(X, Y);
                    ctx.stroke();
                    ctx.closePath();
                }
            
                // 绘制标记
                function drawMarkers(){
                    ctx.strokeStyle = "#E0E0E0";
                    // 绘制 y 轴 及中间横线
                    var oneVal = parseInt(maxValue/totalYNomber);
                    ctx.textAlign = "right";
                    for(var i=0; i<=totalYNomber; i++){
                        var markerVal =  i*oneVal;
                        var xMarker = originX-5;
                        var yMarker = parseInt( cHeight*(1-markerVal/maxValue) ) + cMargin;
                        
                        ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
                        if(i>0){
                            drawLine(originX+2, yMarker, originX+cWidth, yMarker);
                        }
                    }
                    // 绘制 x 轴 及中间竖线
                    ctx.textAlign = "center";
                    for(var i=0; i<tobalDots; i++){
                        var markerVal = dataArr[i][0];
                        var xMarker = originX+i*dotSpace;
                        var yMarker = originY+30;
                        ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
                        if(i>0){
                            drawLine(xMarker, originY-2, xMarker, cMargin    );
                        }
                    }
                    // 绘制标题 y
                    ctx.save();
                    ctx.rotate(-Math.PI/2);
                    ctx.fillText("访问量", -canvas.height/2, cSpace-10);
                    ctx.restore();
                    // 绘制标题 x
                    ctx.fillText("月份", originX+cWidth/2, originY+cSpace/2+20);
                };
            
                //绘制折线图
                function drawLineAnimate(){
                    ctx.strokeStyle = "#566a80";  //"#49FE79";
            
                    //连线
                    ctx.beginPath();
                    for(var i=0; i<tobalDots; i++){
                        var dotVal = dataArr[i][1];
                        var barH = parseInt( cHeight*dotVal/maxValue* ctr/numctr );//
                        var y = originY - barH;
                        var x = originX + dotSpace*i;
                        if(i==0){
                            ctx.moveTo( x, y );
                        }else{
                            ctx.lineTo( x, y );
                        }
                    }
                    ctx.stroke();
            
                    //背景
                    ctx.lineTo( originX+dotSpace*(tobalDots-1), originY);
                    ctx.lineTo( originX, originY);
                    //背景渐变色
                    //柱状图渐变色
                    var gradient = ctx.createLinearGradient(0, 0, 0, 300);
                    gradient.addColorStop(0, 'rgba(133,171,212,0.6)');
                    gradient.addColorStop(1, 'rgba(133,171,212,0.1)');
                    ctx.fillStyle = gradient;
                    ctx.fill();
                    ctx.closePath();
                    ctx.fillStyle = "#566a80";
            
                    //绘制点
                    for(var i=0; i<tobalDots; i++){
                        var dotVal = dataArr[i][1];
                        var barH = parseInt( cHeight*dotVal/maxValue * ctr/numctr );
                        var y = originY - barH;
                        var x = originX + dotSpace*i;
                        drawArc( x, y );  //绘制点
                        ctx.fillText(parseInt(dotVal*ctr/numctr), x+15, y-8); // 文字
                    }
            
                    if(ctr<numctr){
                        ctr++;
                        setTimeout(function(){
                            ctx.clearRect(0,0,canvas.width, canvas.height);
                            drawLineLabelMarkers();
                            drawLineAnimate();
                        }, speed);
                    }
                }
            
                //绘制圆点
                function drawArc( x, y, X, Y ){
                    ctx.beginPath();
                    ctx.arc( x, y, 3, 0, Math.PI*2 );
                    ctx.fill();
                    ctx.closePath();
                }
        
        
            }
            
            var chartData = [["2017/01", 50], ["2017/02", 60], ["2017/03", 100], ["2017/04",200], ["2017/05",350], ["2017/06",600]];
            goChart(chartData);
    
    
        </script>
    </body>
    </html>

    好了,今天就讲到这里,希望大家把代码都自己敲一遍。

    关注公众号,博客更新即可收到推送

  • 相关阅读:
    观察者模式
    简单工厂
    一个数组先按值排序,如果它的值有相同,就再按键排序(转)
    Python 一些好玩的函数
    python 一些基础知识
    python3 写CSV文件多一个空行的解决办法
    pandas学习笔记
    pycharm2017.1破解方法
    python的Debug调试
    python中字典的陷阱
  • 原文地址:https://www.cnblogs.com/chengduxiaoc/p/7678967.html
Copyright © 2011-2022 走看看