用echarts实现3d饼图

安装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>

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ECharts是百度开源的一个基于JavaScript的可视化图表库,支持多种图表类型,包括饼图ECharts中的饼图可以通过配置项来实现立体3D效果。 下面是一个简单的立体3D饼状图的示例代码: ```javascript var myChart = echarts.init(document.getElementById('main')); // 配置数据 var data = [ {value: 335, name: '直接访问'}, {value: 310, name: '邮件营销'}, {value: 234, name: '联盟广告'}, {value: 135, name: '视频广告'}, {value: 1548, name: '搜索引擎'} ]; // 配置选项 var option = { title: { text: '立体3D饼状图', left: 'center' }, series: [ { name: '访问来源', type: 'pie', radius: ['50%', '70%'], avoidLabelOverlap: false, label: { show: false, position: 'center' }, emphasis: { label: { show: true, fontSize: '30', fontWeight: 'bold' } }, labelLine: { show: false }, data: data, itemStyle: { emphasis: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' }, normal: { color: function(params) { var colorList = [ '#C1232B', '#B5C334', '#FCCE10', '#E87C25', '#27727B' ]; return colorList[params.dataIndex] } } }, // 3D效果配置 // z: 10, // 3D效果的深度 // depth: 30, // 3D效果的深度 // label: { // formatter: '{b}: {d}%' // }, // labelLine: { // show: true // }, // itemStyle: { // borderColor: '#fff', // borderWidth: 2 // }, // animationType: 'scale', // animationEasing: 'elasticOut', // animationDelay: function (idx) { // return Math.random() * 200; // } } ] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); ``` 在上面的示例代码中,通过设置`series`数组中的`itemStyle`属性中的`normal`属性来实现立体3D效果。在`normal`属性中,通过设置`color`属性来定义每个扇形区域的颜色。 如果需要进一步调整3D效果的参数,可以在`series`数组中添加如下配置项: ```javascript series: [ { // ... // 3D效果配置 z: 10, // 3D效果的深度 depth: 30, // 3D效果的深度 label: { formatter: '{b}: {d}%' }, labelLine: { show: true }, itemStyle: { borderColor: '#fff', borderWidth: 2 }, animationType: 'scale', animationEasing: 'elasticOut', animationDelay: function (idx) { return Math.random() * 200; } } ] ``` 在上面的配置项中,可以通过`z`属性和`depth`属性来调整3D效果的深度,通过`label`属性和`labelLine`属性来设置标签的样式,通过`itemStyle`属性来设置扇形区域的边框和动画效果的样式。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值