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>

 

 

好了,今天就講到這里,希望大家把代碼都自己敲一遍。

 

 

關注公眾號,博客更新即可收到推送

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM