当前位置:   article > 正文

echarts的3D饼图_echarts 3d饼图环

echarts 3d饼图环

1、需要安装

import * as echarts from 'echarts'
import 'echarts-gl'
  • 1
  • 2

2、使用方法


/**
 * 需要引入扩展资源 https://cdn.jsdelivr.net/npm/echarts-gl@2/dist/echarts-gl.min.js
 * 实际项目中需要安装【echarts-gl】插件
 * (vue)使用中引入 import "echarts-gl";
 */
/**
 * getParametricEquation 生成扇形的曲面参数方程 生成 3D 扇形环曲面
 * @param{} startRatio(浮点数): 当前扇形起始比例,取值区间 [0, endRatio)
 * @param{} endRatio(浮点数): 当前扇形结束比例,取值区间 (startRatio, 1]
 * @param{} isSelected(布尔值):是否选中,效果参照二维饼图选中效果(单选)
 * @param{} isHovered(布尔值): 是否放大,效果接近二维饼图高亮(放大)效果(未能实现阴影)
 * @param{} k(0~1之间的浮点数):用于参数方程的一个参数,取值 0~1 之间,通过「内径/外径」的值换算而来。
 * @param{} h :饼图的初始高度
 */
function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
   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 = typeof k !== "undefined" ? k : 1 / 3;
   const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
   const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
   // 鼠标滑过时外环放大大小
   const hoverRate = isHovered ? 1.05 : 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(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(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(u, v) {
         if (u < -Math.PI * 0.5) { return Math.sin(u); }
         if (u > Math.PI * 2.5) { return Math.sin(u) * h * 0.1; }
         // 当前图形的高度是Z根据h(每个value的值决定的)
         return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
      }
   };
}
// 饼图数据
var optionData = [
   { name: "日化", value: 60 },
   { name: "冻品", value: 44 },
   { name: "粮食", value: 24 },
   { name: "日化1", value: 60 },
   { name: "冻品1", value: 44 },
   { name: "粮食1", value: 24 },
   { name: "日化2", value: 60 },
   { name: "冻品2", value: 44 },
   { name: "粮食2", value: 24 }
];
// 可做为调整内环大小 0为实心圆饼图,大于0 小于1 为圆环
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;
   for (let i = 0; i < pieData.length; i += 1) {
      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 }
      };
      if (typeof pieData[i].itemStyle !== "undefined") {
         const { itemStyle } = pieData[i];
         // eslint-disable-next-line no-unused-expressions
         typeof pieData[i].itemStyle.color !== "undefined" ? (itemStyle.color = pieData[i].itemStyle.color) : null;
         // eslint-disable-next-line no-unused-expressions
         typeof pieData[i].itemStyle.opacity !== "undefined" ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null;
         seriesItem.itemStyle = itemStyle;
      }
      series.push(seriesItem);
   }
   for (let i = 0; i < series.length; i += 1) {
      endValue = startValue + series[i].pieData.value;
      series[i].pieData.startRatio = startValue / sumValue;
      series[i].pieData.endRatio = endValue / sumValue;
      series[i].parametricEquation = getParametricEquation(
         series[i].pieData.startRatio,
         series[i].pieData.endRatio,
         false,
         false,
         k,
         10 // 在此处传入饼图初始高度h
      );
      startValue = endValue;
      legendData.push(series[i].name);
   }
   // 准备待返回的配置项,把准备好的series 传入。
   const option = {
      backgroundColor: "#123756",
      title: {
         text: "立体旋转饼图/环图",
         top: "1%",
         textAlign: "left",
         left: "1%",
         textStyle: {
            color: "#38adb9",
            fontSize: 32,
            fontWeight: "600"
         }
      },
      legend: {
         show: true,
         type: "scroll",
         right: 10,
         top: 10,
         orient: "vertical", // 纵向
         icon: "circle", // icon 类型
         itemHeight: 12, // icon高度
         itemWidth: 12, // icon 宽度
         itemGap: 5, // 图例间隔
         textStyle: {
            color: "#709DD9",
            fontSize: 12,
            fontWeight: "400"
         },
         formatter: name => {
            if (pieData.length) {
               const item = pieData.filter(item => item.name === name)[0];
               return `  ${name}:${item.value}`;
            }
         }
      },
      color: ["#159AFF", "#66E1DF", "#66E193", "#E3F170", "#FFAD6A", "#ffe0ab", "#6bc5f4", "#c08ef2", "#ffda49"],
      tooltip: {
         formatter: params => {
            if (params.seriesName !== "mouseoutSeries") {
               return `${params.marker}${params.seriesName}:${pieData[params.seriesIndex].value}`;
            }
            return "";
         }
      },
      xAxis3D: { min: -1, max: 1 },
      yAxis3D: { min: -1, max: 1 },
      zAxis3D: { min: -1, max: 1 },
      grid3D: {
         show: false,
         boxHeight: 15, // 修改立体饼图的高度
         top: "-10%",
         left: "0%",
         viewControl: {
            // 3d效果可以放大、旋转等,
            alpha: 20, // 饼图翻转的程度
            beta: 30,
            rotateSensitivity: 1,
            zoomSensitivity: 0,
            panSensitivity: 0,
            autoRotate: true, // 是否自动旋转
            distance: 300 // 距离越小看到的饼图越大
         }
      },
      series
   };
   return option;
}

option = getPie3D(optionData, 0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179

3、案例

<template>
  <!-- 病种分类 -->
  <div class="age_distribution">
    <div class="title">{{ title }}</div>
    <div id="age"></div>
    <div class="bg"></div>
  </div>
</template>

<script>
import * as echarts from 'echarts'
import 'echarts-gl'
export default {
  props: ['title'],
  data() {
    return {}
  },
  mounted() {
    this.getPeople()
  },
  methods: {
    // 折线图
    getPeople() {
      var myChart = echarts.init(document.getElementById('age'))
      function getParametricEquation(startRatio, endRatio, isSelected, isHovered, k, h) {
        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 = typeof k !== 'undefined' ? k : 1 / 3
        const offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0
        const offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0
        // 鼠标滑过时外环放大大小
        const hoverRate = isHovered ? 1.05 : 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(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(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(u, v) {
            if (u < -Math.PI * 0.5) {
              return Math.sin(u)
            }
            if (u > Math.PI * 2.5) {
              return Math.sin(u) * h * 0.1
            }
            // 当前图形的高度是Z根据h(每个value的值决定的)
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1
          }
        }
      }
      // 饼图数据
      var optionData = [
        { name: '18岁以下', value: 7140 },
        { name: '19-30岁', value: 8991 },
        { name: '31-40岁', value: 37455 },
        { name: '41-50岁', value: 25490 },
        { name: '51-60岁', value: 7161 }
      ]
      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
        for (let i = 0; i < pieData.length; i += 1) {
          sumValue += pieData[i].value
          const seriesItem = {
            name: typeof pieData[i].name === 'undefined' ? `series${i}` : pieData[i].name,
            radius: ['40%', '60%'],
            type: 'surface',
            parametric: true,
            wireframe: { show: false },
            pieData: pieData[i],
            pieStatus: { selected: false, hovered: false, k }
          }
          if (typeof pieData[i].itemStyle !== 'undefined') {
            const { itemStyle } = pieData[i]
            // eslint-disable-next-line no-unused-expressions
            typeof pieData[i].itemStyle.color !== 'undefined' ? (itemStyle.color = pieData[i].itemStyle.color) : null
            // eslint-disable-next-line no-unused-expressions
            typeof pieData[i].itemStyle.opacity !== 'undefined' ? (itemStyle.opacity = pieData[i].itemStyle.opacity) : null
            seriesItem.itemStyle = itemStyle
          }
          // series = getPie3D(optionData, 0.3)
          series.push(seriesItem)
        }
        for (let i = 0; i < series.length; i += 1) {
          endValue = startValue + series[i].pieData.value
          series[i].pieData.startRatio = startValue / sumValue
          series[i].pieData.endRatio = endValue / sumValue
          series[i].parametricEquation = getParametricEquation(
            series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,
            false,
            k,
            10 // 在此处传入饼图初始高度h
          )
          startValue = endValue
          legendData.push(series[i].name)
        }
        // 准备待返回的配置项,把准备好的series 传入。
        const option = {
          //   backgroundColor: '#123756',
          title: {
            show: false
            // text: '',
            // top: '1%',
            // textAlign: 'left',
            // left: '1%',
            // textStyle: {
            //   color: '#38adb9',
            //   fontSize: 32,
            //   fontWeight: '600'
            // }
          },
          //   右边提示文本
          legend: {
            show: true,
            type: 'scroll',
            right: 50,
            top: 60,
            orient: 'vertical', // 纵向
            icon: 'rect', // icon 类型 标记类型包括 'circle'圆, 'rect'方, 'roundRect'圆角, 'triangle'三角形, 'diamond'lin, 'arrow'箭头, 'pin', 'arrow', 'none'
            itemHeight: 12, // icon高度
            itemWidth: 12, // icon 宽度
            itemGap: 10, // 图例间隔 --每个title之间的间隔
            data: legendData,
            textStyle: {
              color: '#709DD9',
              fontSize: 12,
              fontWeight: '400'
            },
            formatter: name => {
              if (pieData.length) {
                const item = pieData.filter(item => item.name === name)[0]
                // console.log(item, name)
                var arr = [`{a|${name}}`, `{b|${item.value}}`]
                return arr
                // return `${name}:${item.value}`
              }
            },
            textStyle: {
              rich: {
                //改样式 和下面formatter一起
                a: {
                  color: '#fff',
                  fontSize: 12 // 字体大小被覆盖了,这里重新定义
                },
                b: {
                  color: '#59e6ed',
                  fontSize: 12
                }
              }
            }
          },
          //   每一块区域的颜色
          color: ['#9f76f2', '#2a9dff', '#fac924', '#5ce5ff', '#6573f3'],
          tooltip: {
            formatter: params => {
              if (params.seriesName !== 'mouseoutSeries') {
                return `${params.marker}${params.seriesName}:${pieData[params.seriesIndex].value}`
              }
              return ''
            }
          },
          xAxis3D: { min: -1, max: 1 },
          yAxis3D: { min: -1, max: 1 },
          zAxis3D: { min: -1, max: 1 },
          grid3D: {
            show: false,
            boxHeight: 25, // 修改立体饼图的高度
            top: '5%',
            left: '-15%',
            viewControl: {
              // 3d效果可以放大、旋转等,
              alpha: 40, // 饼图翻转的程度
              beta: 30,
              // projection: 'perspective',//默认为透视投影'perspective',也支持设置为正交投影'orthographic'
              rotateSensitivity: 1,
              zoomSensitivity: 0,
              panSensitivity: 0,
              autoRotateSpeed: 50, //物体自传的速度
              autoRotate: true, // 是否自动旋转
              distance: 350 // 距离越小看到的饼图越大
            }
          },
          series
        }
        return option
      }
      // 可做为调整内环大小 0为实心圆饼图,大于0 小于1 为圆环
      let option = getPie3D(optionData, 0)
      myChart.setOption(option)
      // 让图表自适应容器大小
      window.addEventListener('resize', function () {
        myChart.resize()
      })
    }
  }
}
</script>

<style lang="less" scoped>
.age_distribution {
  position: relative;
  width: 100%;
  height: 100%;
  background-image: url('../../assets/box_card.png');
  background-repeat: no-repeat;
  background-size: 100% 100%;
  .title {
    position: absolute;
    top: -7px;
    left: 50%;
    transform: translateX(-50%);
    font-size: 17px;
    font-family: PingFang SC, PingFang SC;
    color: #ffffff;
    letter-spacing: 2px;
  }

  #age {
    z-index: 3;
    width: 100%;
    height: 100%;
  }
  .bg {
    z-index: 2;
    position: absolute;
    background-image: url('../../assets/bottom_img_age.png');
    top: 9%;
    left: 10%;
    background-size: 100% 100%;
    width: 50%;
    height: 84%;
  }
}
</style>

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266

效果图:
在这里插入图片描述

需要底座背景图自取
在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/105374
推荐阅读