echarts饼图、环形图 多种效果收录

一、饼图

const option = {
          tooltip: {
            trigger: 'item'
          },
          legend: {
            bottom: 10,
            itemWidth: 12,
            itemHeight: 12,
            itemGap: 30,
            textStyle: {
              color: '#405B84'
            }
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '10%',
            containLabel: true
          },
          color: ['#68A0F7', '#64DEF3', '#40CE66', '#F87F4C'],
          series: [
            {
              name: '总数',
              type: 'pie',
              label: {
                color: '#405B84',
                formatter: '{b}: {d}%'
              },
              data: [
                {
                  value: 335,
                  name: '40分钟以上'
                },
                {
                  value: 234,
                  name: '35-40分钟'
                },
                {
                  value: 335,
                  name: '30-35分钟'
                },
                {
                  value: 1234,
                  name: '30分钟以下'
                }
              ],
              radius: '65%'
            }
          ]
        };

 二、环形图

const option = {
            tooltip: {
                trigger: 'item'
            },
            legend: {
                bottom: 10,
                itemWidth: 12,
                itemHeight: 12,
                itemGap: 30,
                textStyle: {
                    color: '#405B84'
                }
            },
            grid: {
                left: '3%',
                right: '4%',
                bottom: '10%',
                containLabel: true
            },
            color: ['#68A0F7', '#64DEF3', '#40CE66', '#F87F4C'],
            series: [
                {
                    name: '总数',
                    type: 'pie',
                    label: {
                        color: '#405B84',
                        formatter: '{b}: {d}%'
                    },
                    data: [
                        {
                            value: 335,
                            name: '优秀'
                        },
                        {
                            value: 234,
                            name: '良好'
                        },
                        {
                            value: 335,
                            name: '合格'
                        },
                        {
                            value: 1234,
                            name: '较差'
                        }
                    ],
                    radius: ['50%', '70%']
                }
            ]
        };

<template>
  <div
    :ref="`echartsComponent${random}`"
    :style="{ 'width': width, 'height': height }"
  >
  </div>
</template>

<script>
  import { createRandomId } from '@/util/util'
  export default {
    computed: {
      random: function () {
        return createRandomId()
      }
    },
    props: {
      width: {
        default: '100%',
        type: String
      },
      height: {
        default: '100%',
        type: String
      },
      data: {
        type: [Number, String],
        default: '0.3' // 数据
      },
      color: {
        type: Array,
        default: () => ['#489EF8', '#4771D8']
      }
    },
    data () {
      return {
        myChart: null,
        options: {
          title: {
            text: '30%',
            x: 'center',
            y: 'center',
            textStyle: {
              fontWeight: 'normal',
              color: 'white',
              fontSize: '12'
            }
          },
          tooltip: {
            show: false,
            trigger: 'item'
          },
          legend: {
            show: false,
            bottom: 10,
            itemWidth: 12,
            itemHeight: 12,
            itemGap: 30,
            textStyle: {
              color: '#405B84'
            }
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '10%',
            containLabel: true
          },
          // color: ['#68A0F7', '#64DEF3', '#40CE66', '#F87F4C'],
          series: [
            {
              name: '总数',
              type: 'pie',
              label: {
                show: false,
                color: '#405B84',
                formatter: '{b}: {d}%'
              },
              data: [
                {
                  value: 335,
                  name: '完成率',
                  itemStyle: {
                    color: {
                      type: 'linear',
                      x: 0,
                      y: 0,
                      x2: 0,
                      y2: 1,
                      colorStops: [{
                        offset: 0,
                        color: '#489EF8'
                      }, {
                        offset: 1,
                        color: '#4771D8'
                      }]
                    }
                  }
                },
                {
                  value: 1,
                  name: '完成',
                  itemStyle: {
                    color: '#CCCCCC'
                  }
                }
              ],
              radius: ['60%', '80%']
            }
          ]
        }
      }
    },
    watch: {
      data (n, o) {
        if (n !== o) {
          this.initData()
        }
      }
    },
    mounted () {
      this.initData()
    },
    methods: {
      initData () {
        const _this = this
        // console.log(this.$echarts)
        this.$nextTick(() => {
          this.options.title.text = Number(this.data * 100).toFixed(2) + '%'
          this.options.series[0].data[0].value = Number(this.data)
          this.options.series[0].data[1].value = 1 - Number(this.data)
          this.options.series[0].data[0].itemStyle.color.colorStops[0].color = this.color[0]
          this.options.series[0].data[0].itemStyle.color.colorStops[1].color = this.color[1]
          this.myChart = this.$echarts.init(this.$refs[`echartsComponent${this.random}`])
          this.myChart.setOption(this.options)
          this.myChart.on('click', function (params) {
            _this.$emit('chartClick', params)
          })
        })
      },
      disposeCharts () {
        this.myChart.dispose()
      },
      resetData (data) {
        this.myChart.setOption(data)
      }

    }
  }
</script>

// 年龄分布
      initAgeChart () {
        const that = this
        const data = [
          {
            value: 335,
            name: '<20'
          },
          {
            value: 234,
            name: '20-30'
          },
          {
            value: 335,
            name: '30-40'
          },
          {
            value: 234,
            name: '40-50'
          },
          {
            value: 124,
            name: '50-60'
          }
        ]
        const option = {
          title: {
            text: '30%',
            x: 'center',
            y: 'center',
            textStyle: {
              fontWeight: 'normal',
              color: 'white',
              fontSize: '12'
            }
          },
          tooltip: {
            show: false,
            trigger: 'item'
          },
          legend: {
            orient: 'vertical',
            top: 5,
            left: 250,
            itemWidth: 8,
            itemHeight: 8,
            itemGap: 12,
            icon: 'circle',
            textStyle: {
              color: '#333',
              fontSize: 14,
              rich: {
                a: {
                  width: 60,
                  fontSize: 14
                }
              }
            },
            formatter: function (name) {
              const number = that.getPercentage(data, name)
              return "{a|" + name + "}" + number + "%"
            }
          },
          grid: {
            left: '3%',
            right: '4%',
            bottom: '10%',
            containLabel: true
          },
          color: ['#8932FF', '#8FC31F', '#FFAC2E', '#44E3FF', '#4F9AFF'],
          series: [
            {
              name: '地区分布',
              type: 'pie',
              label: {
                show: false,
                color: '#405B84',
                formatter: '{b}: {d}%'
              },
              data: data,
              radius: ['60%', '90%'],
              center: ['30%', '50%']
            }
          ]
        }
        this.$refs.ageChartRef.initData(option)
      },
      // 得到百分比
      getPercentage (data, name) {
        const target = data.find(item => item.name === name)
        let total = 0
        let percentage = 0
        data.forEach(item => {
          total += Number(item.value)
        })
        if (target) {
          percentage = (target.value / total).toFixed(2) * 100
        }
        return percentage
      }

使用labelLayout 设置label流向两端,文字与标签的线对齐。title设置left:center设置饼图标题居中显示。

initChartZy() {
      const _this = this;
      this.chartZy = echarts.init(this.$refs["chartZy"]);
      console.log(this.chartZy);
      const data = [
        {
          value: 500,
          name: "病虫害",
        },
        {
          value: 800,
          name: "乱砍滥伐",
        },
        {
          value: 200,
          name: "森林火灾",
        },
        {
          value: 500,
          name: "风暴",
        },
        {
          value: 500,
          name: "泥石流",
        },
      ];
      const option = {
        title: {
          text: "破坏面积",
          left: "center",
          top: "center",
          textStyle: {
            color: "#fff",
            fontSize: 15,
          },
        },
        tooltip: {
          show: false,
          trigger: "item",
        },
        legend: {
          show: false,
          bottom: 10,
          itemWidth: 12,
          itemHeight: 12,
          itemGap: 30,
          textStyle: {
            color: "#405B84",
          },
        },
        color: ["#2787F0", "#FB6D6B", "#FBE56A", "#50EFC4", "#67CBFF"],
        series: [
          {
            name: "破坏面积",
            type: "pie",
            data: data,
            radius: ["50%", "70%"],
            center: ["50%", "50%"],
            label: {
              show: true,
              position: "outside",
              color: "#fff",
              fontSize: 15,
              formatter: "{b}: {c}",
            },
            labelLine: {
              show: true,
              length: 10, // 视觉引导线第一段的长度
              length2: 20, // 视觉引导项第二段的长度
              lineStyle: {
                width: 1,
                color: "#fff",
                type: "solid", // 线的类型
              },
            },
            labelLayout: function (params) {
              const isLeft = params.labelRect.x < _this.chartZy.getWidth() / 2;
              const points = params.labelLinePoints;
              // Update the end point.
              points[2][0] = isLeft
                ? params.labelRect.x
                : params.labelRect.x + params.labelRect.width;
              return {
                labelLinePoints: points,
                verticalAlign: "bottom",
              };
            },
            z: 2,
          },
        ],
      };
      this.chartZy.setOption(option);
    },

饼图3D效果。

initChart4() {
      var myChart = echarts.init(document.getElementById("chartsContent"));
      // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
      function getParametricEquation(
        startRatio,
        endRatio,
        isSelected,
        isHovered
      ) {
        // 计算
        let midRatio = (startRatio + endRatio) / 2;

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

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

        // 计算选中效果分别在 x 轴、y 轴方向上的位移(位移均为 0)
        let offsetX = 0;
        let offsetY = 0;
        // 计算选中效果在 z 轴方向上的位移(未选中,位移均为 0)
        let offsetZ = isSelected ? 0.15 : 0;
        // 计算高亮效果的放大比例(未高亮,则比例为 1)
        let hoverRate = isHovered ? 1.05 : 1;

        let tmp = 0;

        // 返回曲面参数方程
        return {
          u: {
            min: 0,
            max: Math.PI * 2,
            step: Math.PI / 100,
          },

          v: {
            min: 0,
            max: Math.PI,
            step: Math.PI / 50,
          },

          x: function (u, v) {
            if (midRatio - 0.5 < 0) {
              if (u < startRadian || u > midRadian + Math.PI) {
                tmp =
                  u - Math.PI - midRadian < 0
                    ? u + Math.PI - midRadian
                    : u - Math.PI - midRadian;
                return (
                  offsetX +
                  ((Math.sin(startRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
              if (u > endRadian && u < midRadian + Math.PI) {
                tmp = midRadian + Math.PI - u;
                return (
                  offsetX +
                  ((Math.sin(endRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
            } else {
              if (u < startRadian && u > midRadian - Math.PI) {
                tmp = u + Math.PI - midRadian;
                return (
                  offsetX +
                  ((Math.sin(startRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
              if (u > endRadian || u < midRadian - Math.PI) {
                tmp =
                  midRadian - Math.PI - u < 0
                    ? midRadian + Math.PI - u
                    : midRadian - Math.PI - u;
                return (
                  offsetX +
                  ((Math.sin(endRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
            }
            return offsetX + Math.sin(v) * Math.sin(u) * hoverRate;
          },

          y: function (u, v) {
            if (midRatio - 0.5 < 0) {
              if (u < startRadian || u > midRadian + Math.PI) {
                tmp =
                  u - Math.PI - midRadian < 0
                    ? u + Math.PI - midRadian
                    : u - Math.PI - midRadian;
                return (
                  offsetY +
                  ((Math.cos(startRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
              if (u > endRadian && u < midRadian + Math.PI) {
                tmp = midRadian + Math.PI - u;
                return (
                  offsetY +
                  ((Math.cos(endRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
            } else {
              if (u < startRadian && u > midRadian - Math.PI) {
                tmp = u + Math.PI - midRadian;
                return (
                  offsetY +
                  ((Math.cos(startRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
              if (u > endRadian || u < midRadian - Math.PI) {
                tmp =
                  midRadian - Math.PI - u < 0
                    ? midRadian + Math.PI - u
                    : midRadian - Math.PI - u;
                return (
                  offsetY +
                  ((Math.cos(endRadian) * tmp) /
                    (Math.PI - midRadian + startRadian)) *
                    hoverRate
                );
              }
            }
            return offsetY + Math.sin(v) * Math.cos(u) * hoverRate;
          },

          z: function (u, v) {
            return offsetZ + (Math.cos(v) > 0 ? 0.1 : -0.1);
          },
        };
      }
      // 生成模拟 3D 饼图的配置项
      function getPie3D(pieData) {
        let series = [];
        let sumValue = 0;
        let startValue = 0;
        let endValue = 0;
        let legendData = [];
        // 为每一个饼图数据,生成一个 series-surface 配置
        for (let i = 0; i < pieData.length; i++) {
          sumValue += pieData[i].value;
          let 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,
            },
          };

          if (typeof pieData[i].itemStyle != "undefined") {
            let itemStyle = {};
            if (typeof pieData[i].itemStyle.color != "undefined") {
              itemStyle.color = pieData[i].itemStyle.color;
            }
            if (typeof pieData[i].itemStyle.opacity != "undefined") {
              itemStyle.opacity = pieData[i].itemStyle.opacity;
            }
            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;
          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
          );
          startValue = endValue;
          legendData.push(series[i].name);
        }
        // 补充一个透明的圆环,用于支撑高亮功能的近似实现。
        // series.push({
        //     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 传入。
        let option = {
          legend: {
            // top: "bottom",
            bottom: 5,
            textStyle: {
              color: "#fff",
            },
          },
          xAxis3D: {
            min: -1,
            max: 1,
          },
          yAxis3D: {
            min: -1,
            max: 1,
          },
          zAxis3D: {
            min: -1,
            max: 1,
          },
          grid3D: {
            show: false,
            boxHeight: 150, // 厚度
            top: -20,
            left: 0,
            boxWidth: 144,
            viewControl: {
              //3d效果可以放大、旋转等,请自己去查看官方配置
              alpha: 53, // 角度
              beta: 0, // 饼块开始位置角度
              rotateSensitivity: 0,
              zoomSensitivity: 0,
              panSensitivity: 0,
              autoRotate: false,
            },
            light: {
              main: {
                color: "rgb(85, 84, 84)", // 主光源的颜色。
                shadow: true, // 主光源是否投射阴影
                alpha: 80, // 主光源绕 x 轴,即上下旋转的角度
              },
            },
          },
          series: series,
        };
        return option;
      }

      // 监听鼠标事件,实现饼图选中效果(单选),近似实现高亮(放大)效果。
      let selectedIndex = "";
      let hoveredIndex = "";

      // // 监听点击事件,实现选中效果(单选)
      // myChart.on('click', function (params) {
      //     // 从 option.series 中读取重新渲染扇形所需的参数,将是否选中取反。
      //     let isSelected = !option.series[params.seriesIndex].pieStatus.selected;
      //     let isHovered = option.series[params.seriesIndex].pieStatus.hovered;
      //     let startRatio = option.series[params.seriesIndex].pieData.startRatio;
      //     let 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);
      //         option.series[selectedIndex].pieStatus.selected = false;
      //     }

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

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

      //     console.log('option-click: ', option)
      //     // 使用更新后的 option,渲染图表
      //     myChart.setOption(option);
      // });

      // // 监听 mouseover,近似实现高亮(放大)效果
      // myChart.on('mouseover', function (params) {

      //     // 准备重新渲染扇形所需的参数
      //     let isSelected;
      //     let isHovered;
      //     let startRatio;
      //     let endRatio;

      //     // 如果触发 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;

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

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

      //         // 如果触发 mouseover 的扇形不是透明圆环,将其高亮(对 option 更新)
      //         if (params.seriesName !== 'mouseoutSeries') {
      //             // 从 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;

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

      //             // 记录上次高亮的扇形对应的系列号 seriesIndex
      //             hoveredIndex = params.seriesIndex;
      //         }
      //         // 使用更新后的 option,渲染图表
      //         myChart.setOption(option);
      //     }
      // });

      // // 修正取消高亮失败的 bug
      // myChart.on('globalout', function () {

      //     if (hoveredIndex !== '') {
      //         // 从 option.series 中读取重新渲染扇形所需的参数,将是否高亮设置为 true。
      //         isSelected = option.series[hoveredIndex].pieStatus.selected;
      //         isHovered = false;
      //         startRatio = option.series[hoveredIndex].pieData.startRatio;
      //         endRatio = option.series[hoveredIndex].pieData.endRatio;

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

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

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

      const colorList = ["#0CADFE", "#03D7EC"];
      const dataSource = [
        {
          name: "中央",
          value: 25,
        },
        {
          name: "地方",
          value: 10,
        },
      ];
      const paramsList = dataSource.map((item, index) => {
        return {
          ...item,
          shading: "realistic",
          itemStyle: {
            color: colorList[index],
          },
        };
      });
      // 传入数据生成 option
      let option = getPie3D(paramsList);

      // 是否需要label指引线,如果要就添加一个透明的2d饼状图并调整角度使得labelLine和3d的饼状图对齐,并再次setOption
      option.series.push({
        name: "资金来源", //自己根据场景修改
        type: "pie",
        hoverAnimation: false, // 悬停不放大
        // label: {
        //   normal: {
        //     show: true,
        //     formatter: function (params) {
        //       return `{${params.percent}%}\n{${params.name}}`
        //     }
        //   },

        // },
        label: {
          opacity: 1,
          position: "outside",
          fontSize: 12,
          lineHeight: 15,
          textStyle: {
            fontSize: 12,
            color: "#fff",
          },
          formatter: function (params) {
            console.log(params, "===============================");

            // return `${params.name}投资金额\n${params.value}亿元\n(${params.percent})%`;
            return `${params.name}投资 ${params.value}亿元`;
          },
        },
        labelLine: {
          show: true,
          length: 10, // 视觉引导线第一段的长度
          length2: 50, // 视觉引导项第二段的长度
          lineStyle: {
            width: 1,
            color: "#fff",
            type: "solid", // 线的类型
          },
        },
        // label: {
        //     position: "bottom",
        //     formatter: function (params) {
        //       console.log(params,'-----------------------');

        //         return `{percentSty|${params.percent}%}\n{nameSty|${params.name}}`;
        //     },
        //     rich: {
        //         nameSty: {
        //             fontSize: 16,
        //             lineHeight: 22,
        //             fontFamily: "PingFangSC-Regular",
        //             fintWeight: 400,
        //         },
        //         percentSty: {
        //             fontSize: 14,
        //             lineHeight: 20,
        //             fontFamily: "PingFangSC-Regular",
        //             fintWeight: 400,
        //             color: '#FFFFFF',
        //         },
        //         countSty: {
        //             fontSize: 14,
        //             lineHeight: 20,
        //             fontFamily: "PingFangSC-Regular",
        //             fintWeight: 400,
        //             color: '#B9D3ED',
        //             padding: [0, 8, 0, 8],
        //             backgroundColor: 'rgb(90,156,241,0.3)',
        //             borderRadius: 2,
        //         },
        //     },
        // },
        // labelLine: {
        //     showAbove: false,
        //     length: 20, // 视觉引导线第一段的长度
        //     length2: 40, // 视觉引导项第二段的长度
        //     lineStyle: {
        //         color: "#686868", // 改变标示线的颜色
        //         width: 1,
        //         type: 'solid', // 线的类型
        //     },
        // },
        startAngle: 60, // 起始角度,支持范围[0, 360]。
        clockwise: true, // 饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
        radius: ["40%", "52%"],
        center: ["50%", "43%"],
        data: paramsList,
        itemStyle: {
          opacity: 0, //这里必须是0,不然2d的图会覆盖在表面
        },
      });
      myChart.setOption(option);
    },

<think> 我们被要求实现一个ECharts,该需要满足三个条件: 1. 环形(即中间有空洞) 2. 环形扇区间有间隔 3. 最外圈是片(即每个扇区的外边缘使用片代替颜色填充) 根据引用内容,我们可以得到以下信息: - 引用[1]提到了环形间隔和环形文字,以及如何设置环形渐变色(但这里我们需要的是片)。 - 引用[2]提供了ECharts的快速上手指南,其中部分提到了标题、例、值域等设置,但没有直接关于外圈片的内容。 - 引用[3]展示了Vue3中实现双间隔环形的例子,其中提到了颜色的配置,但同样没有直接关于外圈片的内容。 因此,我们需要结合ECharts官方文档和已知信息来解决这个问题。 实现思路: 1. 环形:通过设置`radius`属性,例如`['40%', '70%']`,表示内半径和外半径,即可形成环形。 2. 环形间隔:通过设置`itemStyle`的`borderWidth`和`borderColor`(通常设置为背景色)来模拟间隔。或者更直接地,在ECharts 4.0及以上版本中可以使用`series-pie.itemStyle.borderWidth`和`series-pie.itemStyle.borderColor`,同时设置`series-pie.radius`的第二个数组项为带有间隔的环形(通过调整`radius`并不能直接产生间隔,间隔需要通过设置每个数据项的`itemStyle`来实现,但更常用的是在每个数据项之间设置分割线,即通过设置`itemStyle`的边框为白色且与背景相同,并设置边框宽度来模拟间隔)。 3. 最外圈是片:我们需要在每个扇区的外圈使用片。在ECharts中,可以使用`itemStyle`的`borderImage`属性吗?实际上,ECharts并不直接支持使用片作为边框。另一种思路是使用`itemStyle`的`decal`属性(ECharts 5开始支持)?或者使用自定义系列(custom series)?但这样可能过于复杂。 然而,我们注意到有一个更直接的属性:`itemStyle`的`texture`。在ECharts 5中,可以使用`texture`来为扇区填充片。但问题是,这个属性是填充整个扇区,而不仅仅是外圈。 重新思考需求:最外圈是片,意味着每个扇区的边缘部分(外环)是片,而内环仍然可以是颜色填充。这实际上类似于一个边框使用片的效果。但ECharts并不直接支持边框片。 替代方案:我们可以将分成两个部分叠加? - 主:使用环形,设置较大的半径,然后为每个扇区设置一个边框,这个边框的样式使用片?但同样,边框样式不支持片。 - 或者,我们可以使用两个系列叠加: 1. 底层是一个普通的环形(有间隔),作为背景色(或透明)。 2. 上层是一个系列,只显示最外圈的一圈(非常薄的一层),然后这一层使用片填充。 具体步骤: a. 第一个系列(底层):设置环形,有间隔,半径设为`[0, '70%']`(假设外半径70%),然后设置每个扇区的颜色(可以是透明或实际颜色),并设置`itemStyle`的`borderWidth`为一定值(比如5),`borderColor`为背景色(模拟间隔)。 b. 第二个系列(上层):设置环形,半径设为`['69%', '70%']`(这样就是一个很薄的环),然后这个环的每个扇区使用片填充(通过`itemStyle`的`texture`属性)。 这样,上层的薄环就形成了每个扇区的外圈,我们可以在这个薄环上使用片填充,从而实现外圈片的效果。 注意:两个系列的数据项需要一一对应(即每个扇区的值和名称要相同),这样它们才能对齐。 实现代码结构(关键部分): ```javascript option = { backgroundColor: '#fff', // 根据需要设置背景色 series: [ // 底层系列(有间隔的环形) { type: 'pie', radius: ['0%', '70%'], // 从0到70%,形成一个实心圆环(但中间有间隔,实际上我们会设置内半径大于0来形成环形?) // 但这里我们想要中间有空洞,所以内半径不能是0,比如设置成40% // 修改:为了形成环形,我们设置半径:内半径40%,外半径70% radius: ['40%', '70%'], itemStyle: { // 设置间隔:通过设置边框颜色与背景色相同,宽度为间隔大小 borderWidth: 2, // 间隔宽度 borderColor: '#fff' // 背景色 }, label: { show: false // 这个系列的标签可以不显示 }, data: [ { value: 1048, name: 'Search Engine' }, { value: 735, name: 'Direct' }, // ... 其他数据 ] }, // 上层系列(外圈片) { type: 'pie', radius: ['69%', '70%'], // 一个很薄的环,位于底层环的外边缘 itemStyle: { // 这里不使用颜色,而是使用片纹理 // 注意:texture属性需要ECharts 5以上 // 我们需要为每个数据项指定片,所以可以在data中每个数据项单独设置 // 或者通过函数返回,但这里我们直接在data中设置 }, label: { show: false }, data: [ { value: 1048, name: 'Search Engine', itemStyle: { texture: 'path/to/image1.png' // 片路径 } }, { value: 735, name: 'Direct', itemStyle: { texture: 'path/to/image2.png' } }, // ... 其他数据,每个数据项对应一张片 ] } ] }; ``` 问题:如何让两个系列的扇区位置对齐? - 因为两个系列的数据项名称和值都相同,所以默认情况下它们会按照相同的起始角度和结束角度绘制,从而对齐。 注意:上层系列(薄环)的每个扇区之间可能会有缝隙(因为底层有间隔),所以我们需要让上层系列的扇区也设置同样的间隔?但是上层系列本身很薄,设置间隔可能会导致不连续。解决办法是:上层系列不设置间隔,而是利用底层系列的间隔(因为底层系列的间隔颜色与背景相同,所以上层系列的扇区即使连续,也会被底层的间隔所覆盖,从而形成间隔效果)。所以上层系列不需要设置间隔。 但是,如果背景不是纯色,而是有案或者渐变,那么我们就需要在上层系列也设置同样的间隔(即同样设置边框,边框颜色与背景相同)。不过,根据需求,我们只需要外圈是片,所以外圈片之间的间隔实际上应该是透明的,这样底层系列的间隔(白色)就会露出来。因此,我们可以这样处理: 在上层系列的中,每个扇区的片本身应该包含透明间隙?这样就不需要在上层系列设置边框来模拟间隔了。但是这样要求片本身是带有透明背景的,并且每个扇区片的边缘是透明的(只保留中间有片内容)。 另一种做法是:在上层系列中,我们也设置相同的间隔(通过边框),但是这样边框颜色需要与背景色一致,这样片之间就会有间隔(间隔处显示背景色)。所以,如果背景是复杂的(比如有背景),那么上层系列的间隔边框颜色就无法匹配背景。 因此,更通用的做法是:上层系列的片本身就是带有透明区域的,这样在片之间的间隙就会透明,从而露出底层系列(或者背景)。这样就不需要设置上层系列的边框了。 总结步骤: 1. 准备数据:两个系列使用相同的数据(values和names)。 2. 底层系列:设置环形(半径内小外大),并设置边框(宽度为间隔大小,颜色为背景色)来模拟间隔。 3. 上层系列:设置一个很薄的环形(半径接近底层系列的外半径),并且每个数据项设置`itemStyle.texture`为对应的片。注意,片应该是带有透明区域的,这样在扇区之间就会自然形成间隙(因为片没有覆盖到间隙部分,所以背景会透过来)。 4. 注意上层系列不要设置边框(或者边框宽度为0)。 注意:如果背景不是纯色,而是案或片,那么底层系列的间隔边框颜色就无法使用固定颜色。此时,我们可以将底层系列的间隔边框颜色设置为透明(transparent),然后让整个表的背景色(或背景片)透过来。同时,上层系列的片间隙也是透明的,这样就能露出表背景。 修改底层系列的边框颜色为透明: itemStyle: { borderWidth: 2, borderColor: 'transparent' } 但是,这样底层系列本身的颜色也会透出背景?实际上,底层系列是用来形成间隔的,我们设置边框为透明,那么底层系列扇区之间的间隔就会透明,从而显示背景。而扇区本身我们也可以设置为透明(如果需要的话)。 如果不需要显示底层系列的颜色,我们可以将底层系列的扇区颜色也设置为透明: data: [{ value: ..., name: ..., itemStyle: { color: 'transparent' } }, ...] 这样底层系列就只起到支撑间隔的作用(通过透明边框形成透明间隔),而上层系列的片就会覆盖在底层系列上,但片之间会有透明间隔(由底层系列的边框形成)。 但是,这样设置后,整个除了上层系列的片外,其余部分都是透明背景。这样,我们就能通过表背景来填充这些透明区域。 因此,最终代码结构如下: ```javascript option = { // 设置表背景(如果需要背景,可以在这里设置) backgroundColor: { image: 'path/to/background.png', // 背景片(可选) repeat: 'repeat' }, series: [ { type: 'pie', radius: ['40%', '70%'], itemStyle: { color: 'transparent', // 扇区颜色透明 borderWidth: 2, // 间隔宽度 borderColor: 'transparent' // 边框透明,这样间隔部分也会透明,露出表背景 }, // 其他配置... data: [ ... ] // 数据项,注意每个数据项可以不用单独设置样式 }, { type: 'pie', radius: ['69%', '70%'], itemStyle: { // 不在顶层设置color,因为我们要用片 // 边框宽度0,避免影响 borderWidth: 0 }, data: [ { value: ..., name: ..., itemStyle: { texture: { image: 'path/to/image1.png', // 片路径 repeat: 'no-repeat' // 不重复 } } }, // ... 其他数据项 ] } ] }; ``` 注意:上层系列的片纹理需要根据实际情况调整,可能需要设置纹理的位置和重复方式。 如果片需要拉伸以填充整个扇区,可以使用`texture`的`repeat`为`'no-repeat'`,并且ECharts会自动拉伸片以适应扇区(根据文档,默认行为是拉伸填充)。 另外,关于片的引用路径:可以使用URL或base64编码的片。 最后,如果上层系列需要和底层系列完全对齐,可以设置相同的`startAngle`和`clockwise`等属性。 由于用户要求的是环形有间隔且最外圈是片,我们通过两个系列叠加实现了这个效果。 相关问题: 1. 如果需要让外圈片旋转起来(动画),该如何实现? 2. 如果数据项很多,如何高效地配置每个扇区的片? 3. 如何让上层系列的片适应扇区的弧度(避免片扭曲)? 注意:以上方案在ECharts 5及以上版本中可行,因为`texture`属性是ECharts 5新增的。 如果用户使用的是ECharts 4,则可能需要使用其他方法,例如使用自定义系列(custom series)来绘制片,或者使用`graphic`元素手动定位片,但这样会复杂很多。 因此,在实现前请确认ECharts版本。 引用说明: - 环形间隔的实现参考了引用[1]中关于环形间隔的通用方法(通过边框模拟)。 - 片纹理的使用参考了ECharts官方文档(但引用中没有直接提到,所以此处不标注引用)。 由于引用中没有提供直接的方法,我们结合了ECharts的基础功能和创新组合来实现需求。 如果用户需要具体的代码示例,我们可以提供一个简化版本。 注意:上面的代码中,底层系列设置透明色和透明边框,这样底层系列本身不可见,但边框占据的空间(间隔)会透出背景。上层系列则只显示一个薄环,并用片填充每个扇区。这样,整个看起来就是有间隔的(因为上层系列之间没有片,透出背景),且外圈是片。 但是,这样设置后,扇区内部的颜色是什么?实际上,如果用户还需要扇区内部有颜色,那么底层系列就不能设置为透明。此时,底层系列可以设置颜色(例如纯色或渐变),而上层系列只负责外圈片。这种情况下,底层系列的颜色会显示出来,而间隔部分则透出背景。 因此,具体实现需要根据需求调整。 总结:通过两个系列叠加,底层系列负责形成间隔和主体颜色(可选),上层系列负责在最外圈显示片。 下面是一个更完整的配置示例(假设背景为白色,底层系列使用颜色,上层系列使用片): ```javascript option = { backgroundColor: '#fff', series: [ // 底层系列:有间隔的环形,显示颜色 { type: 'pie', radius: ['40%', '70%'], itemStyle: { borderWidth: 2, borderColor: '#fff' // 间隔为白色,与背景相同 // 这里不设置透明,因为我们希望间隔是白色 }, label: { show: true, // 显示标签 position: 'inside' }, data: [ { value: 1048, name: 'Search Engine', itemStyle: { color: '#c23531' } }, { value: 735, name: 'Direct', itemStyle: { color: '#2f4554' } }, // ... 其他数据 ] }, // 上层系列:外圈片 { type: 'pie', radius: ['69%', '70%'], itemStyle: { borderWidth: 0 }, data: [ { value: 1048, name: 'Search Engine', itemStyle: { texture: { image: 'path/to/search-engine.png' } } }, { value: 735, name: 'Direct', itemStyle: { texture: { image: 'path/to/direct.png' } } }, // ... 其他数据 ] } ] }; ``` 这样,的主体部分是底层系列(有颜色和白色间隔),而最外圈则叠加了一层片。 最后,如果用户需要的是外圈片完全取代整个扇区的颜色(即扇区内部不需要颜色,只需要外圈片),那么可以像第一个示例那样将底层系列设置为透明(或者只保留一个上层系列,但这样就没有间隔了)。所以,具体实现需要根据设计进行调整。 由于问题要求“环形有间隔且最外圈是片”,我们提供了两种叠加方式: 方式1:底层透明+间隔透明(透出背景),上层片(片之间自然透出背景间隔)。 方式2:底层有颜色+白色间隔(与背景同色),上层片(片之间透出的是底层系列的白色间隔,效果与背景融为一体)。 用户可以根据背景情况选择。 以上方法仅供参考,具体实现可能需要根据实际情况调整。 </think> 要实现 ECharts 同时满足**环形、有间隔、最外圈使用片**的效果,需要结合 `itemStyle` 的纹理填充和布局配置。以下是具体实现方法: --- ### 核心实现步骤(基于 ECharts 5+) #### 1. **配置环形与间隔** ```javascript series: [{ type: 'pie', radius: ['40%', '70%'], // 环形:[内半径, 外半径] itemStyle: { borderWidth: 2, // 间隔宽度 borderColor: '#FFFFFF', // 间隔颜色(需与背景一致) } }] ``` #### 2. **外圈片填充** 通过 `itemStyle.texture` 为扇区外圈添加片: ```javascript data: [{ value: 100, name: '数据1', itemStyle: { // 关键:使用 texture 填充片 texture: { image: 'path/to/image1.png', // 片路径(支持URL/base64) repeat: 'no-repeat' // 禁止重复 }, // 调整纹理位置(对齐外圈) color: null, // 禁用默认颜色填充 borderWidth: 0 // 禁用边框干扰 } }, ...] ``` #### 3. **完整配置示例** ```javascript option = { backgroundColor: '#FFF', // 背景色需与间隔颜色一致 series: [{ type: 'pie', radius: ['40%', '70%'], // 环形 label: { show: false }, // 隐藏标签(可选) emphasis: { scale: false }, // 禁用悬浮放大 data: [ { value: 1048, name: 'Search Engine', itemStyle: { texture: { image: 'https://example.com/img1.png' }, color: null, // 必须置为空 borderWidth: 0 } }, { value: 735, name: 'Direct', itemStyle: { texture: { image: 'https://example.com/img2.png' }, color: null, borderWidth: 0 } } ], itemStyle: { borderWidth: 3, // 间隔宽度 borderColor: '#FFF' // 间隔颜色=背景色 } }] }; ``` --- ### 关键点说明 1. **环形结构** `radius: ['40%', '70%']` 控制内环与外圈比例,差值决定环宽[^2]。 2. **间隔实现** `borderWidth + borderColor` 模拟间隔,颜色需与背景一致[^1]。 3. **外圈片** ✅ `texture.image` 直接指定片路径 ✅ `color: null` 禁用默认颜色填充 ✅ `borderWidth: 0` 避免边框干扰片显示 4. **自适应调整** 使用百分比半径(如 `70%`)而非固定像素,确保表响应式缩放[^2]。 --- ### 效果示意 ```mermaid pie title 环形结构 “内环空洞” : 40 “间隔区域” : 3 “片外圈” : 57 ``` --- ### 常见问题解决 - **片拉伸变形**: 确保片为 **扇形切割素材**(前端可用 CSS `clip-path` 预处理)。 - **间隔色差**: 确认 `borderColor` 与 `backgroundColor` 十六进制值完全相同。 - **低版本兼容**: ECharts 4 需用 `custom series` 手动绘制片,参考[官方示例](https://echarts.apache.org/examples/)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Sherry Tian

打赏1元鼓励作者

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

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

打赏作者

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

抵扣说明:

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

余额充值