js封装成插件_Canvas统计图插件编写实例


Posted in Javascript onSeptember 12, 2017

之前就说过,我想写一个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封装成插件_Canvas统计图插件编写实例

在整个编码的过程中我把代码改过一次,为什么改呢,因为在第一次的时候我在js里面使用了大量的 ChartDraws.prototype.XXXX = function(){};

后来我一想不对啊,我为什么要把这么多的方法暴露给外部呢......这不是没事找事么.......

所以现在就改成这样了,如果有不对的地方和可以改进的地方,希望路过的指点下,谢谢!还有那个白条代码背景怎么删不掉...........

以上这篇js封装成插件_Canvas统计图插件编写实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
{}与function(){}选用空对象{}来存放keyValue
May 23 Javascript
使用jQuery Ajax功能时需要注意的一个问题(内存溢出)
May 30 Javascript
深入了解javascript中的prototype与继承
Apr 14 Javascript
jQuery结合CSS制作动态的下拉菜单
Oct 27 Javascript
整理关于Bootstrap过渡动画的慕课笔记
Mar 29 Javascript
jQuery动态添加.active 实现导航效果代码思路详解
Aug 29 jQuery
解决vue项目报错webpackJsonp is not defined问题
Mar 14 Javascript
微信小程序仿RadioGroup改变样式的处理方案
Jul 13 Javascript
AngularJS修改model值时,显示内容不变的实例
Sep 13 Javascript
Vue路由前后端设计总结
Aug 06 Javascript
vue使用高德地图点击下钻上浮效果的实现思路
Oct 12 Javascript
js 计数排序的实现示例(升级版)
Jan 12 Javascript
JS监控关闭浏览器操作的实例详解
Sep 12 #Javascript
详解angular笔记路由之angular-router
Sep 12 #Javascript
jQuery实现用户信息表格的添加和删除功能
Sep 12 #jQuery
vue引入jq插件的实例讲解
Sep 12 #Javascript
解决jquery appaend元素中id绑定事件失效的问题
Sep 12 #jQuery
BootStrap实现文件上传并带有进度条效果
Sep 11 #Javascript
详解关于react-redux中的connect用法介绍及原理解析
Sep 11 #Javascript
You might like
php 采集书并合成txt格式的实现代码
2009/03/01 PHP
php+webSoket实现聊天室示例代码(附源码)
2017/02/17 PHP
thinkphp5.1 框架导入/导出excel文件操作示例
2020/05/25 PHP
utf-8编码引起js输出中文乱码的解决办法
2010/06/23 Javascript
jQuery获取样式中的背景颜色属性值/颜色值
2012/12/17 Javascript
关于IE BUG与字符串截取substr的解决办法
2013/04/10 Javascript
JS运动基础框架实例分析
2015/03/03 Javascript
简介JavaScript中getUTCMonth()方法的使用
2015/06/10 Javascript
javascript作用域问题实例分析
2015/07/13 Javascript
JavaScript实战之菜单特效
2016/08/16 Javascript
第一次接触Bootstrap框架
2016/10/24 Javascript
微信小程序开发实战教程之手势解锁
2016/11/18 Javascript
vue.js实现价格格式化的方法
2017/05/23 Javascript
JS遍历DOM文档树的方法实例详解
2018/04/03 Javascript
vue多层嵌套路由实例分析
2019/03/19 Javascript
Vue项目路由刷新的实现代码
2019/04/17 Javascript
详解Vue中组件的缓存
2019/04/20 Javascript
vux-scroller实现移动端上拉加载功能过程解析
2019/10/08 Javascript
js刷新页面location.reload()用法详解
2019/12/09 Javascript
[45:18]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第一场
2018/04/04 DOTA
python实现关键词提取的示例讲解
2018/04/28 Python
Python中出现IndentationError:unindent does not match any outer indentation level错误的解决方法
2020/04/18 Python
Python基本数据结构之字典类型dict用法分析
2019/06/08 Python
python使用celery实现异步任务执行的例子
2019/08/28 Python
Python tkinter制作单机五子棋游戏
2020/09/14 Python
优秀中专生推荐信
2013/11/17 职场文书
大学生找工作推荐信范文
2013/11/28 职场文书
旅游业大学生创业计划书
2014/01/31 职场文书
大专应届毕业生求职信
2014/07/15 职场文书
2014社区健康教育工作总结
2014/12/16 职场文书
毕业论文指导老师意见
2015/06/04 职场文书
导师鉴定意见
2015/06/05 职场文书
Python基础之赋值,浅拷贝,深拷贝的区别
2021/04/30 Python
JavaScript异步操作中串行和并行
2021/11/20 Javascript
python中urllib包的网络请求教程
2022/04/19 Python
Win10服务全部禁用了怎么启动?Win10服务全部禁用解决方法
2022/09/23 数码科技