1.前言

  • 将以下数据渲染成饼图,数据格式:
var data = [
{value:"10",title:"16-22的年龄人数"},
{value:"15",title:"23-30的年龄人数"},
{value:"25",title:"31-35的年龄人数"},
{value:"10",title:"36及以上的年龄人数"}
]

2.代码

  • 实例化传入dom,内部变量初始化
  • 调用渲染方法,传入数据
  • 接收传入的数据,进行初始化(区间占比,起始角度,结束角度,颜色)
  • 渲染扇形模块
  • 渲染图例模块
  • 渲染延长线和文字模块
<script>
//构造函数
var PieChart = function(dom) {
//初始化
this.init(dom)
} //初始化方法
PieChart.prototype.init = function(dom){
//初始化dom
var dom = dom || document.querySelector('canvas')
//初始化绘图工具
this.ctx = dom && dom.getContext("2d") //以下配置暂时写死
//获取画布宽高
this.w = this.ctx.canvas.width
this.h = this.ctx.canvas.height
//饼图的圆心和半径
this.x0 = this.w / 2
this.y0 = this.h / 2
this.r = 150 //半径
//延长线的长度
this.outLineWidth = this.r + 20
//图例大小及间距
this.legend_width = 30
this.legend_height = 16
this.legend_space = 10 //初始化文本对其方式
this.ctx.textAlign = 'left' //设置字体(设置字体要在计算字体宽度之前)
this.ctx.font = '12px 微软雅黑'
} //接收数据,执行渲染
PieChart.prototype.render = function(data = []){
//缓存数据
this._data = JSON.parse(JSON.stringify(data))
this.data = JSON.parse(JSON.stringify(data))
//区间信息初始化
this.dataInit()
//绘制扇形
this.drawSector()
//绘制图例
this.drawLegend()
//绘制延长线及说明
this.drawText()
} //获取随机颜色
PieChart.prototype.getRandomColor = function() {
var r = Math.round(Math.random() * 255)
var g = Math.round(Math.random() * 255)
var b = Math.round(Math.random() * 255)
return 'rgb(' + r + ',' + g + ',' + b + ')'
} //为区间挂载角度信息
PieChart.prototype.dataInit = function() {
//计算总数
var total = data.reduce((total,item) => {
return total + Number(item.value)
},0) //遍历前数据
var startRadian = 0 //开始角度
var endRadian = 0 //结束角度
//遍历数据
for(var i=0;i<this.data.length;i++){
//占比
var proportion = this.data[i].value/total
//区间所占角度
var radian = Math.PI * 2 * proportion
//开始角度 = 上一次结束角度
startRadian = endRadian
//结束角度 = 开始角度 + 区间所占角度
endRadian = startRadian + radian
//颜色
var color = this.getRandomColor()
//挂载到数据中
this.data[i].proportion = proportion
this.data[i].radian = radian
this.data[i].startRadian = startRadian
this.data[i].endRadian = endRadian
this.data[i].color = color
}
} //绘制扇形
PieChart.prototype.drawSector = function(){
//遍历数据
for(var i=0;i<this.data.length;i++){
//当前区间数据
var section_data = this.data[i]
//开启路径
this.ctx.beginPath()
//设置填充颜色
this.ctx.fillStyle = this.data[i].color
//绘制曲线(角度直接读取data)
this.ctx.arc(this.x0, this.y0, this.r, section_data.startRadian, section_data.endRadian)
//延时到原点,以便绘制扇形
this.ctx.lineTo(this.x0, this.y0)
//填充
this.ctx.fill()
}
} //绘制图例
PieChart.prototype.drawLegend = function(){
//初始化垂直对其方式
this.ctx.textBaseline = 'middle'
//默认一行一个图例,一次往下移动
var item_center_y = -this.legend_height //当前图例y轴坐标
//提示
//遍历数据
for(var i=0;i<this.data.length;i++){
//开启新路径
this.ctx.beginPath()
//设置颜色
this.ctx.fillStyle = this.data[i].color
//标题
var title = this.data[i].title
//当前y轴坐标(依次往下移动)
item_center_y += (this.legend_space + this.legend_height)
//绘制矩形
this.ctx.fillRect(this.legend_space, item_center_y, this.legend_width, this.legend_height)
//绘制文字,文字的基点y坐标为矩形的垂直方向的中心
this.ctx.fillText(title, this.legend_space + this.legend_width + 10, item_center_y + this.legend_height/2)
}
//还原垂直对其方式
this.ctx.textBaseline = 'baseline'
} //绘制延长线及说明
PieChart.prototype.drawText = function(){
//初始化垂直对其方式
this.ctx.textBaseline = 'bottom'
//遍历数据
for(var i=0;i<this.data.length;i++){
//当前区间数据
var section_data = this.data[i]
//标题内容
var title = section_data.title //计算中心角度
var middleRadian = (section_data.startRadian + section_data.endRadian) / 2
//延长线坐标(起点坐标为圆心,终点坐标根据正弦 余弦计算)
var outX = this.x0 + this.outLineWidth * Math.cos(middleRadian)
var outY = this.y0 + this.outLineWidth * Math.sin(middleRadian) //优化文字对齐方式
if (outX >= this.x0) {
//在右边
var underlineX = outX + this.ctx.measureText(title).width
this.ctx.textAlign = 'left'
} else {
//在左边
var underlineX = outX - this.ctx.measureText(title).width
this.ctx.textAlign = 'right'
} //绘制延长线
//开启路径
this.ctx.beginPath()
//设置颜色
this.ctx.strokeStyle = this.data[i].color
this.ctx.fillStyle = this.data[i].color
//绘制直线
this.ctx.moveTo(this.x0, this.y0)
this.ctx.lineTo(outX, outY)
//绘制下划线
this.ctx.lineTo(underlineX, outY)
this.ctx.stroke() //绘制文本
this.ctx.beginPath()
this.ctx.fillText(title, outX, outY)
} //还原垂直对其方式
this.ctx.textBaseline = 'baseline'
}
</script>

3.调用

  • 数据随机生成
<script>
var box = document.querySelector("canvas") var data = [
{value:0,title:"16-22的年龄人数"},
{value:0,title:"23-30的年龄人数"},
{value:0,title:"31-35的年龄人数"},
{value:0,title:"36及以上的年龄人数"}
] //模拟数据(产生50个数据)
var num = 50
for(var i=0;i<num;i++){
//随机产生16-40的整数
var age = Math.floor(16 + Math.random()*25)
if(age < 23){
data[0].value++
}else if(age < 31){
data[1].value++
}else if(age < 36){
data[2].value++
}else{
data[3].value++
}
} //创建饼图对象
var pieChart = new PieChart(box)
//传入数据开始绘制饼图
pieChart.render(data)
</script>

4.效果图

5.ES6类语法

<script>
class PieChart{
//构造器
constructor(dom){
//初始化
this.init(dom)
} //初始化方法
init(dom){
//初始化dom
var dom = dom || document.querySelector('canvas')
//初始化绘图工具
this.ctx = dom && dom.getContext("2d") //以下配置暂时写死
//获取画布宽高
this.w = this.ctx.canvas.width
this.h = this.ctx.canvas.height
//饼图的圆心和半径
this.x0 = this.w / 2
this.y0 = this.h / 2
this.r = 150 //半径
//延长线的长度
this.outLineWidth = this.r + 20
//图例大小及间距
this.legend_width = 30
this.legend_height = 16
this.legend_space = 10 //初始化文本对其方式
this.ctx.textAlign = 'left' //设置字体(设置字体要在计算字体宽度之前)
this.ctx.font = '12px 微软雅黑'
} //接收数据,执行渲染
render(data = []){
//缓存数据
this._data = JSON.parse(JSON.stringify(data))
this.data = JSON.parse(JSON.stringify(data))
//区间信息初始化
this.dataInit()
//绘制扇形
this.drawSector()
//绘制图例
this.drawLegend()
//绘制延长线及说明
this.drawText()
} //获取随机颜色
getRandomColor() {
var r = Math.round(Math.random() * 255)
var g = Math.round(Math.random() * 255)
var b = Math.round(Math.random() * 255)
return 'rgb(' + r + ',' + g + ',' + b + ')'
} //为区间挂载角度信息
dataInit() {
//计算总数
var total = data.reduce((total,item) => {
return total + Number(item.value)
},0) //遍历前数据
var startRadian = 0 //开始角度
var endRadian = 0 //结束角度
//遍历数据
for(var i=0;i<this.data.length;i++){
//占比
var proportion = this.data[i].value/total
//区间所占角度
var radian = Math.PI * 2 * proportion
//开始角度 = 上一次结束角度
startRadian = endRadian
//结束角度 = 开始角度 + 区间所占角度
endRadian = startRadian + radian
//颜色
var color = this.getRandomColor()
//挂载到数据中
this.data[i].proportion = proportion
this.data[i].radian = radian
this.data[i].startRadian = startRadian
this.data[i].endRadian = endRadian
this.data[i].color = color
}
} //绘制扇形
drawSector(){
//遍历数据
for(var i=0;i<this.data.length;i++){
//当前区间数据
var section_data = this.data[i]
//开启路径
this.ctx.beginPath()
//设置填充颜色
this.ctx.fillStyle = this.data[i].color
//绘制曲线(角度直接读取data)
this.ctx.arc(this.x0, this.y0, this.r, section_data.startRadian, section_data.endRadian)
//延时到原点,以便绘制扇形
this.ctx.lineTo(this.x0, this.y0)
//填充
this.ctx.fill()
}
} //绘制图例
drawLegend(){
//初始化垂直对其方式
this.ctx.textBaseline = 'middle'
//默认一行一个图例,一次往下移动
var item_center_y = -this.legend_height //当前图例y轴坐标
//提示
//遍历数据
for(var i=0;i<this.data.length;i++){
//开启新路径
this.ctx.beginPath()
//设置颜色
this.ctx.fillStyle = this.data[i].color
//标题
var title = this.data[i].title
//当前y轴坐标(依次往下移动)
item_center_y += (this.legend_space + this.legend_height)
//绘制矩形
this.ctx.fillRect(this.legend_space, item_center_y, this.legend_width, this.legend_height)
//绘制文字,文字的基点y坐标为矩形的垂直方向的中心
this.ctx.fillText(title, this.legend_space + this.legend_width + 10, item_center_y + this.legend_height/2)
}
//还原垂直对其方式
this.ctx.textBaseline = 'baseline'
} //绘制延长线及说明
drawText(){
//初始化垂直对其方式
this.ctx.textBaseline = 'bottom'
//遍历数据
for(var i=0;i<this.data.length;i++){
//当前区间数据
var section_data = this.data[i]
//标题内容
var title = section_data.title //计算中心角度
var middleRadian = (section_data.startRadian + section_data.endRadian) / 2
//延长线坐标(起点坐标为圆心,终点坐标根据正弦 余弦计算)
var outX = this.x0 + this.outLineWidth * Math.cos(middleRadian)
var outY = this.y0 + this.outLineWidth * Math.sin(middleRadian) //优化文字对齐方式
if (outX >= this.x0) {
//在右边
var underlineX = outX + this.ctx.measureText(title).width
this.ctx.textAlign = 'left'
} else {
//在左边
var underlineX = outX - this.ctx.measureText(title).width
this.ctx.textAlign = 'right'
} //绘制延长线
//开启路径
this.ctx.beginPath()
//设置颜色
this.ctx.strokeStyle = this.data[i].color
this.ctx.fillStyle = this.data[i].color
//绘制直线
this.ctx.moveTo(this.x0, this.y0)
this.ctx.lineTo(outX, outY)
//绘制下划线
this.ctx.lineTo(underlineX, outY)
this.ctx.stroke() //绘制文本
this.ctx.beginPath()
this.ctx.fillText(title, outX, outY)
} //还原垂直对其方式
this.ctx.textBaseline = 'baseline'
}
}
</script>

canvas(六)绘制带说明的饼图的更多相关文章

  1. Canvas上绘制几何图形

    重要的类自定义View组件要重写View组件的onDraw(Canvase)方法,接下来是在该 Canvas上绘制大量的几何图形,点.直线.弧.圆.椭圆.文字.矩形.多边形.曲线.圆角矩形,等各种形状 ...

  2. Turtle绘制带颜色和字体的图形(Python3)

    转载自https://blog.csdn.net/wumenglu1018/article/details/78184930 在Python中有很多编写图形程序的方法,一个简单的启动图形化程序设计的方 ...

  3. openLayers 4 canvas图例绘制,canvas循环添加图片,解决图片闪烁问题

    一.问题来源: 接触Openlayers 一段时间了,最近做了一个农业产业系统,项目中涉及到产业图例,最后考虑用canvas来绘制图例图像.当中带图片的图例移动时,图片会实现闪烁留白情况.闪烁是因为绘 ...

  4. 自定义控件之Canvas图形绘制基础练习-青春痘笑脸^_^

    对于自定义控件的意义不言而喻,所以对它的深入研究是很有必要的,前些年写过几篇关于UI效果的学习过程,但是中途比较懒一直就停滞了,而对于实际工作还是面试来说系统深入的了解自定义控件那是很有必要的,所以接 ...

  5. Canvas:绘制曲线

    前言 画曲线要用到二次贝塞尔曲线或三次贝塞尔曲线.贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如 PhotoShop. 二次贝塞尔曲线 二次贝塞尔曲线在 ...

  6. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  7. 用html5的canvas画布绘制贝塞尔曲线

    查看效果:http://keleyi.com/keleyi/phtml/html5/7.htm 完整代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...

  8. HTML5在canvas中绘制复杂形状附效果截图

    HTML5在canvas中绘制复杂形状附效果截图 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,绘图环境提供了如下方法来绘制复杂的形状或路径. beginPath() : 开始绘制一个新路 ...

  9. Canvas 2D绘制抗锯齿的1px线条

    当绘制1像素的线条时,发现多条线明显存在着粗细不均的问题,线条带有明显的锯齿. 事实上,Canvas的绘制线条指令都存在这个状况,如lineTo,arcTo,strokeRect. 解决方案是将Can ...

  10. HTML5 canvas标签绘制正三角形 鼠标按下点为中间点,鼠标抬起点为其中一个顶点

    用html5的canvas标签绘制圆.矩形比较容易,绘制三角形,坐标确定相当于前面两种难点,这里绘制的是正三角形,比较容易,我们只需要把鼠标刚按下去的点设置为三角形的中心点,鼠标抬起的点设置为三角形右 ...

随机推荐

  1. sicp每日一题[2.13-2.16]

    Exercise 2.13 Show that under the assumption of small percentage tolerances there is a simple formul ...

  2. linux操作系统和文件系统,命令(上)

    Linux是一个类似于windows的操作系统 Linux操作系统的一种主要使用方式是通过终端软件:终端软件里只能使用键盘不能使用鼠标,在终端软件里通过输入命令完成各种任务 clear命令可以删除终端 ...

  3. 解决 SHADERed无法打开,报错 缺失XINPUT1_4.DLL 文件的办法

    起因: 在给某个不知名的同事安装软件时遇到的一个问题,安装完成后打开软件报错:SHADERed无法打开,报错 缺失XINPUT1_4.DLL ,C++依赖项均已打上,卸载C++插件后依然报错只能找缺失 ...

  4. AntDesign-Vue Table 查询与分页

    前言 之前的增删改查小 Demo 已经快要进行到最后一步了,这节的任务是将请求数据的方式改为 分页,并且增加 分页条件查询 的功能. 页面布局 <a-table :data-source=&qu ...

  5. 如何快速定位 Linux Panic 出错的代码行

    问题描述 内核调试中最常见的一个问题是:内核Panic后,如何快速定位到出错的代码行? 就是这样一个常见的问题,面试过的大部分同学都未能很好地回答,这里希望能够做很彻底地解答. 问题分析 内核Pani ...

  6. Vue3 和 Vue2 的区别 ?

    1. Vue3 和 VUe2 性能提升 :使用 proxy 代替 defainProperty 实现响应式数据 :使用 ts 书写代码 : 新特性有:组合 api compositionApi  :新 ...

  7. 云原生爱好者周刊:GitHub 官方文档终于开源了!

    云原生一周动态要闻: API 在 Kubernetes 1.22 中被删除 ContainIQ 公开发布 - Kubernetes 本地实时监控! Sophos 收购 Capsule8 开源项目推荐 ...

  8. cornerstone中RAFT的buffer的实现

    1.概览: 谈到raft协议实现就绕不开网上流行的mit6.824,但其为go语言,官方没有lab的答案,框架也很晦涩难懂,且全网没有一个博客对其有清晰的解释,有的只是甩一堆名词然后直接贴没有任何注释 ...

  9. AI五子棋_09 消息循环实现自动对局

    AI五子棋 第九步 恭喜你到达第九步! 上一步我们已经完成了一个AI大脑的最核心功能.我们可以用它来对战了. 访问服务器 http://202.207.12.156:9012/join_game,会返 ...

  10. spring boot--@Value注解失效

    接手一个任务开发预警邮件需求,计划将邮件信息(hostName,用户名,密码,发送方,接受方等)设置为可配置变量,配置在配置中心,使用@Value注解获取配置,如下: @Value("${w ...