众所周知,饼图在数据表达上起着至关重要的作用,它使数据更为直观,也更为清晰,所以对于业务需求方来说,能让用户一目了然看懂数据的,必然会采用饼图来表达。

当我们既想要表达数据的占比关系,又想表达某些数据之间的包含关系时,就要用到复合饼图了。

之前尝试过用Echart、Highcharts、Chart等框架,实现出的饼图较为美观,功能强大且容易上手,但要实现复合饼图,却总显的美中不足,以Echart为例,实现饼图只限于两种模式:

  • 'radius' 扇区圆心角展现数据的百分比,半径展现数据的大小。
  • 'area' 所有扇区圆心角相同,仅通过半径展现数据大小。

而我们的业务需求是:根据不同类型规定好圆心角角度(圆心角不相同),并根据各类型的实际份额与虚拟份额的占比显示半径的大小。

由于框架的局限性,只能放弃框架纯手写一套实现业务需求,代码如下:

<!--html-->
<div style="overflow: hidden"><canvas id="canvas" width="300" height="300" style="display: block;margin:50px auto;"></canvas>
</div> 

实现逻辑:

1.首先 各类型radius半径相同,按规定角度画出浅色扇形代表虚拟份额各类型 ;

2.同样的方式,按照 实虚份额的占比*radius=实际份额的半径,绘制实际份额各类型;

3.添加点击事件,利用三角函数公式及点坐标位置判断是否在某一扇形内;

4.根据符合条件的扇形的索引,在重新绘图时,将此扇形的填充色更改,点击效果就做好了。

<script type="text/javascript"> var canvas = document.getElementById("canvas");var ctx = canvas.getContext("2d");  var nums = [60,90,30,50,100,30];  var scale = [0.3,0.5,0.6,0.8,0.4,1]; var arrs = ["重疾","意外","养老","医疗","身故","子女"]; var val = [300,50,220,7,1100,65];   var colors1 = ["rgba(139,113,205,.3)","rgba(237,209,1,.3)","rgba(0,124,226,.3)","rgba(1,162,217,.3)","rgba(80,194,135,.3)","rgba(242,171,48,.3)"]; var colors = ["#8b71cd","#edd101","#007ce2","#01a2d9","#50c287","#f2ab4e"];   var width = canvas.clientWidth;var height = canvas.clientHeight;// 计算半径、圆心点,定义初始值start、endvar radius = height/4;var start = 0;  var end = 0; var endDeg = 0;var centerX = width/2;var centerY = height/2;var ctxArr = [];//绘制圆饼 背景 function pieBg(){  for (var i = 0;i < nums.length; i ++){  ctx.beginPath();  ctx.moveTo(0,0);  end += nums[i]/180*Math.PI;//终止角度  ctx.fillStyle = colors1[i];  ctx.arc(0,0,radius,start,end);  ctx.fill();  ctx.closePath();  start += nums[i]/180*Math.PI;//起始角度  }  } //重绘选中扇形function redraw(index){for (var i = 0;i < nums.length; i ++){ ctx.beginPath();  ctx.moveTo(0,0);  end += nums[i]/180*Math.PI;//终止角度 if(index == i){ctx.fillStyle = "#ff9000"; }else{ctx.fillStyle = colors[i]; }ctx.arc(0,0,radius*scale[i],start,end); ctxArr.push({key:i,startDeg:start,endDeg:end}); ctx.fill();  ctx.closePath(); start += nums[i]/180*Math.PI;//起始角度 } }//实心圆function draw(){ctx.translate(centerX,centerY); pieBg(); redraw();console.log("圆心点x="+centerX+'----y='+centerY);//添加扇形的点击事件canvas.addEventListener('click', function(e) {//获取坐标点(x,y)var x = event.pageX - canvas.getBoundingClientRect().left;var y = event.pageY - canvas.getBoundingClientRect().top;if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[0],2)) && (y > centerY) && (y-centerY < Math.tan(60 * (Math.PI/180))*(x-centerX))){console.log("在扇形1内");redraw(0);}if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[1],2)) && (y - centerY > Math.tan(60* (Math.PI/180))*(x-centerX)) && (y - centerY > Math.tan(150* (Math.PI/180))*(x-centerX))){console.log("在扇形2内");redraw(1);}if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[2],2)) && (y > centerY) && (y-centerY < Math.tan(150 * (Math.PI/180))*(x-centerX))){console.log("在扇形3内");redraw(2);}if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[3],2)) && (y < centerY) && (y-centerY > Math.tan(50 * (Math.PI/180))*(x-centerX))){console.log("在扇形4内");redraw(3);}if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[4],2)) && (y - centerY < Math.tan(50* (Math.PI/180))*(x-centerX)) && (y - centerY < Math.tan(150* (Math.PI/180))*(x-centerX))){console.log("在扇形5内");redraw(4);}if((Math.pow(x-centerX,2)+Math.pow(y-centerY,2) < Math.pow(radius*scale[5],2)) && (y < centerY) && (y - centerY > Math.tan(150* (Math.PI/180))*(x-centerX))){console.log("在扇形6内");redraw(5);}}, false)} draw();</script>  

为了使饼图更加完善,小编经过不懈的努力钻研,在此基础上增加了指示线及文字说明功能;在draw()方法里调用pieLine()就可以了,效果图及代码:

var endDeg = 0;
//指示线及文字说明
function pieLine(){for (var i = 0;i < nums.length; i ++){  ctx.beginPath();  var x = radius*Math.cos((endDeg+nums[i]/2)/180*Math.PI);var y = radius*Math.sin((endDeg+nums[i]/2)/180*Math.PI);// console.log("x="+x+"--y="+y);ctx.moveTo(x,y);var x1 = (radius+20)*Math.cos((endDeg+nums[i]/2)/180*Math.PI);var y1 = (radius+20)*Math.sin((endDeg+nums[i]/2)/180*Math.PI);ctx.lineTo(x1,y1);var x2 = (radius+20+20)*Math.cos((endDeg+nums[i]/2)/180*Math.PI);if(x1>0){x2 = x1+10;}else{x2 = x1-10;}ctx.lineTo(x2,y1);ctx.stroke();   //描边console.log(x2);//指示文字//计算文字宽度高度ctx.font = "12px scans-serif";  ctx.fillStyle = "#333";  var w = ctx.measureText(val[i]+"万").width > ctx.measureText(arrs[i]).width?ctx.measureText(val[i]+"万").width:ctx.measureText(arrs[i]).width;if(x2<0){x2 = x2-w;}ctx.fillText(arrs[i],x2,y1); ctx.fillText(val[i]+"万",x2,y1+14); endDeg += nums[i];  //起始角度 }
}

canvas双层复合饼图及点击效果的实现相关推荐

  1. 利用Excel可视化分析,柱形图、条形图、饼图、复合饼图,圆环图、组合图、漏斗图、地图的操作方法(适合小白)

    今天收获满满,感觉学习到许多,进行归纳,整理利用Excel进行可视化分析. 如果想要进行实操,可以自己从网盘中提取数据 链接:https://pan.baidu.com/s/1xus3KRlXvLXo ...

  2. 鼠标点击特效:canvas点击效果

    JS代码(代码中包含了sketch.min.js的源码,如果你的网站已经引用了,请删掉下面的6到7行.): /** 鼠标点击特效:canvas点击效果*/ /* Copyright (C) 2013 ...

  3. 微信小程序之图表系列——canvas绘制饼状图,带点击效果

    一图胜千言,相信很多开发者都没有绕开过图表制作这个坑,在小程序中也是,当然可以用第三方echart等制图插件来做,但小程序要求代码量最大12M,还得分好几个包,一个echart插件就将近1M,要是只做 ...

  4. Excel 作复合饼图和双轴柱形图

    复合饼图1.点击"插入-饼图",在"二维饼图"下面选"复合条饼图",即插入了一个空白的图表. 2.右击这个图表,在菜单中选"选择数 ...

  5. 博客园自定义页面风格设计 后续篇(页面设计模式及代码高亮 鼠标点击效果升级)...

    前言 在之前所写过的博客园自定义页面风格设计篇中,我们已经说明了其中两种风格的页面设计,鼠标图案的修改,公告栏的设置,背景音乐的制作,关于CSS以及用Canvas和requestAnimFrame做动 ...

  6. android l 效果,[原]Android L中水波纹点击效果的实现

    博主参加了2014 CSDN博客之星评选,帮我投一票吧. 前言 前段时间android L(android 5.0)出来了,界面上做了一些改动,主要是添加了若干动画和一些新的控件,相信大家对view的 ...

  7. apex图表使用饼图居中_还在用单饼图展示Excel数据?一分钟学会制作复合饼图

    导读:今天要讲的Excel小技巧,关于Excel的复合饼图,饼图是用来快速展示百分比的一个优良工具,单一饼图大部分小伙伴都能够制作出来,其实在Excel中还有另外一种饼图--复合饼图,是饼图的升级版, ...

  8. Android L中水波纹点击效果的实现

    博主参加了2014 CSDN博客之星评选,帮我投一票吧. 点击给我投票 前言 前段时间android L(android 5.0)出来了,界面上做了一些改动,主要是添加了若干动画和一些新的控件,相信大 ...

  9. li 一行2个图会错乱_2分钟做出的Excel复合饼图,简洁又美观、数据更清晰!

    饼图能够形象生动的展示各种类别数据的占比,在类别较多的情况下,单一饼图会显得很凌乱,降低了数据的对比效果,这个时候复合条饼图就可以让统计结果更直观,数据更清晰. 一.复合条饼图效果 复合条饼图的特点: ...

  10. Hexo优化(鼠标点击效果+评论系统+站点统计+hexo-admin-qiniu+markdown图片存储+live2d板娘)

    欢迎访问我的博客 文章目录 欢迎访问[我的博客](http://blog.duanzy.xyz) 鼠标点击效果 鼠标点击出现桃心 鼠标点击出现爆炸烟花 评论系统 DISQUS Facebook Com ...

最新文章

  1. Mycat 读写分离 数据库分库分表 中间件 安装部署,及简单使用
  2. Servlet程序入门
  3. P1001 第K极值【tyvj】
  4. 零XML的Spring配置
  5. Redis__WindowsServer主从服务部署及调用实例
  6. 日志组件logback的介绍及配置使用方法(一)
  7. 昨天晚上学MFC的ADO,跟着书上的讲解和例子,完成了ADO的初级使用。
  8. matlab做信号实验需要安装那些模块_无人机基于Matlab/Simulink的模型开发(连载一)...
  9. 方法参数关键字:params、ref及out
  10. 关闭linux日志功能后性能测试
  11. Java基础__Integer类型中的自动装箱
  12. 测试连接数据库是否成功
  13. 区块链如何解决电商供应的问题?
  14. 单片机蜂鸣器源代码+仿真
  15. 华为网络设备——单臂路由的配置示例
  16. 详讯:微软宣布446亿美元收购雅虎
  17. 国外广告联盟前期需要准备的事情
  18. 使用BeautifulSoup爬取想要的标签(《python网络爬虫权威指南》笔记)
  19. 我的世界1.12.2 神奇宝贝(精灵宝可梦) 开服教程
  20. 矩阵分解 SVD 和 CUR的区别

热门文章

  1. cad的php文件怎么用,CAD无法弹出打开、保存等窗口,用FILEDIA解决
  2. java闰年_java 判断闰年
  3. excel自动调整列宽_Java 设置Excel自适应行高、列宽
  4. 第三方支付回调地址代理转发
  5. 正则验证正整数表达式
  6. Vue组件通信(父传子、子传父、兄弟传值)
  7. kettle简单的更新与插入
  8. pm2开启nuxt项目pm2 start ./node_modules/nuxt/bin/nuxt-start
  9. while 循环导致 IndentationError: unexpected unindent错误
  10. 调试Micrium OS应用程序时检测任务堆栈溢出的简单方法