Echarts-3d饼图

<template>
  <div :id="echartsId"></div>
</template>
<script>
import 'echarts-gl'
var echarts = require('echarts')
export default {
  props: {
    echartsId: {
      type: String,
      default: 'chart-panel'
    },
    k: {
      // 内外径之比
      type: Number,
      default: 1 / 3
    },
    size: {
      type: Number,
      default: 0.5
    },
    value: {
      type: Array,
      default: () => []
    }
  },
  data () {
    return {
      echarts: null
    }
  },
  watch: {
    value: {
      handler () {
        this.drawPie()
        this.$nextTick(() => {
          this.echarts.resize()
        })
      },
      deep: true
    }
  },
  mounted () {
    this.echarts = echarts.init(document.getElementById(this.echartsId))
    // this.drawPie()
  },
  beforeDestroy () {
    this.echarts.dispose()
    this.echarts = null
  },
  methods: {
    /** ***********************
pie3D 尝试

更新时间: 2020.8.6 17:12 v1.1
使用组件: grid3D、xAxis3D、yAxis3D、zAxis3D、surface
 EC 版本: 4.8.0
 GL 版本:0.4.3
 公众号 : ZXand618的ECharts之旅
 知乎号 : ZhXand618
如果转载: 请注明出处

*************************
【 getParametricEquation 函数说明 】 :
*************************
    根据传入的
    startRatio(浮点数): 当前扇形起始比例,取值区间 [0, endRatio)
    endRatio(浮点数): 当前扇形结束比例,取值区间 (startRatio, 1]
    isSelected(布尔值):是否选中,效果参照二维饼图选中效果(单选)
    isHovered(布尔值): 是否放大,效果接近二维饼图高亮(放大)效果(未能实现阴影)
    k(0~1之间的浮点数):用于参数方程的一个参数,取值 0~1 之间,通过「内径/外径」的值换算而来。

    生成 3D 扇形环曲面

*************************
【 getPie3D 函数说明 】 :
*************************
    根据传入的
    pieData(object):饼图数据
    internalDiameterRatio(0~1之间的浮点数):内径/外径的值(默认值 1/2),当该值等于 0 时,为普通饼图

    生成模拟 3D 饼图的配置项 option

    备注:饼图数据格式示意如下
    [{
        name: '数据1',
        value: 10
    }, {
        // 数据项名称
        name: '数据2',
        value : 56,
        itemStyle:{
            // 透明度
            opacity: 0.5,
            // 扇形颜色
            color: 'green'
        }
    }]

*************************
【 鼠标事件监听说明 】 :
*************************
    click: 实现饼图的选中效果(单选)
            大致思路是,通过监听点击事件,获取到被点击数据的系列序号 params.seriesIndex,
            然后将对应扇形向外/向内移动 10% 的距离。

    mouseover: 近似实现饼图的高亮(放大)效果
            大致思路是,在饼图外部套一层透明的圆环,然后监听 mouseover 事件,获取
            到对应数据的系列序号 params.seriesIndex 或系列名称 params.seriesName,
            如果鼠标移到了扇形上,则先取消高亮之前的扇形(如果有),再高亮当前扇形;
            如果鼠标移到了透明圆环上,则只取消高亮之前的扇形(如果有),不做任何高亮。

    globalout: 当鼠标移动过快,直接划出图表区域时,有可能监听不到透明圆环的 mouseover,
            导致此前高亮没能取消,所以补充了对 globalout 的监听。

*************************/
    drawPie () {
      const self = this

      // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
      function getParametricEquation (startRatio, endRatio, isSelected, isHovered, k) {
        // 计算
        const midRatio = (startRatio + endRatio) / 2

        const startRadian = startRatio * Math.PI * 2
        const endRadian = endRatio * Math.PI * 2
        const midRadian = midRatio * Math.PI * 2

        // 如果只有一个扇形,则不实现选中效果。
        if (startRatio === 0 && endRatio === 1) {
          isSelected = false
        }

        // 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
        k = typeof k !== 'undefined' ? k : 1 / 3

        // 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
        const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
        const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0

        // 计算高亮效果的放大比例(未高亮,则比例为 1)
        const hoverRate = isHovered ? self.size * 1.05 : self.size * 1

        // 返回曲面参数方程
        return {
          u: {
            min: -Math.PI,
            max: Math.PI * 3,
            step: Math.PI / 32
          },

          v: {
            min: 0,
            max: Math.PI * 2,
            step: Math.PI / 20
          },

          x: function (u, v) {
            if (u < startRadian) {
              return offsetX + Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            if (u > endRadian) {
              return offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate
          },

          y: function (u, v) {
            if (u < startRadian) {
              return offsetY + Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            if (u > endRadian) {
              return offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
            }
            return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate
          },

          z: function (u, v) {
            if (u < -Math.PI * 0.5) {
              return Math.sin(u)
            }
            if (u > Math.PI * 2.5) {
              return Math.sin(u)
            }
            return Math.sin(v) > 0 ? 1 : -1
          }
        }
      }

      // 生成模拟 3D 饼图的配置项
      function getPie3D (pieData, internalDiameterRatio) {
        const series = []
        let sumValue = 0
        let startValue = 0
        let endValue = 0
        const legendData = []
        const k =
          typeof internalDiameterRatio !== 'undefined'
            ? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
            : 1 / 3

        // 为每一个饼图数据,生成一个 series-surface 配置
        for (let i = 0; i < pieData.length; i++) {
          sumValue += pieData[i].value

          const seriesItem = {
            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
            type: 'surface',
            parametric: true,
            wireframe: {
              show: false
            },
            pieData: pieData[i],
            pieStatus: {
              selected: false,
              hovered: false,
              k: k
            }
          }

          if (typeof pieData[i].itemStyle !== 'undefined') {
            const itemStyle = {}

            typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null
            typeof pieData[i].itemStyle.opacity !== 'undefined'
              ? (itemStyle.opacity = pieData[i].itemStyle.opacity)
              : null

            seriesItem.itemStyle = itemStyle
          }
          series.push(seriesItem)
        }

        // 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
        // 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
        for (let i = 0; i < series.length; i++) {
          endValue = startValue + series[i].pieData.value

          if (sumValue > 0) {
            series[i].pieData.startRatio = startValue / sumValue
            series[i].pieData.endRatio = endValue / sumValue
          } else {
            // sumValue 为0,各项值均为0
            series[i].pieData.startRatio = (1 / series.length) * i
            series[i].pieData.endRatio = (1 / series.length) * (i + 1)
          }
          series[i].parametricEquation = getParametricEquation(
            series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,
            false,
            k
          )

          startValue = endValue

          legendData.push(series[i].name)
        }

        // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
        series.push({
          name: '',
          type: 'surface',
          parametric: true,
          wireframe: {
            show: false
          },
          itemStyle: {
            opacity: 0
          },
          parametricEquation: {
            u: {
              min: 0,
              max: Math.PI * 2,
              step: Math.PI / 20
            },
            v: {
              min: 0,
              max: Math.PI,
              step: Math.PI / 20
            },
            x: function (u, v) {
              return Math.sin(v) * Math.sin(u) + Math.sin(u)
            },
            y: function (u, v) {
              return Math.sin(v) * Math.cos(u) + Math.cos(u)
            },
            z: function (u, v) {
              return Math.cos(v) > 0 ? 0.1 : -0.1
            }
          }
        })
        // 准备待返回的配置项,把准备好的 legendData、series 传入。
        const option = {
          // animation: false,
          tooltip: {
            formatter: (params) => {
              if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== '') {
                return `<span>${params.marker}${params.seriesName}<span style="margin-left:10px">${
                  option.series[params.seriesIndex].pieData.value
                }</span></span>`
                // return `${
                //   params.seriesName
                // }<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${
                //   params.color
                // };"></span>${option.series[params.seriesIndex].pieData.value}`
              }
            }
          },
          //   title: {
          //     text: '年审核问题分布情况',
          //     x: 'center',
          //     top: '20',
          //     textStyle: {
          //       color: '#fff',
          //       fontSize: 22
          //     }
          //   },
          legend: {
            icon: 'circle',
            itemWidth: 10,
            itemHeight: 10,
            itemGap: 10,
            data: legendData,
            // left: '4%',
            top: '10%',
            textStyle: {
              fontSize: 12,
              color: '#fff'
            }
          },
          backgroundColor: 'rgba(0,0,0,0)',
          xAxis3D: {
            min: -1,
            max: 1
          },
          yAxis3D: {
            min: -1,
            max: 1
          },
          zAxis3D: {
            min: -1,
            max: 1
          },
          grid3D: {
            show: false,
            boxHeight: 15,
            // top: '30%',
            bottom: '50%',
            viewControl: {
              // 3d效果可以放大、旋转等,请自己去查看官方配置
              alpha: 30,
              //   beta: -60,
              rotateSensitivity: 0,
              zoomSensitivity: 0,
              panSensitivity: 0,
              autoRotate: false
            }
          },
          series: series
        }
        return option
      }

      // 传入数据生成 option
      const option = getPie3D(this.value, self.k)

      self.echarts.setOption(option, true)
      // 监听鼠标事件,实现饼图选中效果(单选),近似实现高亮(放大)效果。
      let selectedIndex = ''
      let hoveredIndex = ''

      // 监听点击事件,实现选中效果(单选)
      self.echarts.on('click', function (params) {
        // 从 option.series 中读取重新渲染扇形所需的参数,将是否选中取反。
        const isSelected = !option.series[params.seriesIndex].pieStatus.selected
        const isHovered = option.series[params.seriesIndex].pieStatus.hovered
        const k = option.series[params.seriesIndex].pieStatus.k
        const startRatio = option.series[params.seriesIndex].pieData.startRatio
        const endRatio = option.series[params.seriesIndex].pieData.endRatio

        // 如果之前选中过其他扇形,将其取消选中(对 option 更新)
        if (selectedIndex !== '' && selectedIndex !== params.seriesIndex) {
          option.series[selectedIndex].parametricEquation = getParametricEquation(
            option.series[selectedIndex].pieData.startRatio,
            option.series[selectedIndex].pieData.endRatio,
            false,
            false,
            k
          )
          option.series[selectedIndex].pieStatus.selected = false
        }

        // 对当前点击的扇形,执行选中/取消选中操作(对 option 更新)
        option.series[params.seriesIndex].parametricEquation = getParametricEquation(
          startRatio,
          endRatio,
          isSelected,
          isHovered,
          k
        )
        option.series[params.seriesIndex].pieStatus.selected = isSelected

        // 如果本次是选中操作,记录上次选中的扇形对应的系列号 seriesIndex
        isSelected ? (selectedIndex = params.seriesIndex) : null

        // 使用更新后的 option,渲染图表
        self.echarts.setOption(option)
      })

      // 监听 mouseover,近似实现高亮(放大)效果
      self.echarts.on('mouseover', function (params) {
        // 准备重新渲染扇形所需的参数
        let isSelected
        let isHovered
        let startRatio
        let endRatio
        let k

        // 如果触发 mouseover 的扇形当前已高亮,则不做操作
        if (hoveredIndex === params.seriesIndex) {
          return

          // 否则进行高亮及必要的取消高亮操作
        } else {
          // 如果当前有高亮的扇形,取消其高亮状态(对 option 更新)
          if (hoveredIndex !== '') {
            // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 false。
            isSelected = option.series[hoveredIndex].pieStatus.selected
            isHovered = false
            startRatio = option.series[hoveredIndex].pieData.startRatio
            endRatio = option.series[hoveredIndex].pieData.endRatio
            k = option.series[hoveredIndex].pieStatus.k

            // 对当前点击的扇形,执行取消高亮操作(对 option 更新)
            option.series[hoveredIndex].parametricEquation = getParametricEquation(
              startRatio,
              endRatio,
              isSelected,
              isHovered,
              k
            )
            option.series[hoveredIndex].pieStatus.hovered = isHovered

            // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
            hoveredIndex = ''
          }

          // 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)
          if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== '') {
            // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
            isSelected = option.series[params.seriesIndex].pieStatus.selected
            isHovered = true
            startRatio = option.series[params.seriesIndex].pieData.startRatio
            endRatio = option.series[params.seriesIndex].pieData.endRatio
            k = option.series[params.seriesIndex].pieStatus.k

            // 对当前点击的扇形,执行高亮操作(对 option 更新)
            option.series[params.seriesIndex].parametricEquation = getParametricEquation(
              startRatio,
              endRatio,
              isSelected,
              isHovered,
              k
            )
            option.series[params.seriesIndex].pieStatus.hovered = isHovered

            // 记录上次高亮的扇形对应的系列号 seriesIndex
            hoveredIndex = params.seriesIndex
          }

          // 使用更新后的 option,渲染图表
          self.echarts.setOption(option)
        }
      })

      // 修正取消高亮失败的 bug
      self.echarts.on('globalout', function () {
        if (hoveredIndex !== '') {
          // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
          const isSelected = option.series[hoveredIndex].pieStatus.selected
          const isHovered = false
          const k = option.series[hoveredIndex].pieStatus.k
          const startRatio = option.series[hoveredIndex].pieData.startRatio
          const endRatio = option.series[hoveredIndex].pieData.endRatio

          // 对当前点击的扇形,执行取消高亮操作(对 option 更新)
          option.series[hoveredIndex].parametricEquation = getParametricEquation(
            startRatio,
            endRatio,
            isSelected,
            isHovered,
            k
          )
          option.series[hoveredIndex].pieStatus.hovered = isHovered

          // 将此前记录的上次选中的扇形对应的系列号 seriesIndex 清空
          hoveredIndex = ''
        }

        // 使用更新后的 option,渲染图表
        self.echarts.setOption(option)
      })
    }
  }
}
</script>
<style lang="scss" scoped>
#chart-panel {
  width: 100%;
  height: 210px;
}
</style>
 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ECharts是一个基于JavaScript的开源可视化库,用于构建交互式的图表和数据可视化。它支持多种图表类型,包括饼图、柱状图、折线图等。而ECharts-GL是在ECharts的基础上扩展而来,专门用于创建基于WebGL的3D图表。 要实现3D饼图,可以使用ECharts-GL库。首先,你需要引入ECharts-GL库的脚本文件。然后,创建一个容器元素,用于显示图表。接下来,通过配置项来定义饼图的数据和样式。最后,使用ECharts-GL提供的API将配置项应用到容器元素中,即可生成3D饼图。 以下是一个简单的示例代码: ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>3D饼图示例</title> <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script> </head> <body> <div id="chart" style="width: 600px; height: 400px;"></div> <script> // 初始化echarts实例 var myChart = echarts.init(document.getElementById('chart')); // 配置项 var option = { series: [{ type: 'pie3D', data: [ {value: 335, name: '直接访问'}, {value: 310, name: '邮件营销'}, {value: 234, name: '联盟广告'}, {value: 135, name: '视频广告'}, {value: 1548, name: '搜索引擎'} ] }] }; // 使用配置项生成图表 myChart.setOption(option); </script> </body> </html> ``` 以上代码中,我们引入了EChartsECharts-GL的脚本文件,并创建了一个id为"chart"的容器元素。通过配置项中的series属性,我们定义了一个饼图的数据和样式。最后,使用setOption方法将配置项应用到容器元素中,即可生成3D饼图

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值