canvas(六)绘制带说明的饼图
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(六)绘制带说明的饼图的更多相关文章
- Canvas上绘制几何图形
重要的类自定义View组件要重写View组件的onDraw(Canvase)方法,接下来是在该 Canvas上绘制大量的几何图形,点.直线.弧.圆.椭圆.文字.矩形.多边形.曲线.圆角矩形,等各种形状 ...
- Turtle绘制带颜色和字体的图形(Python3)
转载自https://blog.csdn.net/wumenglu1018/article/details/78184930 在Python中有很多编写图形程序的方法,一个简单的启动图形化程序设计的方 ...
- openLayers 4 canvas图例绘制,canvas循环添加图片,解决图片闪烁问题
一.问题来源: 接触Openlayers 一段时间了,最近做了一个农业产业系统,项目中涉及到产业图例,最后考虑用canvas来绘制图例图像.当中带图片的图例移动时,图片会实现闪烁留白情况.闪烁是因为绘 ...
- 自定义控件之Canvas图形绘制基础练习-青春痘笑脸^_^
对于自定义控件的意义不言而喻,所以对它的深入研究是很有必要的,前些年写过几篇关于UI效果的学习过程,但是中途比较懒一直就停滞了,而对于实际工作还是面试来说系统深入的了解自定义控件那是很有必要的,所以接 ...
- Canvas:绘制曲线
前言 画曲线要用到二次贝塞尔曲线或三次贝塞尔曲线.贝塞尔曲线是计算机图形学中相当重要的参数曲线,在一些比较成熟的位图软件中也有贝塞尔曲线工具,如 PhotoShop. 二次贝塞尔曲线 二次贝塞尔曲线在 ...
- canvas快速绘制圆形、三角形、矩形、多边形
想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...
- 用html5的canvas画布绘制贝塞尔曲线
查看效果:http://keleyi.com/keleyi/phtml/html5/7.htm 完整代码: <!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...
- HTML5在canvas中绘制复杂形状附效果截图
HTML5在canvas中绘制复杂形状附效果截图 一.绘制复杂形状或路径 在简单的矩形不能满足需求的情况下,绘图环境提供了如下方法来绘制复杂的形状或路径. beginPath() : 开始绘制一个新路 ...
- Canvas 2D绘制抗锯齿的1px线条
当绘制1像素的线条时,发现多条线明显存在着粗细不均的问题,线条带有明显的锯齿. 事实上,Canvas的绘制线条指令都存在这个状况,如lineTo,arcTo,strokeRect. 解决方案是将Can ...
- HTML5 canvas标签绘制正三角形 鼠标按下点为中间点,鼠标抬起点为其中一个顶点
用html5的canvas标签绘制圆.矩形比较容易,绘制三角形,坐标确定相当于前面两种难点,这里绘制的是正三角形,比较容易,我们只需要把鼠标刚按下去的点设置为三角形的中心点,鼠标抬起的点设置为三角形右 ...
随机推荐
- SQL Server 语句日期格式查找方法
1. SQL Server中,处理日期格式和查找特定日期格式方法示例 在SQL Server中,处理日期格式和查找特定日期格式的记录是一个常见的需求.SQL Server提供了多种函数和格式选项来处理 ...
- [33](CSP 集训)CSP-S 模拟 4
A 商品 对于任意一组相邻的数 \((l,r)\ (l\le r)\),可以发现,不管怎么压缩,都会有 \(l\le r\),也就是说,相邻两个数的大小关系是不变的 那么我们就可以把 \(\sum(| ...
- Windows 10 LTSC 2019(1809) WSL 安装 CentOS 7
1.安装WSL 通过控制面板--程序和功能--启用或关闭WIndows功能,勾选"适用于Linux的Windows子系统". 或者通过管理员权限打开 PowerShel ...
- CPU缓存伪共享
CPU缓存什么东西?当然这个问题很多人有可能觉得比较傻,CPU缓存什么,肯定是缓存数据(代码)啊,要不然还能缓存啥,这个确实没问题,但是CPU到底缓存什么样的数据呢?因为对CPU来说,无论是指令,还是 ...
- 这十年我与广告不共戴天练就的十八般武艺 #PC去广告 #手机去广告
背景 大家应该都体会过广告的苦恼,比如看着好看的电视,突然给播放广告,这时候痛苦系数飙升.随着社会进步,广告的载体,还有形式也越来越多,比如手机端各种APP启动广告,PC端软件弹窗,网站Banner等 ...
- 解决数据库表的字段id中间自增断层问题(删除自增主键其中的任意一条数据后,再次插入数据发现id排序出现问题)
万能解决办法: 先将该表的id字段删除,然后再重新按照见表需求创建该字段 注意!!!!!!!!!!!!! 注意!!!!!!!!!!!!! 注意!!!!!!!!!!!!! 删除之前一定要复制建表时候的S ...
- P3571 [POI2014] SUP-Supercomputer 题解
P3571「POI2014」SUP-Supercomputer 题解 一道 "较" 水的黑题 (可一开始苦思冥想还是不会). 本蒟蒻的第一篇黑题题解,求赞. 题意简化 给定一棵 \ ...
- 用故事说透HTTPS
本文来自素燕公众号,原文地址:用故事说透https 故事中的主演: 小华今年上大一,这是她第一次离开父母,独自一人到北京上学.今天妈妈的生日,想了想要给妈妈一个祝福,便给妈妈发了条消息: 妈妈收到这条 ...
- SpringBoot用户头像上传
1.上传到本地服务器 controller层主要以MultipartFile接收即可,这里返回给前端的该文件保存后的相对路径 @RequestMapping(value = "/applic ...
- 小程序框架开发笔记-wepy
WePY 一.前置 本地使用版本V1.7.3最新版本V2.x 二.使用 export class App extends wepy.app {} 小程序入口,App基类 wepy ...