使用Vue画饼图的全流程

一、调用饼图页面
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
    }
  }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

帝博格T-bag

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值