不规则渐变饼图

描述:当前是关于Echarts图表中的 饼图 示例。
 
            let data = [

      { value: 45, name: "2室1厅1卫" },

      { value: 52, name: "3室1厅1卫" },

      { value: 30, name: "2室2厅1卫" },

      { value: 60, name: "3室2厅1卫" }

];

let color = [

   { colorStart: "rgba(7, 166, 255,0)", colorEnd: "rgba(7, 166, 255,0)", solid: 'rgba(7, 166, 255,1)' },

   { colorStart: "rgba(255, 255, 255,0)", colorEnd: "rgba(255, 255, 255,0)", solid: 'rgba(255, 255, 255,1)' },

   { colorStart: "rgba(255, 208, 118,0)", colorEnd: "rgba(255, 208, 118,0.4)", solid: 'rgba(255, 208, 118,1)' },

   { colorStart: "rgba(72, 235, 236,0)", colorEnd: "rgba(72, 235, 236,0.4)", solid: 'rgba(72, 235, 236,1)' }

];

// 无渐变颜色
    let colorNoGradient = ["rgba(7, 166, 255,1)", "rgba(255, 255, 255,1)", "rgba(255, 208, 118,1)", "rgba(72, 235, 236,1)"]

// 计算总数

let total = data.map(v => v.value).reduce((o, n) => o + n);



// 计算每一个data的其实角度和末了角度 θ1和θ2

data.reduce((o, v) => {

   v.theta1 = o;

   v.theta2 = o + v.value / total;

   return v.theta2

}, 0);

// 添加渐变

data.forEach((v, i) => {

   let ops = calc(v.theta1 * 2 * Math.PI, v.theta2 * 2 * Math.PI);

   if (v.value) v.itemStyle = {

      color: {

         type: 'radial',

         x: ops.center[0],

         y: ops.center[1],

         r: ops.radius,

         colorStops: [{

            offset: 0, color: color[i].colorStart

         }, {

            offset: 0.32, color: color[i].colorStart

         }, {

            offset: 0.95, color: color[i].colorEnd

         }, {

            offset: 0.95, color: color[i].solid

         }, {

            offset: 1, color: color[i].solid

         }],

         globalCoord: false // 缺省为 false

      }

   }

})

let option = {

   backgroundColor:'#00266b',

      grid: {

        left: 0, // -100,

        top: 50,

        bottom: 10,

        right: 10

        // containLabel: true

      },

      tooltip: {

        trigger: "item",

        axisPointer: {

          type: 'shadow',

        },

        backgroundColor: 'rgba(9, 24, 48, 0.5)',

        borderColor: 'rgba(255,255,255,0.4)',

        textStyle: {

          color: '#fff',

          align: 'left',

          textAlign: 'left'

        },

        borderWidth: 1,

        formatter: "{b} : {c} ({d}%)"

      },


      calculable: true,
      legend: {
        icon: 'none',
        show: false,
        orient: 'vertical',
        right: '0%',
        top: '20%',
        itemGap: 30,
        itemWidth: 12,
        itemHeight: 12,
        selectedMode:false, // 关闭图例选择
        textStyle: {
          color: '#fff',
          fontSize: 13,
          rich: {
            // iconName: {
            //   width: 5,
            //   height: 8,
            //   background: '#fff'
            // },
            name: {
              color: '#FFF',
              fontSize: 28,
              // width: 200,
              padding: [0, 0, 0, 30],
            },
            value: {
              color: '#2BDFD4',
              fontSize: 28,
              // width: 80,
              padding: [0, 0, 0, 30]
            },
            percent: {
              color: '#2BDFD4',
              fontSize: 28,
              padding: [0, 0, 0, 30]
            },
            unit: {
              color: '#ACDCE4',
              fontSize: 18,
              padding: [0, 0, 0, 5]
            }
          }
        },
        
        formatter: name => {
          let obj = data.find(item => item.name === name);
          let datas = data;
          let total = 0;
          let target = obj.value
          for (let i = 0; i < datas.length; i++) {
            total += datas[i].value;
          }
          const arr = [
            `{iconName|}{name|${name}}{value|${obj.value}}{unit|人}{percent|${((target / total) * 100).toFixed(0)}}{unit|%}`
          ];
          return arr.join('')
        },
         data: data.map((dItem,dIndex) => {
          return {
            ...dItem,
            textStyle: {
              rich: {
                iconName: {
                  width: 4,
                  height: 16,
                  borderRadius: 5,
                  backgroundColor: colorNoGradient[dIndex],
                },
                percent: {
                  color: colorNoGradient[dIndex],
                },
              }
            },

          }
        }),
       
      },
     
        series: [
        {
          // radius: ["90", "100"],
          // center: ["40%", "50%"],
          type: "pie",
          radius: "10%",
          hoverAnimation: false,

          labelLine: { show: false, },

          itemStyle: { color: '#c2d7fd' },

          tooltip: { show: false },

          data: [0]

        },

        {

          type: "pie",
          // center: ["40%", "50%"],
          // radius: ["15%", "16%"],
          radius: '10%',

          hoverAnimation: false,

          labelLine: { show: false, },

          itemStyle: { color: '#c2d7fd', opacity: 0.4 },

          tooltip: { show: false },

          data: [0]

        },
        {
          type: "pie",
          // center: ["40%", "50%"],
          radius: ["71%", "72%"],
          hoverAnimation: false,

          labelLine: { show: false, },

          itemStyle: { color: '#b7cffc', opacity: 0.4 },

          tooltip: { show: false },

          data: [0]

        },

        {

          type: "pie",
          // center: ["40%", "50%"],
          radius: ["20%", "80%"],
          name: '保障房套数分析',
          roseType: true,
          zlevel: 10,

          label: {

            show: false,

            formatter: params => {

              let datas = data;

              let target;

              var total = 0;

              for (let i = 0; i < datas.length; i++) {

                total += datas[i].value;

                if (datas[i].name == params.name) {

                  target = datas[i].value;

                }

              }

              let arr = ["{a|" + params.data.name + '   ' + ((target / total) * 100).toFixed(0) + "%}"];

              return arr.join("\n");

            },

            rich: {

              a: {

                fontSize: 12,

                color: "#eef4ff",

                lineHeight: 50,

                align: 'left'

              }

            },

            height: 70,

            padding: [0, -30],

            distanceToLabelLine: 0,

            alignTo: 'labelLine'

          },

          labelLine: {

            length: 10,

            length2: 35,

            lineStyle: {

              color: "rgba(255,255,255,0.4)",

              type: "dashed"

            }

          },

          data

        }

      ]


};



function calc(theta1, theta2) {

   let r = 0.5; // 半径是0.5 其实表示0.5个直径

   let inner = 0.5; // 里面镂空部分占60%  option中radius为[33%, 55%]  55 * 0.6 == 33



   let cos = Math.cos;

   let sin = Math.sin;

   let PI = Math.PI;

   let min = Math.min;

   let max = Math.max;



   let bottom = 0;

   let left = 2 * r;

   let right = 0;



   // y0: θ1对应的外部点的y值

   // y1: θ2对应的外部点的y值

   // _y0: θ1对应的内部点的y值

   // _y1: θ2对应的内部点的y值

   // x0: θ1对应的外部点的x值

   // x1: θ2对应的外部点的x值

   // _x0: θ1对应的内部点的x值

   // _x1: θ2对应的内部点的x值

   let y0 = r * (1 - cos(theta1));

   let y1 = r * (1 - cos(theta2));



   let _y0 = r * (1 - inner * cos(theta1));

   let _y1 = r * (1 - inner * cos(theta2));



   // 如果这个弧经过θ == PI的点  则bottom = 2PI

   // bottom用于之后的max计算中

   if (theta1 < PI && theta2 > PI) {

      bottom = 2 * r;

   }

   // 计算这几个点的最大最小值

   let ymin = min(_y0, _y1, y0, y1);

   let ymax = max(_y0, _y1, y0, y1, bottom);



   let x0 = r * (1 + sin(theta1));

   let x1 = r * (1 + sin(theta2));



   let _x0 = r * (1 + inner * sin(theta1));

   let _x1 = r * (1 + inner * sin(theta2));



   // 如果这个弧经过θ == PI / 2的点  则right = 2PI

   if (theta1 < PI / 2 && theta2 > PI / 2) {

      right = 2 * r;

   }

   // 如果这个弧经过θ == PI / 2 * 3的点  则left = 0

   if (theta1 < PI / 2 * 3 && theta2 > PI / 2 * 3) {

      left = 0;

   }

   let xmin = min(_x0, _x1, x0, x1, left);

   let xmax = max(_x0, _x1, x1, x0, right);



   return {

      // 计算圆心以及半径

      center: [(r - xmin) / (xmax - xmin), (r - ymin) / (ymax - ymin)],

      radius: r / min(xmax - xmin, ymax - ymin)

   }

}