之前就说过,我想写一个canvas画统计图的插件,现在写好了
先说下实现的功能吧:
1.可以通过自定义X轴坐标属性和Y轴坐标属性按比例画出统计图
2.可以选择画折现图还是柱形统计图,或者两者都实现
3.可以自由定义折现颜色,坐标颜色,柱形图颜色 和canvas边框颜色,当然边框你也可以选择要或者不要
4.可以选择是否实现柱形图和折现图的动画实现
实现过程
画坐标——画箭头——做X轴和Y轴的标注——画柱形图——画折现图
话不多说,上代码
(function(window,document){ var ChartDraws = function(options){ if(!(this instanceof ChartDraws))return new ChartDraws(options); this.options = $.extend({ //报表所需的参数 "containerId" : "", //canvas所在容器id "canvasWidth" : 400, "canvasHeight" : 300, "paddingLeft" : 20, "paddingTop" : 20, "columnChartData" :[], //柱形图的数量和对应得名称以及百分比 "yChartData" :[], //y轴的数量及名称 "axisColor" : "white", //坐标轴颜色 "columnChartColor" : "#EEE685", //柱形图颜色 "isNeedAnimation" : true, //是否需要动画 "isNeedLineChart" : true, //是否需要折线图 "isNeedColumnChart" : true, //是否需要柱形图 "lineChartColor" : "#90EE90", //折线图颜色,当isNeedLineChart=true时有效 "isNeedBorder" : false, //canvas是否需要外边框 "borderColor" : "white" //外边框颜色 },options); if(this.options.canvasWidth<=500) { this.axisBorderWidth = 3; this.fontSize = 8; } else if(this.options.canvasWidth<=800){ this.axisBorderWidth = 4; this.fontSize = 12; } else{ this.axisBorderWidth = 5; this.fontSize = 16; } var self = this; _init(); function _init(){ var canvasDom = document.createElement("canvas"); canvasDom.id = self.options.containerId+"_"+"canvas"; canvasDom.width = self.options.canvasWidth; canvasDom.height = self.options.canvasHeight; if(self.options.isNeedBorder){ canvasDom.style.borderWidth = 1; canvasDom.style.borderStyle = "solid"; canvasDom.style.borderColor = self.options.borderColor; } document.getElementById(self.options.containerId).appendChild(canvasDom); self.context = document.getElementById(self.options.containerId+"_"+"canvas"); self.ctx = self.context.getContext("2d"); _drawAxis(); } function _drawAxis(){ var XYData =transformAxis( [{x:self.options.paddingLeft,y:self.options.canvasHeight-self.options.paddingTop},{x:self.options.paddingLeft,y:self.options.paddingTop},{x:self.options.canvasWidth-self.options.paddingLeft,y:self.options.paddingTop}]); self.ctx.strokeStyle=self.options.axisColor; drawLine(self.ctx,XYData,self.axisBorderWidth); //画三角箭头 //画y轴三角箭头 drawLine(self.ctx,transformAxis([{x:self.options.paddingLeft-self.axisBorderWidth,y:self.options.canvasHeight-self.options.paddingTop-self.axisBorderWidth*2},{x:self.options.paddingLeft,y:self.options.canvasHeight-self.options.paddingTop},{x:self.options.paddingLeft+self.axisBorderWidth,y:self.options.canvasHeight-self.options.paddingTop-self.axisBorderWidth*2}]),self.axisBorderWidth); //画x轴三角箭头 drawLine(self.ctx,transformAxis([{x:self.options.canvasWidth-self.options.paddingLeft-self.axisBorderWidth*2,y:self.options.paddingTop+self.axisBorderWidth},{x:self.options.canvasWidth-self.options.paddingLeft,y:self.options.paddingTop},{x:self.options.canvasWidth-self.options.paddingLeft-self.axisBorderWidth*2,y:self.options.paddingTop-self.axisBorderWidth}]),self.axisBorderWidth); _drawCoordinatePoints(); } function _drawCoordinatePoints(){ self.reactAngleWidth = (1-2*0.04)*(self.options.canvasWidth-(2*self.options.paddingLeft))/(self.options.columnChartData.length*2-1); self.lineDataList = []; for(var i = 0;i<self.options.columnChartData.length;i++) { drawXText(self.ctx,2*self.options.columnChartData[i].NO*self.reactAngleWidth+self.options.paddingLeft+0.04*(self.options.canvasWidth-(2*self.options.paddingLeft))+self.reactAngleWidth/2,self.options.paddingTop/2,self.options.columnChartData[i].Name); self.lineDataList.push({ x:2*self.options.columnChartData[i].NO*self.reactAngleWidth+self.options.paddingLeft+0.04*(self.options.canvasWidth-(2*self.options.paddingLeft))+self.reactAngleWidth/2, y:self.options.canvasHeight-(self.options.paddingTop+(self.options.canvasHeight-2*self.options.paddingTop)*self.options.columnChartData[i].PT) }) } //画Y轴title 画y轴虚线 self.reactAngleHeight = (self.options.canvasHeight-2*self.options.paddingTop)/(self.options.yChartData.length+1); for(var j = 0;j<self.options.yChartData.length;j++) { drawYText(self.ctx,3*self.options.paddingLeft/4,self.options.paddingTop+self.reactAngleHeight*(j+1),self.options.yChartData[j].Name); //画虚线 drawDottedLine(self.ctx,self.options.paddingLeft,self.options.paddingTop+self.reactAngleHeight*(j+1),self.options.canvasWidth-self.options.paddingLeft,self.options.paddingTop+self.reactAngleHeight*(j+1),self.options.canvasWidth-2*self.options.paddingLeft,10,self.axisBorderWidth/2); } _drawColumnChart(); } function _drawColumnChart(){ //柱形图循环 var reactAngleTimer = 1; function loopColumnChart() { var columnChartLooped = window.requestAnimationFrame(loopColumnChart); if(reactAngleTimer<=100) { for(var k=0;k<self.options.columnChartData.length;k++) { self.ctx.fillStyle =self.options.columnChartColor; drawRectangle(self.ctx,self.lineDataList[k].x-self.reactAngleWidth/2,self.options.canvasHeight-((self.options.canvasHeight-2*self.options.paddingTop)*self.options.columnChartData[k].PT*reactAngleTimer/100+self.options.paddingTop),self.reactAngleWidth,(self.options.canvasHeight-2*self.options.paddingTop)*self.options.columnChartData[k].PT*reactAngleTimer/100); } reactAngleTimer++; } else { window.cancelAnimationFrame(columnChartLooped); columnChartLooped = null; reactAngleTimer = 1; if(self.options.isNeedLineChart) { loopLineChart(); } } } //折线图循环 var lineTimer = 0; function loopLineChart() { var lineChartLooped = window.requestAnimationFrame(loopLineChart); if(lineTimer<self.lineDataList.length-1) { self.ctx.lineWidth = 2*self.axisBorderWidth/3; if(lineTimer == 0) { drawCircle(self.ctx,self.lineDataList[lineTimer].x,self.lineDataList[lineTimer].y); } drawCircle(self.ctx,self.lineDataList[lineTimer+1].x,self.lineDataList[lineTimer+1].y); self.ctx.beginPath(); self.ctx.moveTo(self.lineDataList[lineTimer].x,self.lineDataList[lineTimer].y); self.ctx.lineTo(self.lineDataList[lineTimer+1].x,self.lineDataList[lineTimer+1].y); self.ctx.strokeStyle = self.options.lineChartColor; self.ctx.lineWidth = 2*self.axisBorderWidth/3; self.ctx.stroke(); lineTimer++; } else { window.cancelAnimationFrame(lineChartLooped); lineChartLooped = null; lineTimer = 0; } } //画柱形图 function drawRectangle(context,x,y,width,height){ context.beginPath(); context.fillRect(x,y,width,height); } //画圆 function drawCircle(context,x,y){ context.beginPath(); context.arc(x,y,self.axisBorderWidth/2,0,2*Math.PI,true); context.strokeStyle=self.options.lineChartColor; context.stroke(); context.closePath(); } if(self.options.isNeedAnimation) { if(self.options.isNeedColumnChart) { loopColumnChart(); } else { if(self.options.isNeedLineChart) { loopLineChart(); } } } else { if(self.options.isNeedColumnChart) { for(var k=0;k<self.options.columnChartData.length;k++) { self.ctx.fillStyle =self.options.columnChartColor; drawRectangle(self.ctx,self.lineDataList[k].x-self.reactAngleWidth/2,self.options.canvasHeight-((self.options.canvasHeight-2*self.options.paddingTop)*self.options.columnChartData[k].PT+self.options.paddingTop),self.reactAngleWidth,(self.options.canvasHeight-2*self.options.paddingTop)*self.options.columnChartData[k].PT); } } if(self.options.isNeedLineChart) { for (var l = 0; l < self.lineDataList.length - 1; l++) { self.ctx.lineWidth = 4; if (l == 0) { drawCircle(self.ctx, self.lineDataList[l].x, self.lineDataList[l].y); } drawCircle(self.ctx, self.lineDataList[l + 1].x, self.lineDataList[l + 1].y); self.ctx.beginPath(); self.ctx.moveTo(self.lineDataList[l].x, self.lineDataList[l].y); self.ctx.lineTo(self.lineDataList[l + 1].x, self.lineDataList[l + 1].y); self.ctx.strokeStyle = self.options.lineChartColor; self.ctx.lineWidth = 2*self.axisBorderWidth/3; self.ctx.stroke(); } } } } function transformAxis(data) { var newData=[]; for(var i=0;i<data.length;i++){ newData.push({ x:data[i].x, y:self.options.canvasHeight-data[i].y }) } return newData; } function drawLine(context,point,width){ context.beginPath(); context.moveTo(point[0].x,point[0].y); if(point.length>2) { for(var i=1;i<point.length;i++) { context.lineTo(point[i].x,point[i].y); } } context.lineWidth = width; context.lineJoin='round'; context.stroke(); context.closePath(); } //画y轴title function drawYText(context,x,y,str) { context.beginPath(); context.font = '{fontSize} Microsoft Yahei'.replace("{fontSize}",self.fontSize+"px"); context.fillStyle = 'white'; context.textAlign = 'right'; context.fillText(str,x,self.options.canvasHeight-y); context.closePath(); } //画x轴title function drawXText(context,x,y,str) { context.beginPath(); context.font = '{fontSize} Microsoft Yahei'.replace("{fontSize}",self.fontSize+"px"); context.fillStyle = 'white'; context.textAlign = 'center'; context.fillText(str,x,self.options.canvasHeight-y); context.closePath(); } function drawDottedLine(context,x1,y1,x2,y2,totalLength,length,lineWidth){ y1 = self.options.canvasHeight-y1; y2 = self.options.canvasHeight-y2; var dashLen = length === undefined ? 5 : length; //计算有多少个线段 context.beginPath(); var num = Math.floor(totalLength/dashLen); context.lineWidth = lineWidth; for(var i = 0 ; i < num; i++) { context[i%2==0 ? 'moveTo' : 'lineTo'](x1+(x2-x1)/num*i,y1+(y2-y1)/num*i); } context.stroke(); } }; window.ChartDraws = ChartDraws; }(window,document));
下面还有一个是实现requestAnimationFrame浏览器兼容的
(function(){ var lastTime = 0; var prefixes = ['ms','webkit','o','moz']; //各浏览器前缀 var requestAnimationFrame = window.requestAnimationFrame; var cancelAnimationFrame = window.cancelAnimationFrame; var prefix; //通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式 for( var i = 0; i < prefixes.length; i++ ) { if ( requestAnimationFrame && cancelAnimationFrame ) { break; } prefix = prefixes[i]; requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ]; cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || window[ prefix + 'CancelRequestAnimationFrame' ]; } //如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeout if ( !requestAnimationFrame || !cancelAnimationFrame ) { requestAnimationFrame = function( callback, element ) { var currTime = new Date().getTime(); //为了使setTimteout的尽可能的接近每秒60帧的效果 var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); var id = window.setTimeout( function() { callback( currTime + timeToCall ); }, timeToCall ); lastTime = currTime + timeToCall; return id; }; cancelAnimationFrame = function( id ) { window.clearTimeout( id ); }; } window.requestAnimationFrame = requestAnimationFrame; window.cancelAnimationFrame = cancelAnimationFrame; }());
附上<script>调用
ChartDraws({ "containerId" : "chart1", //canvas所在容器id "canvasWidth" : 1000, "canvasHeight" : 250, "paddingLeft" : 50, "paddingTop" : 50, "columnChartData": [ {NO:0,PT:0.2,Name:"Html/Css"}, {NO:1,PT:0.4,Name:"Html5/Css3"}, {NO:2,PT:0.4,Name:"JavaScript"}, {NO:3,PT:0.5,Name:"JQuery"}, {NO:4,PT:0.2,Name:"Angular.js"}, {NO:5,PT:0.8,Name:"BootStrap"}, {NO:6,PT:0.6,Name:"React.js"}, {NO:7,PT:0.5,Name:"Java"} ], "yChartData" : [ {NO:0,Name:"熟悉"}, {NO:1,Name:"掌握"}, {NO:2,Name:"精通"} ], "isNeedAnimation" : false, "isNeedBorder" : false, "isNeedLineChart":true, "axisColor" : "#8DEEEE" }); ChartDraws({ "containerId" : "chart2", //canvas所在容器id "canvasWidth" : 1000, "canvasHeight" : 250, "paddingLeft" : 50, "paddingTop" : 50, "columnChartData": [ {NO:0,PT:0.4,Name:"Html/Css"}, {NO:1,PT:0.5,Name:"Html5/Css3"}, {NO:2,PT:0.2,Name:"JavaScript"}, {NO:3,PT:0.7,Name:"JQuery"}, {NO:4,PT:0.2,Name:"Angular.js"}, {NO:5,PT:0.3,Name:"BootStrap"}, {NO:6,PT:0.8,Name:"React.js"}, {NO:7,PT:0.2,Name:"Java"} ], "yChartData" : [ {NO:0,Name:"熟悉"}, {NO:1,Name:"掌握"}, {NO:2,Name:"精通"} ], "isNeedAnimation" : false, "isNeedBorder" : false, "isNeedLineChart":false, "isNeedColumnChart" : true, "columnChartColor":"#9370DB" }); ChartDraws({ "containerId" : "chart3", //canvas所在容器id "canvasWidth" : 1000, "canvasHeight" : 250, "paddingLeft" : 50, "paddingTop" : 50, "columnChartData": [ {NO:0,PT:0.4,Name:"Html/Css"}, {NO:1,PT:0.5,Name:"Html5/Css3"}, {NO:2,PT:0.2,Name:"JavaScript"}, {NO:3,PT:0.7,Name:"JQuery"}, {NO:4,PT:0.2,Name:"Angular.js"}, {NO:5,PT:0.3,Name:"BootStrap"}, {NO:6,PT:0.8,Name:"React.js"}, {NO:7,PT:0.2,Name:"Java"} ], "yChartData" : [ {NO:0,Name:"熟悉"}, {NO:1,Name:"掌握"}, {NO:2,Name:"精通"} ], "isNeedAnimation" : false, "isNeedBorder" : true, "isNeedLineChart":true, "isNeedColumnChart" : false, "lineChartColor" : "#8DB6CD", "borderColor" : "#87CEFA" })
html代码
<div class="section"> <div id="chart1"></div> <div id="chart2"></div> <div id="chart3"></div> </div>
下面是一个实现后的效果图
在整个编码的过程中我把代码改过一次,为什么改呢,因为在第一次的时候我在js里面使用了大量的 ChartDraws.prototype.XXXX = function(){};
后来我一想不对啊,我为什么要把这么多的方法暴露给外部呢......这不是没事找事么.......
所以现在就改成这样了,如果有不对的地方和可以改进的地方,希望路过的指点下,谢谢!还有那个白条代码背景怎么删不掉...........
以上这篇js封装成插件_Canvas统计图插件编写实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。