首页 前端知识 用echarts实现3d饼图

用echarts实现3d饼图

2024-01-25 11:01:18 前端知识 前端哥 712 615 我要收藏

安装echarts和echarts-gl

npm install echarts

npm install echarts-gl

echarts版本5.x的话需要对应echarts-gl版本2.x

echarts版本4.x的话需要对应echarts-gl版本1.x

指定版本命令 npm install echarts-gl@1.1.2

1.关键函数,生成扇形的曲面参数方程,用于 series-surface

Documentation - Apache ECharts官网series-surface介绍 Documentation - Apache ECharts

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(默认值 1/3)

    k = 1;

    // 计算选中效果分别在 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 ? 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: 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) * h * 0.1;

            }

            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;

        },

    };

  }

2.构建饼图函数,绘制3d图。internalDiameterRatio为透明的空心占比,0就是普通饼1就镂空

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 += 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: k,

            },

            itemStyle:{}

        };

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

            const itemStyle :any= {};

            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 += 1) {

        endValue = startValue + series[i].pieData.value;

        series[i].pieData.startRatio = startValue / sumValue;

        series[i].pieData.endRatio = endValue / sumValue;

        console.log(series[i].pieData.startRatio,

            series[i].pieData.endRatio,

            false,  

            false,

            k,

            series[i].pieData.value)

            series[i].parametricEquation = this.getParametricEquation(

            series[i].pieData.startRatio,

            series[i].pieData.endRatio,

            false,

            false,

            k,

            25//高度

        );

        startValue = endValue;

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

    }

    return series;

  }

3.绘制2d饼图,添加label指引线

series.push({

      name: 'pie2d',

      type: 'pie',

      label: {

          opacity: 1,

          fontSize: 14,

          lineHeight: 20,

      },

      labelLine: {

          length: 50,

          length2: 50,

      },

      startAngle: -50, //起始角度,支持范围[0, 360]。

      clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式

      radius: ['20%', '50%'],

      center: ['50%', '50%'],

      data: optionsData,

      itemStyle: {

          opacity: 0,

      },

    });

js代码:

  setPie(){
    const optionsData = [
      {      
          name: 'aa',
          value: 20,
          itemStyle: {
              color: '#38ACEC',
              // opacity: 1,
          },
      },
      {
        name: 'bb',
        value: 15,
        itemStyle: {
            color: '#6960EC',
            // opacity: 1,
        },
      },
      {
          name: 'cc',
          value: 25,
          itemStyle: {
              color: '#6CBB3C',
              // opacity: 1,
          },
      },
    ];
    const series = this.getPie3D(optionsData, 0.5,);
    series.push({
      name: 'pie2d',
      type: 'pie',
      label: {
          opacity: 1,
          fontSize: 14,
          lineHeight: 20,
      },
      labelLine: {
          length: 50,
          length2: 50,
      },
      startAngle: -50, //起始角度,支持范围[0, 360]。
      clockwise: false, //饼图的扇区是否是顺时针排布。上述这两项配置主要是为了对齐3d的样式
      radius: ['20%', '50%'],
      center: ['50%', '50%'],
      data: optionsData,
      itemStyle: {
          opacity: 0,
      },
    });

    this.option1 = 
    {
      legend: {
          tooltip: {
              show: true,
          },
          data: ['aa', 'bb', 'cc'],
          right: '2%',
          textStyle: {
              color: '#fff',
              fontSize: 12,
          },
      },
      tooltip: {
          formatter: (params) => {
              if (params.seriesName !== 'mouseoutSeries' && params.seriesName !== 'pie2d') {
                  let bfb = (
                      (this.option.series[params.seriesIndex].pieData.endRatio -
                          this.option.series[params.seriesIndex].pieData.startRatio) *
                      100
                  ).toFixed(2);
                  return (
                      `${params.seriesName}<br/>` +
                      `<span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;"></span>` +
                      `${bfb}%`
                  );
              }
          },
      },
      title: {
          text: '3D 饼图',
          x: 'center',
          top: '5%',
          textStyle: {
              color: '#fff',
              fontSize: 22,
          },
      },
      labelLine: {
          show: true,
          lineStyle: {
              color: '#7BC0CB',
          },
      },
      label: {
          show: true,
          position: 'outside',
          formatter: '{b} \n{c} {d}%',
      },
      xAxis3D: {
          min: -1,
          max: 1,
      },
      yAxis3D: {
          min: -1,
          max: 1,
      },
      zAxis3D: {
          min: -1,
          max: 1,
      },
      grid3D: {
          show: false,
          boxHeight: 25, // 三维笛卡尔坐标系在三维场景中的高度
          viewControl: {
              alpha: 45,
              // beta: 1000,
              distance: 300, //调整视角到主体的距离,类似调整zoom
              // rotateSensitivity: 0, // 设置为0无法旋转
              zoomSensitivity: 0, // 设置为0无法缩放
              panSensitivity: 0, // 设置为0无法平移
              autoRotate: false, // 自动旋转
          },
      },
      series: series,
    };
  }
   // 生成扇形的曲面参数方程,用于 series-surface.parametricEquation
  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(默认值 1/3)
    k = 1;
    // 计算选中效果分别在 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 ? 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: 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) * h * 0.1;
            }
            return Math.sin(v) > 0 ? 1 * h * 0.1 : -1;
        },
    };
  }
  //构建饼图数据,绘制3d图  internalDiameterRatio:透明的空心占比
  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 += 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: k,
            },
            itemStyle:{}
        };
        if (typeof pieData[i].itemStyle !== 'undefined') {
            const itemStyle :any= {};
            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 += 1) {
        endValue = startValue + series[i].pieData.value;
        series[i].pieData.startRatio = startValue / sumValue;
        series[i].pieData.endRatio = endValue / sumValue;
        console.log(series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,  
            false,
            k,
            series[i].pieData.value)
            series[i].parametricEquation = this.getParametricEquation(
            series[i].pieData.startRatio,
            series[i].pieData.endRatio,
            false,
            false,
            k,
            25//高度
        );
        startValue = endValue;
        legendData.push(series[i].name);
    }
    return series;
  }

html代码:

<div  id="option" echarts [options]="option1"  style="width:100%;height:100%"></div>

转载请注明出处或者链接地址:https://www.qianduange.cn//article/351.html
标签
echarts
评论
发布的文章
大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!