canvas圖表詳解系列(1):柱狀圖

 

本章建議學習時間4小時javascript

學習方式:詳細閱讀,並手動實現相關代碼(若是沒有canvas基礎,須要先學習前面的canvas基礎筆記)html

學習目標:此教程將教會你們如何使用canvas繪製各類圖表,詳細分解步驟,本次講解柱狀圖。前端

 

源文件下載地址:https://github.com/sutianbinde/chartsjava

 

柱狀圖git


柱狀圖是前端最基本的圖表之一,咱們的案例展現效果以下github

功能:橫軸年份,縱軸產量,圖表會根據年份的多少自動分配柱的寬度,高度會有由低到高的運動效果,當鼠標移入時,當前柱會顏色加深。點擊圖表會有刷新重載動畫效果。canvas

 

實現步驟瀏覽器


 

 

--新建Html文件,寫入canvas標籤,而且定義繪製圖表的方法學習

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <canvas id="barChart" height="400" width="600" style="margin:50px"> 你的瀏覽器不支持HTML5 canvas </canvas>

    <script type="text/javascript">
        //封裝繪製圖表的方法
        function goBarChart(dataArr){
            
        }

        //調用方法,並傳入須要顯示的數據
        goBarChart(
                [[2007, 750], [2008, 425], [2009, 960], [2010, 700], [2011, 800], [2012, 975], [2013, 375], [2014, 775]]
        )
    </script>
</body>
</html>

 

--在 goBarChart方法中定義須要使用的變量 並獲取 canvas上下文 動畫

            // 聲明所需變量
            var canvas,ctx;
            // 圖表屬性
            var cWidth, cHeight, cMargin, cSpace;
            var originX, originY;
            // 柱狀圖屬性
            var bMargin, tobalBars, bWidth, maxValue;
            var totalYNomber;
            var gradient;

            // 運動相關變量
            var ctr, numctr, speed;
            //鼠標移動
            var mousePosition = {};

            // 得到canvas上下文
            canvas = document.getElementById("barChart");
            if(canvas && canvas.getContext){
                ctx = canvas.getContext("2d");
            }

 

--初始化圖表  (接着上一步的代碼寫在 goBarChart方法中 )

            initChart(); // 圖表初始化

            // 圖表初始化
            function initChart(){
                // 圖表信息
                cMargin = 30;
                cSpace = 60;
                cHeight = canvas.height - cMargin*2 - cSpace;
                cWidth = canvas.width - cMargin*2 - cSpace;
                originX = cMargin + cSpace;
                originY = cMargin + cHeight;

                // 柱狀圖信息
                bMargin = 15;
                tobalBars = dataArr.length;
                bWidth = parseInt( cWidth/tobalBars - bMargin );
                maxValue = 0;
                for(var i=0; i<dataArr.length; i++){
                    var barVal = parseInt( dataArr[i][1] );
                    if( barVal > maxValue ){
                        maxValue = barVal;
                    }
                }
                maxValue += 50;
                totalYNomber = 10;
                // 運動相關
                ctr = 1;
                numctr = 100;
                speed = 10;

                //柱狀圖漸變色
                gradient = ctx.createLinearGradient(0, 0, 0, 300);
                gradient.addColorStop(0, 'green');
                gradient.addColorStop(1, 'rgba(67,203,36,1)');

            }

 

--繪製圖表的軸和標記 (接着上一步的代碼寫在 goBarChart方法中 )

 

            drawLineLabelMarkers(); // 繪製圖表軸、標籤和標記
            
            // 繪製圖表軸、標籤和標記
            function drawLineLabelMarkers(){
                ctx.translate(0.5,0.5);  // 當只繪製1像素的線的時候,座標點須要偏移,這樣才能畫出1像素實線
                ctx.font = "12px Arial";
                ctx.lineWidth = 1;
                ctx.fillStyle = "#000";
                ctx.strokeStyle = "#000";
                // y軸
                drawLine(originX, originY, originX, cMargin);
                // x軸
                drawLine(originX, originY, originX+cWidth, originY);

                // 繪製標記
                drawMarkers();
                ctx.translate(-0.5,-0.5);  // 還原位置
            }
            
            // 畫線的方法
            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;
                    //console.log(xMarker, yMarker+3,markerVal/maxValue,originY);
                    ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
                    if(i>0){
                        drawLine(originX, yMarker, originX+cWidth, yMarker);
                    }
                }
                // 繪製 x
                ctx.textAlign = "center";
                for(var i=0; i<tobalBars; i++){
                    var markerVal = dataArr[i][0];
                    var xMarker = parseInt( originX+cWidth*(i/tobalBars)+bMargin+bWidth/2 );
                    var yMarker = originY+15;
                    ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
                }
                // 繪製標題 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+10);
            };

 

-- 繪製柱狀圖(接着上一步的代碼寫在 goBarChart方法中 )

            drawBarAnimate(); // 繪製柱狀圖的動畫
            //繪製柱形圖
            function drawBarAnimate(mouseMove){
                for(var i=0; i<tobalBars; i++){
                    var oneVal = parseInt(maxValue/totalYNomber);
                    var barVal = dataArr[i][1];
                    var barH = parseInt( cHeight*barVal/maxValue * ctr/numctr );
                    var y = originY - barH;
                    var x = originX + (bWidth+bMargin)*i + bMargin;
                    drawRect( x, y, bWidth, barH, mouseMove );  //高度減一避免蓋住x軸
                    ctx.fillText(parseInt(barVal*ctr/numctr), x+15, y-8); // 文字
                }
                if(ctr<numctr){
                    ctr++;
                    setTimeout(function(){
                        ctx.clearRect(0,0,canvas.width, canvas.height);
                        drawLineLabelMarkers();
                        drawBarAnimate();
                    }, speed);
                }
            }
            //繪製方塊
            function drawRect( x, y, X, Y, mouseMove ){

                ctx.beginPath();
                ctx.rect( x, y, X, Y );
                if(mouseMove && ctx.isPointInPath(mousePosition.x, mousePosition.y)){ //若是是鼠標移動的到柱狀圖上,從新繪製圖表
                    ctx.fillStyle = "green";
                }else{
                    ctx.fillStyle = gradient;
                    ctx.strokeStyle = gradient;
                }
                ctx.fill();
                ctx.closePath();

            }

 

--檢測鼠標移動並顯示當前項(接着上一步的代碼寫在 goBarChart方法中 )

注:這裏鼠標移動的檢測在有文字縮放顯示的高清屏幕上會有誤差不許確的狀況,並且在高清屏幕中canvas中的文字會略顯模糊,之後的章節中會說明如何處理這個問題,你們能夠先無論這個問題。(github上的 柱狀圖-高清.html也已經解決了這個問題,你能夠點擊上面的下載連接去查看源碼)

 

 //檢測鼠標移動
            var mouseTimer = null;
            canvas.addEventListener("mousemove",function(e){
                e = e || window.event;
                if( e.offsetX || e.offsetX==0 ){
                    mousePosition.x = e.offsetX;
                    mousePosition.y = e.offsetY;
                }else if( e.layerX || e.layerX==0 ){
                    mousePosition.x = e.layerX;
                    mousePosition.y = e.layerY;
                }
                
                clearTimeout(mouseTimer);
                mouseTimer = setTimeout(function(){
                    ctx.clearRect(0,0,canvas.width, canvas.height);
                    drawLineLabelMarkers();
                    drawBarAnimate(true);
                },10);
            });

  

 

--當點擊canvas的時候從新刷新圖表(接着上一步的代碼寫在 goBarChart方法中 )

            //點擊刷新圖表
            canvas.onclick = function(){
                initChart(); // 圖表初始化
                drawLineLabelMarkers(); // 繪製圖表軸、標籤和標記
                drawBarAnimate(); // 繪製折線圖的動畫
            };

 

 

這樣咱們整個代碼就編寫完成了,爲了代碼更便於閱讀,咱們能夠將全部方法放到後面,把調用方法的代碼放到前面,通過調整的所有代碼以下

 

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <canvas id="barChart" height="400" width="600" style="margin:50px"> 你的瀏覽器不支持HTML5 canvas </canvas>

    <script type="text/javascript">
        function goBarChart(dataArr){
            // 聲明所需變量
            var canvas,ctx;
            // 圖表屬性
            var cWidth, cHeight, cMargin, cSpace;
            var originX, originY;
            // 柱狀圖屬性
            var bMargin, tobalBars, bWidth, maxValue;
            var totalYNomber;
            var gradient;

            // 運動相關變量
            var ctr, numctr, speed;
            //鼠標移動
            var mousePosition = {};

            // 得到canvas上下文
            canvas = document.getElementById("barChart");
            if(canvas && canvas.getContext){
                ctx = canvas.getContext("2d");
            }
            initChart(); // 圖表初始化
            drawLineLabelMarkers(); // 繪製圖表軸、標籤和標記
            drawBarAnimate(); // 繪製柱狀圖的動畫
            //檢測鼠標移動
            var mouseTimer = null;
            canvas.addEventListener("mousemove",function(e){
                e = e || window.event;
                if( e.layerX || e.layerX==0 ){
                    mousePosition.x = e.layerX;
                    mousePosition.y = e.layerY;
                }else if( e.offsetX || e.offsetX==0 ){
                    mousePosition.x = e.offsetX;
                    mousePosition.y = e.offsetY;
                }
                
                clearTimeout(mouseTimer);
                mouseTimer = setTimeout(function(){
                    ctx.clearRect(0,0,canvas.width, canvas.height);
                    drawLineLabelMarkers();
                    drawBarAnimate(true);
                },10);
            });

            //點擊刷新圖表
            canvas.onclick = function(){
                initChart(); // 圖表初始化
                drawLineLabelMarkers(); // 繪製圖表軸、標籤和標記
                drawBarAnimate(); // 繪製折線圖的動畫
            };


            // 圖表初始化
            function initChart(){
                // 圖表信息
                cMargin = 30;
                cSpace = 60;
                cHeight = canvas.height - cMargin*2 - cSpace;
                cWidth = canvas.width - cMargin*2 - cSpace;
                originX = cMargin + cSpace;
                originY = cMargin + cHeight;

                // 柱狀圖信息
                bMargin = 15;
                tobalBars = dataArr.length;
                bWidth = parseInt( cWidth/tobalBars - bMargin );
                maxValue = 0;
                for(var i=0; i<dataArr.length; i++){
                    var barVal = parseInt( dataArr[i][1] );
                    if( barVal > maxValue ){
                        maxValue = barVal;
                    }
                }
                maxValue += 50;
                totalYNomber = 10;
                // 運動相關
                ctr = 1;
                numctr = 100;
                speed = 10;

                //柱狀圖漸變色
                gradient = ctx.createLinearGradient(0, 0, 0, 300);
                gradient.addColorStop(0, 'green');
                gradient.addColorStop(1, 'rgba(67,203,36,1)');

            }

            // 繪製圖表軸、標籤和標記
            function drawLineLabelMarkers(){
                ctx.translate(0.5,0.5);  // 當只繪製1像素的線的時候,座標點須要偏移,這樣才能畫出1像素實線
                ctx.font = "12px Arial";
                ctx.lineWidth = 1;
                ctx.fillStyle = "#000";
                ctx.strokeStyle = "#000";
                // y軸
                drawLine(originX, originY, originX, cMargin);
                // x軸
                drawLine(originX, originY, originX+cWidth, originY);

                // 繪製標記
                drawMarkers();
                ctx.translate(-0.5,-0.5);  // 還原位置
            }

            // 畫線的方法
            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;
                    //console.log(xMarker, yMarker+3,markerVal/maxValue,originY);
                    ctx.fillText(markerVal, xMarker, yMarker+3, cSpace); // 文字
                    if(i>0){
                        drawLine(originX, yMarker, originX+cWidth, yMarker);
                    }
                }
                // 繪製 x
                ctx.textAlign = "center";
                for(var i=0; i<tobalBars; i++){
                    var markerVal = dataArr[i][0];
                    var xMarker = parseInt( originX+cWidth*(i/tobalBars)+bMargin+bWidth/2 );
                    var yMarker = originY+15;
                    ctx.fillText(markerVal, xMarker, yMarker, cSpace); // 文字
                }
                // 繪製標題 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+10);
            };

            //繪製柱形圖
            function drawBarAnimate(mouseMove){
                for(var i=0; i<tobalBars; i++){
                    var oneVal = parseInt(maxValue/totalYNomber);
                    var barVal = dataArr[i][1];
                    var barH = parseInt( cHeight*barVal/maxValue * ctr/numctr );
                    var y = originY - barH;
                    var x = originX + (bWidth+bMargin)*i + bMargin;
                    drawRect( x, y, bWidth, barH, mouseMove );  //高度減一避免蓋住x軸
                    ctx.fillText(parseInt(barVal*ctr/numctr), x+15, y-8); // 文字
                }
                if(ctr<numctr){
                    ctr++;
                    setTimeout(function(){
                        ctx.clearRect(0,0,canvas.width, canvas.height);
                        drawLineLabelMarkers();
                        drawBarAnimate();
                    }, speed);
                }
            }

            //繪製方塊
            function drawRect( x, y, X, Y, mouseMove ){

                ctx.beginPath();
                ctx.rect( x, y, X, Y );
                if(mouseMove && ctx.isPointInPath(mousePosition.x, mousePosition.y)){ //若是是鼠標移動的到柱狀圖上,從新繪製圖表
                    ctx.fillStyle = "green";
                }else{
                    ctx.fillStyle = gradient;
                    ctx.strokeStyle = gradient;
                }
                ctx.fill();
                ctx.closePath();

            }


        }


        goBarChart(
                [[2007, 750], [2008, 425], [2009, 960], [2010, 700], [2011, 800], [2012, 975], [2013, 375], [2014, 775]]
        )


    </script>
</body>
</html>

 

 

好了,今天就講到這裏,但願你們把代碼都本身敲一遍。

 

 

關注公衆號,博客更新便可收到推送

相關文章
相關標籤/搜索