一、调用饼图页面
1、引入
import pieChart from '../../../../components/pieChart/pieChart'
2、注册
components: {pieChart},
3、使用
<div class="pie-list">
<pie-chart class="pie"
:cfg="taskPieCfg"></pie-chart>
<pie-chart class="pie"
:cfg="workPieCfg"></pie-chart>
</div>
4、初始化数据参数
taskPieCfg: {
domId: 'taskPie',
width: 600,
height: 400,
title: '例行任务类型分布图',
data: [],
},
workPieCfg: {
domId: 'workPie',
width: 600,
height: 400,
title: 'MPJC工作类别分布图',
data: [],
},
5、请求接口处理数据,数据结构如下
data: [
// 颜色可以自定义或者使用备用颜色
['EOJC', .10,'#000'],
['EAJC', .10,'red'],
['MPJC', .10],
['LMJC', .10],
['NRC', .10],
['NRC', .10],
['NRC', .10],
['NRC', .10],
['NRC', .10],
['NRC', .10],
]
二、编写饼图页面
1、编写结构
<div class="canvas-wrap"
:id="cfg.domId"
:style="{width:cfg.width+'px',height:cfg.height+'px'}"></div>
2、编写样式
<style type="text/less" lang="less">
.canvas-wrap {
position: relative;
min-width: 300px;
min-height: 300px;
display: inline-block;
canvas {
position: absolute;
left: 0px;
top: 0px;
height: 100%;
width: 100%;
}
}
</style>
3、接受父组件的参数
props: {
cfg: Object
},
4、监听父组件的参数 (因为参数为动态获取)
watch: {
cfg: {
handler: function (val, oldVal) {
if (val) {
this.initPie()
}
},
deep: true
}
},
5、导入饼图的对象
import { H5ComponentPie } from '../../common/js/commonMethod'
6、根据数据初始化饼图
methods: {
initPie () {
H5ComponentPie(this.cfg.domId, this.cfg);
},
}
三、编写饼图的对象
// 饼图
let H5ComponentPie = function (name, cfg) {
// canvas容器
let canvas = document.getElementById(name)
// 绘制网格线 - 背景层
var w = cfg.width
var h = cfg.height
// 创建一个背景画布
var cns = document.createElement('canvas')
var ctx = cns.getContext('2d')
cns.width = ctx.width = w
cns.height = ctx.height = h
cns.style.zIndex = 1
canvas.appendChild(cns)
var padding = 100
var leftWidth = (w - h) / 2
var r = h / 2 - padding
var ce = {
x: r + padding + leftWidth,
y: r + padding,
}
// 绘制一个背景圆
ctx.beginPath()
ctx.fillStyle = '#eee'
ctx.strokeStyle = '#eee'
ctx.lineWidth = 1
ctx.arc(ce.x, ce.y, r, 0, 2 * Math.PI)
ctx.fill()
ctx.stroke()
// 创建一个数据层 画布
var cns = document.createElement('canvas')
var ctx = cns.getContext('2d')
cns.width = ctx.width = w
cns.height = ctx.height = h
cns.style.zIndex = 2
canvas.appendChild(cns)
var colors = [
'#7ed3f4',
'#ee6666',
'#fac858',
'#91cc75',
'#5470c6',
'orange',
'#7ed3f4',
'#ee6666',
'#fac858',
'#91cc75',
] // 备用颜色
var sAngel = 2 * Math.PI // 设置开始的角度在3点位置
var eAngel = 0 // 结束弧度
var aAngel = Math.PI * 2 // 100%的圆结束的角度 2pi = 360
var cumAngel = 0 // 累积角度
// 绘制标题
ctx.beginPath()
ctx.font = '18px "微软雅黑"'
ctx.fillStyle = '#000'
ctx.textBaseline = 'middle'
ctx.textAlign = 'center'
ctx.fillText(cfg.title, ce.x, (padding - 18) / 2)
ctx.closePath()
// 绘制一个数据源
var step = cfg.data.length
for (let i = 0; i < step; i++) {
var item = cfg.data[i]
var color = item[3] || (item[3] = colors.pop())
var text = item[0] || '测试' // 文本
var per = (minX(item[2]) * 100).toFixed(1) + '%' // 百分比
var cAngle = aAngel * item[2] // 当前弧度
var curAngle = 360 * item[2] // 当前角度
// console.log('累积角度', cumAngel)
eAngel = sAngel + aAngel * item[2]
ctx.beginPath()
ctx.fillStyle = color
ctx.strokeStyle = color
ctx.lineWidth = 0.1
if (item[1] !== 0) {
// 百分比为0不画图形
// 画图形区域
ctx.moveTo(ce.x, ce.y) // 圆心的位置
ctx.arc(ce.x, ce.y, r, sAngel, eAngel)
ctx.fill()
ctx.stroke()
ctx.closePath()
// 画线区域数据
var centerAngle = cumAngel + curAngle / 2 // 当前扇形中心角度
// console.log(centerAngle)
var endDot = dotCoordinate(ce, centerAngle, r) //当前扇形中心点的坐标
var extendDot = dotCoordinate(ce, centerAngle, r + 20) // 当前扇形中心点的延长点坐标
var horLength = getHorLength(centerAngle, 20) // 延长点水平延伸的方向和距离
// 画线区域
ctx.beginPath()
ctx.moveTo(ce.x, ce.y)
ctx.lineTo(endDot.x, endDot.y)
ctx.lineTo(extendDot.x, extendDot.y)
ctx.lineTo(extendDot.x + horLength, extendDot.y)
ctx.lineWidth = 2
ctx.strokeStyle = color
ctx.stroke()
ctx.closePath()
// 画文本区域
var textHorLength = getHorLength(centerAngle, 25) // 延长点水平延伸的方向和距离
var textAlign = getTextAlign(centerAngle)
ctx.beginPath()
ctx.font = '14px "微软雅黑"'
ctx.fillStyle = color
ctx.textBaseline = 'middle'
ctx.textAlign = textAlign
ctx.fillText(
text + ',' + per + '(' + item[1] + ')',
extendDot.x + textHorLength,
extendDot.y
)
ctx.closePath()
}
// 画分类列表
var num = i % 5
var listX = 20 + getListCurX() * num // 每个分类的X轴的坐标
var row = i >= 5 ? 1 : 0
var sideLength = 10 // 小正方形的边长
var spacing = 10
var listY = h - padding + 40 + row * 30 // 分类列表第一行的Y的坐标
ctx.beginPath()
ctx.rect(listX, listY, sideLength, sideLength)
ctx.lineWidth = 2
ctx.strokeStyle = color
ctx.stroke()
ctx.fill()
ctx.closePath()
ctx.beginPath()
ctx.font = '14px "微软雅黑"'
ctx.fillStyle = '#000'
ctx.textBaseline = 'middle'
ctx.textAlign = 'start'
ctx.fillText(
text,
listX + sideLength + 5,
listY + sideLength / 2,
getListCurX()
)
ctx.closePath()
// 获取圆点上的坐标
function dotCoordinate(ce, degree, r) {
// ce 圆心坐标,degree 当前角度,当前半径
let x = ce.x + Math.cos(((Math.PI * 2) / 360) * degree) * r
let y = ce.y + Math.sin(((Math.PI * 2) / 360) * degree) * r
return { x, y }
}
// 返回水平长度
function getHorLength(cumAngel, length) {
switch (true) {
case cumAngel <= 90 || cumAngel >= 270:
return length
case cumAngel > 90 && cumAngel < 270:
return -length
}
}
// 返回左侧汉字的对齐方式
function getTextAlign(cumAngel) {
let align
switch (true) {
case 0 < cumAngel && cumAngel <= 90:
align = 'start'
break
case 90 < cumAngel && cumAngel <= 180:
align = 'end'
break
case 180 < cumAngel && cumAngel < 270:
align = 'end'
break
case 270 <= cumAngel && cumAngel <= 360:
align = 'start'
break
}
return align
}
// 返回分类列表每列的X轴的坐标
function getListCurX() {
let x
if (step > 5) {
x = (w - 40) / 5
} else {
x = (w - 40) / step
}
return x
}
cumAngel = cumAngel + curAngle
sAngel = eAngel
// 设置最小百分比
function minX(x, defaultX) {
defaultX = defaultX || 0.001
if(x < defaultX){
return defaultX
}
return x
}
}
}