本章建議學習時間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> <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>
好了,今天就講到這裏,但願你們把代碼都本身敲一遍。
關注公衆號,博客更新便可收到推送