天天看點

echarts常用餅圖、圓環圖示例

最近做可視化比較多,就常用的圖表類型做了一下總結。 

因為做可視化的圖表代碼量非常大,是以會把echarts圖表單獨抽離出來,封裝成一個元件,也可以複用,是以這裡我直接把封裝的元件直接放在這裡,是可以直接拿來用的,根據所需稍作修改即可。

這裡都是用的vue3,其實和vue2差不多,各式各樣的花裡胡哨的圖表無非就是option配置不同,如果使用的是vue2,在寫法上稍作修改即可

vue2、vue3元件封裝模闆連結  ​​echarts圖表元件封裝模闆​​

1. 精簡圓環圖

echarts常用餅圖、圓環圖示例
<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script setup>
import { fontChart } from '@/utils/echartPxToRem'
import * as echarts from "echarts";
import { onMounted, watch, onUnmounted } from "vue";

const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  text: {
    type: String,
    default: '這個是頭'
  },
  value: {
    type: Number,
    default: 50
  },
  totalNum: {
    type: Number,
    default: 150
  }
});

watch(
  () => props.Value,
  (newValue) => {
    drawPie()
  },
  {
    deep: true
  }
)

let charts = "";  // 這裡不要讓它成為響應式

onMounted(() => {
  drawPie();
});

onUnmounted(() => {
  window.removeEventListener('resize', selfAdaption)
})
//  初始化echarts
const drawPie = () => {
  charts = echarts.init(document.getElementById(props.id));
  let option = {
    color: ['#414f5b', '#1890ff'],
    series: [
      {
        name: '任務進度',
        type: 'pie',
        radius: ['65%', '95%'],
        avoidLabelOverlap: false,
        hoverAnimation: false,
        labelLine: {
          normal: {
            show: false
          }
        },
        data: [{
          value: props.totalNum - props.value,
          label: {
            normal: {
              show: false,
            }
          }
        }, {   // 資料值
          value: props.value,
          // 資料項名稱
          // name: props.name,
          //該資料項是否被選中
          selected: false,
          // 單個扇區的标簽配置
          label: {
            // 是顯示标簽
            show: true,
            position: 'center',
            fontSize: fontChart(10),
            color: '#fff',
            lineHeight: fontChart(15),
            formatter: `{b| ${props.text}}` + '\n\n' + `{c| 打鑽進尺}` + '\n\n' + `{a| ${props.value} }` + '\n\n' + `{d| 米}`, //  \n換行  兩個就換兩行   a、b、c、d後面不能有空格!
            rich: {
              a: {
                color: '#1890ff',
                fontSize: fontChart(35),
                fontWeight: 600,
              },
              b: {
                color: '#fff',
                fontSize: fontChart(25),
                fontWeight: 600,
              },
              c: {
                color: '#fff',
                fontSize: fontChart(25),
                fontWeight: 600,
              },
              d: {
                color: '#fff',
                fontSize: fontChart(25),
                fontWeight: 600,
              }
            }
          },
        }]
      }
    ]
  }
  charts.setOption(option)
  window.addEventListener('resize', selfAdaption)
};

//  自适應
function selfAdaption() {
  if(!charts) return
  charts.resize()
  drawPie()
}
</script>      

2. 經典餅圖 (1)

echarts常用餅圖、圓環圖示例
<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script setup>
import { fontChart } from '@/utils/echartPxToRem'
import * as echarts from "echarts";
import { onMounted, watch, onUnmounted } from "vue";

const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  title: {
    type: String,
    default: '風險等級統計'
  },
  colorList: {
    type: Array,
    default:() => ['#008eea','#fac800','#ff7d00','#f00041']
  },
  pieData: {
    type: Array,
    default:() => ([{name:'低風險',value:10},{name:'一般風險',value:6},{name:'較大風險',value:3},{name:'重大風險',value:1}])
  },
  seriesCenter: {
    type: Array,
    default:() => ['60%','50%']
  }
});

watch(
  () => props.pieData,
  (newValue) => {
    drawPie()
  },
  {
    deep: true
  }
)

let charts = "";  // 這裡不要讓它成為響應式

onMounted(() => {
  drawPie();
});

onUnmounted(() => {
  window.removeEventListener('resize', selfAdaption)
})
//  初始化echarts
const drawPie = () => {
  charts = echarts.init(document.getElementById(props.id));
  let option = {
    title: {
      show: false,
      left: '50%',
      top: 'auto',
      text: props.title,
      textStyle: {
        color:'#868a96',
        fontSize:fontChart(13)
      }
    },
    legend: {
      textStyle: {
        color:'#868a96',
        fontSize:fontChart(13)
      },
      itemWidth: fontChart(20),  // 設定寬度
      itemHeight: fontChart(10), // 設定高度
      x : '5%',
      y : 'center',
      orient : 'vertical',  //horizontal、vertical可選
    },
    color: props.colorList,
    series: [
      {
        // name: 'Nightingale Chart',
        type: 'pie',
        center: props.seriesCenter,
        label: {
          normal: {
            show: true,
            position:'inner', //outside、inside同inner、center 可選
            formatter: '{c}',
            fontSize:fontChart(13)
          },
        },
        data: props.pieData
      }
    ]
  };
  charts.setOption(option)
  window.addEventListener('resize', selfAdaption)
};

//  自适應
function selfAdaption() {
  if(!charts) return
  charts.resize()
  drawPie()
}
</script>      

3. 經典圖餅圖(2)

echarts常用餅圖、圓環圖示例
<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script setup>
import { fontChart } from '@/utils/echartPxToRem'
import * as echarts from "echarts";
import { onMounted, watch, onUnmounted } from "vue";

const props = defineProps({
  id: {
    type: String,
    required:true  //聲明這個參數是否必須傳入
  },
  dataList: {
    type: Array,
    default: () => ([{ value: 1048, name: 'Search Engine' }, { value: 735, name: 'Direct' }, { value: 580, name: 'Email' }, { value: 484, name: 'Union Ads' }, { value: 300, name: 'Video Ads' }])
  }
});

watch(
  () => props.dataList,
  (newValue) => {
    drawPie()
  },
  {
    deep: true
  }
)

let charts = "";  // 這裡不要讓它成為響應式

onMounted(() => {
  drawPie();
});

onUnmounted(() => {
  window.removeEventListener('resize', selfAdaption)
})
//  初始化echarts
const drawPie = () => {
  charts = echarts.init(document.getElementById(props.id));
  let option = {
    title: {
      show:true,
      text: 'Referer of a Website',
      // subtext: 'Fake Data',
      left: 'center',
      textStyle: {//主标題文本樣式{"fontSize": 18,"fontWeight": "bolder","color": "#333"}
        fontSize: fontChart(18),
        color:'#fff'
      },
      subtextStyle: {//副标題文本樣式{"color": "#aaa"}
        fontSize: fontChart(12),
        color:'#fff'
      },
    },
    tooltip: {
      trigger: 'item'
    },
    legend: {
      orient: 'vertical',  //horizontal   vertical
      left: 'left',
      align:'left',
      top:'middle',
      itemWidth: fontChart(20),  // 設定寬度
      itemHeight: fontChart(10), // 設定高度
      textStyle: {
        color:'#fff',
        fontSize:fontChart(12)
      }
    },
    series: [
      {
        name: 'Access From',
        type: 'pie',
        radius: '50%',
        data: props.dataList,
        center: ['65%','50%'],
        label: {
          normal: {
            show: true,
            position:'outside',  //outside、inside同inner、center 可選
            formatter: '{c}',
            fontSize:fontChart(13)
          },
        },
        emphasis: {
          itemStyle: {
            shadowBlur: fontChart(10),
            shadowOffsetX: 0,
            shadowColor: 'rgba(0, 0, 0, 0.5)'
          }
        }
      }
    ]
  }
  charts.setOption(option)
  window.addEventListener('resize', selfAdaption)
};

//  自适應
function selfAdaption() {
  if(!charts) return
  charts.resize()
  drawPie()
}
</script>      

4.  動态圓環(圖例自定義位置) 

echarts常用餅圖、圓環圖示例
<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script>
import { fontChart } from "@/utils/echartPxToRem";
import * as echarts from "echarts";
export default {
  data() {
    return {
      charts: "",
      timer: null,
    };
  },
  props: {
    id: {
      type: String,
      required: true, //聲明這個參數是否必須傳入
    },
    dataList: {
      type: Array,
      default: () => ([
        {
          name: "平平果蔬種植基地",
          value: 2,
        },
        {
          name: "馥羽草莓基地",
          value: 8,
        },
        {
          name: "紅梅家庭農場",
          value: 4,
        },
        {
          name: "三郭城家庭農場",
          value: 8,
        },
        {
          name: "嘉祺農業基地",
          value: 4,
        },
      ])
    }
  },
  watch: {
    dataList() {
      this.drawBar();
    },
  },
  mounted() {
    this.drawBar();
  },
  destroyed() {
    window.removeEventListener("resize", this.selfAdaption);
    if (this.timer) clearInterval(this.timer);
  },
  methods: {
    drawBar() {
      this.charts = echarts.init(document.getElementById(this.id));
      let angle = 0;
      let data = [];
      let data2 = [];
      var color = [
        "#2A8BFD",
        "#BAFF7F",
        "#00FAC1",
        "#00CAFF",
        "#FDE056",
        "#4ED33C",
        "#FF8A26",
        "#FF5252",
        "#9689FF",
        "#CB00FF",
      ];
      let lefts = ['6%', '6%', '70%', '70%', '70%']
      let tops = ['25%', '55%', '10%', '40%', '70%']
      let legendData = [];
      let total = 0
      this.dataList.forEach(item => {
        total += item.value
      })
      for (let i = 0; i < this.dataList.length; i++) {
        let bfb = parseInt((this.dataList[i].value / total) * 100) + '%'
        legendData.push({
          show: true,
          icon: "circle", //'circle', 'rect', 'roundRect', 'triangle', 'diamond', 'pin', 'arrow', 'none'
          left: lefts[i],
          top: tops[i],
          itemWidth: fontChart(10),
          itemHeight: fontChart(10),
          itemStyle: {
            color: color[i],
          },
          formatter: `{aa| `+ this.dataList[i].name +` }` + `\n\n` + `{bb| ` +bfb+ `}`,  // 也可以是個函數return
          x: "left",
          textStyle: {
            // color: "#BAFF7F",
            rich: {
              aa: {
                color: "#ffffff",
              },
              bb: {
                color: color[i]
              }
            }
          },
          data: [this.dataList[i].name],
        });
        data.push(
          {
            value: this.dataList[i].value,
            name: this.dataList[i].name,
            itemStyle: {
              normal: {
                borderWidth: 8,
                shadowBlur: 20,
                borderRadius: 20,
                borderColor: color[i],
                shadowColor: color[i],
              },
            },
          },
          {
            value: 1.5,
            name: "",
            itemStyle: {
              normal: {
                label: {
                  show: false,
                },
                labelLine: {
                  show: false,
                },
                color: "rgba(0, 0, 0, 0)",
                borderColor: "rgba(0, 0, 0, 0)",
                borderWidth: 0,
              },
            },
          }
        );
        data2.push(
          {
            value: this.dataList[i].value,
            name: this.dataList[i].name,
          },
          {
            value: 5,
            name: "",
            itemStyle: {
              normal: {
                label: {
                  show: false,
                },
                labelLine: {
                  show: false,
                },
                color: "rgba(0, 0, 0, 0)",
                borderColor: "rgba(0, 0, 0, 0)",
                borderWidth: 0,
                opacity: 0.2,
              },
            },
          }
        );
      }
      let option = {
        // backgroundColor: "#061740",
        // color: color,
        legend: legendData,
        series: [
          {
            //外線1
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              return {
                type: "arc",
                shape: {
                  cx: api.getWidth() / 2,
                  cy: api.getHeight() / 2,
                  r: (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.6,
                  startAngle: ((0 + angle) * Math.PI) / 180,
                  endAngle: ((90 + angle) * Math.PI) / 180,
                },
                style: {
                  stroke: "#4EE9E6",
                  fill: "transparent",
                  lineWidth: 1.5,
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //内線1
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              return {
                type: "arc",
                shape: {
                  cx: api.getWidth() / 2,
                  cy: api.getHeight() / 2,
                  r: (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.6,
                  startAngle: ((180 + angle) * Math.PI) / 180,
                  endAngle: ((270 + angle) * Math.PI) / 180,
                },
                style: {
                  stroke: "#4EE9E6",
                  fill: "transparent",
                  lineWidth: 1.5,
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //外線2
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              return {
                type: "arc",
                shape: {
                  cx: api.getWidth() / 2,
                  cy: api.getHeight() / 2,
                  r: (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.65,
                  startAngle: ((270 + -angle) * Math.PI) / 180,
                  endAngle: ((40 + -angle) * Math.PI) / 180,
                },
                style: {
                  stroke: "#4EE9E6",
                  fill: "transparent",
                  lineWidth: 1.5,
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //外線2
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              return {
                type: "arc",
                shape: {
                  cx: api.getWidth() / 2,
                  cy: api.getHeight() / 2,
                  r: (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.65,
                  startAngle: ((90 + -angle) * Math.PI) / 180,
                  endAngle: ((220 + -angle) * Math.PI) / 180,
                },
                style: {
                  stroke: "#4EE9E6",
                  fill: "transparent",
                  lineWidth: 1.5,
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //綠點1
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              let x0 = api.getWidth() / 2;
              let y0 = api.getHeight() / 2;
              let r = (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.65;
              let point = this.getCirlPoint(x0, y0, r, 90 + -angle);
              return {
                type: "circle",
                shape: {
                  cx: point.x,
                  cy: point.y,
                  r: 4,
                },
                style: {
                  stroke: "#66FFFF", //粉
                  fill: "#66FFFF",
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //綠點2
            name: "", //綠點
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              let x0 = api.getWidth() / 2;
              let y0 = api.getHeight() / 2;
              let r = (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.65;
              let point = this.getCirlPoint(x0, y0, r, 270 + -angle);
              return {
                type: "circle",
                shape: {
                  cx: point.x,
                  cy: point.y,
                  r: 4,
                },
                style: {
                  stroke: "#66FFFF", //粉
                  fill: "#66FFFF",
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //綠點3
            name: "",
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              let x0 = api.getWidth() / 2;
              let y0 = api.getHeight() / 2;
              let r = (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.6;
              let point = this.getCirlPoint(x0, y0, r, 90 + angle);
              return {
                type: "circle",
                shape: {
                  cx: point.x,
                  cy: point.y,
                  r: 4,
                },
                style: {
                  stroke: "#66FFFF", //粉
                  fill: "#66FFFF",
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            //綠點4
            name: "", //綠點
            type: "custom",
            coordinateSystem: "none",
            renderItem: (params, api) => {
              let x0 = api.getWidth() / 2;
              let y0 = api.getHeight() / 2;
              let r = (Math.min(api.getWidth(), api.getHeight()) / 2) * 0.6;
              let point = this.getCirlPoint(x0, y0, r, 270 + angle);
              return {
                type: "circle",
                shape: {
                  cx: point.x,
                  cy: point.y,
                  r: 4,
                },
                style: {
                  stroke: "#66FFFF", //粉
                  fill: "#66FFFF",
                },
                silent: true,
              };
            },
            data: [0],
          },
          {
            name: "",
            type: "pie",
            clockWise: false,
            radius: ["98%", "95%"],
            hoverAnimation: false,
            center: ["50%", "50%"],
            top: "center",
            itemStyle: {
              normal: {
                label: {
                  show: false,
                },
              },
            },
            data: data,
          },
          {
            type: "pie",
            top: "center",
            startAngle: 90,
            clockwise: false,
            center: ["50%", "50%"],
            legendHoverLink: false,
            hoverAnimation: false,
            radius: ["94%", "55%"],
            itemStyle: {
              opacity: 0.15,
            },
            label: {
              show: false,
              position: "center",
            },
            labelLine: {
              show: false,
            },
            data: data2,
          },
          {
            name: "",
            type: "pie",
            clockWise: false,
            center: ["50%", "50%"],
            radius: ["39%", "38%"],
            hoverAnimation: false,
            top: "center",
            itemStyle: {
              normal: {
                label: {
                  show: false,
                },
              },
            },
            data: data,
          },
        ],
      };
      this.charts.setOption(option, true);
      if(this.timer) clearInterval(this.timer)
      this.timer = setInterval(() => {
        angle = angle + 3;
        this.charts.setOption(option, true);
      }, 100);
      window.addEventListener("resize", this.selfAdaption);
    },
    //擷取圓上面某點的坐标(x0,y0表示坐标,r半徑,angle角度)
    getCirlPoint(x0, y0, r, angle) {
      let x1 = x0 + r * Math.cos((angle * Math.PI) / 180);
      let y1 = y0 + r * Math.sin((angle * Math.PI) / 180);
      return {
        x: x1,
        y: y1,
      };
    },
    // 自适應
    selfAdaption() {
      if (!this.charts) return;
      this.charts.resize();
      clearInterval(this.timer);
      this.drawBar();
    },
  },
};
</script>      

5.  圓環嵌套 

echarts常用餅圖、圓環圖示例
<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script setup>
import { fontChart } from "@/utils/echartPxToRem";
import * as echarts from "echarts";
import { onMounted, watch, onUnmounted, computed } from "vue";

const props = defineProps({
  id: {
    type: String,
    required: true, //聲明這個參數是否必須傳入
  },
  dataList: {
    type: Array,
    default: () => [
      {
        name: "過高",
        value: 60,
      },
      {
        name: "偏高",
        value: 40,
      },
      {
        name: "正常",
        value: 20,
      },
    ],
  },
});

watch(
  () => props.dataList,
  (newValue) => {
    drawPie();
  },
  {
    deep: true,
  }
);

let charts = ""; // 這裡不要讓它成為響應式
const total = computed(() => {
  let total = 0;
  props.dataList.forEach((item) => {
    total += item.value;
  });
  return total;
});

onMounted(() => {
  drawPie();
});

onUnmounted(() => {
  window.removeEventListener("resize", selfAdaption);
});
//  初始化echarts
const drawPie = () => {
  charts = echarts.init(document.getElementById(props.id));
  let arrName = getArrayValue(props.dataList, "name");
  let arrValue = getArrayValue(props.dataList, "value");
  let sumValue = getsumValue(props.dataList, "value");
  let optionData = getData(props.dataList, sumValue);
  let option = {
    // backgroundColor: "RGB(8,20,67)",
    grid: {
      // top: "16%",
      // bottom: "54%",
      // left: "50%",
      containLabel: false,
    },
    legend: {
      show: false,
      bottom: 6,
      icon: "rect",
      orient: "vertical",
      itemHeight: 10,
      itemWidth: 10,
      left: 800,
      top: 200,
      show: true,
      data: arrName,
      selectedMode: false,
      textStyle: {
        color: "#96F5F6",
        fontSize: 16,
      },
      formatter: (name) => {
        let tarValue;
        for (var i = 0; i < 4; i++) {
          if (props.dataList[i].name == name) {
            tarValue = props.dataList[i].value;
            break;
          }
        }
        var p = Math.round((tarValue / sumValue) * 1000) / 10;
        return `${name}     ${p}%`;
      },
    },
    yAxis: [
      {
        type: "category",
        inverse: true,
        z: 3,
        axisLine: {
          show: false,
        },
        axisTick: {
          show: false,
        },
        axisLabel: {
          show: false,
          interval: 0,
          inside: false,
          textStyle: {
            color: "RGB(78,184,252)",
            fontSize: 25,
          },
        },
        data: optionData.yAxis,
      },
    ],
    xAxis: [
      {
        show: false,
      },
    ],
    series: optionData.series,
  };
  charts.setOption(option);
  window.addEventListener("resize", selfAdaption);
};

function getsumValue(array, key) {
  let sum = 0;
  for (let i = 0; i < array.length; i++) {
    sum = sum + array[i].value;
  }
  return sum;
}

function getArrayValue(array, key) {
  var key = key || "value";
  var res = [];
  if (array) {
    array.forEach(function (t) {
      res.push(t[key]);
    });
  }
  return res;
}

function getData(dataList, sumValue) {
  let colorList2 = [
    {
      c1: "#fdd660",
      c2: "#ffa934",
    },
    {
      c1: "#03edff",
      c2: "#1d7dff",
    },
    {
      c1: "#4cfac1",
      c2: "#15d481",
    },
  ];
  var res = {
    series: [],
    yAxis: [],
    formatter: [],
  };
  for (let i = 1; i <= dataList.length; i++) {
    res.series.push({
      name: "學曆",
      type: "pie",
      clockWise: true,
      z: 2,
      hoverAnimation: false,
      radius: [103 - i * 15 + "%", 95 - i * 15 + "%"],
      center: ["50%", "50%"],
      // label: {
      //   show: false,
      // },
      labelLine: {
        show: false,
      },
      itemStyle: {
        normal: {
          label: {
            show: true, //開啟顯示
            position: "inside", //在上方顯示
            textStyle: {
              //數值樣式
              color: "#fff",
              fontSize: fontChart(10),
            },
            formatter: (data) => {
              let value = data.value;
              return ((value / total.value) * 100).toFixed(1) + "%";
            },
            textBorderColor: "#000",
            textBorderWidth: 1,
          },
          color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
            {
              offset: 0,
              color: colorList2[i - 1].c2,
            },
            {
              offset: 1,
              color: colorList2[i - 1].c1,
            },
          ]),
        },
      },
      data: [
        {
          value: dataList[i - 1].value,
          name: dataList[i - 1].name,
        },
        {
          value: sumValue - dataList[i - 1].value,
          name: "",
          itemStyle: {
            color: "rgba(0,0,0,0)",
            borderWidth: 0,
          },
          tooltip: {
            show: false,
          },
          label: {
            show: false,
          },
          hoverAnimation: false,
        },
      ],
    });
    res.series.push({
      name: "背景線",
      type: "pie",
      silent: true,
      z: 1,
      clockWise: true,
      hoverAnimation: false,
      radius: [100 - i * 15 + "%", 98 - i * 15 + "%"],
      center: ["50%", "50%"],
      label: {
        show: false,
      },
      itemStyle: {
        label: {
          show: false,
        },
        labelLine: {
          show: false,
        },
        borderWidth: 5,
      },
      data: [
        {
          value: 100,
          itemStyle: {
            color: "RGB(12,64,128)",
            borderWidth: 0,
          },
          tooltip: {
            show: false,
          },
          hoverAnimation: false,
        },
      ],
    });
    res.yAxis.push(dataList[i - 1].name);
  }
  return res;
}

//  自适應
function selfAdaption() {
  if (!charts) return;
  charts.resize();
  drawPie();
}
</script>      

6.  儀表盤進度 

<template>
  <div :id="id" style="width: 100%; height: 100%"></div>
</template>
<script setup>
import { fontChart } from "@/utils/echartPxToRem";
import * as echarts from "echarts";
import { onMounted, watch, onUnmounted } from "vue";

const props = defineProps({
  id: {
    type: String,
    required: true,
  },
  value: {
    type: Number,
    default: 65.23,
  },
  title: {
    type: String,
    default: "高尿酸",
  },
});

watch(
  () => props.value,
  (newValue) => {
    drawPie();
  },
  {
    deep: true,
  }
);

let charts = ""; // 這裡不要讓它成為響應式
let timer = null;

onMounted(() => {
  drawPie();
});

onUnmounted(() => {
  window.removeEventListener("resize", selfAdaption);
  if (timer) clearInterval(timer);
});
//  初始化echarts
const drawPie = () => {
  charts = echarts.init(document.getElementById(props.id));
  let int = props.value.toFixed(2).split(".")[0];
  let float = props.value.toFixed(2).split(".")[1];
  let option = {
    backgroundColor: "transparent",
    title: {
      text: "{a|" + int + "}{b|." + float + "}\n{c|" + props.title + "}",
      x: "center",
      y: "center",
      textStyle: {
        rich: {
          a: {
            fontSize: fontChart(15),
            color: "#fff",
            fontWeight: "600",
          },
          b: {
            fontSize: fontChart(15),
            color: "#fff",
            padding: [0, 0, 0, 0],
          },
          c: {
            fontSize: fontChart(15),
            color: "#fff",
            padding: [5, 0],
          },
        },
      },
    },
    series: [
      {
        type: "gauge",
        radius: "60%",
        clockwise: false,
        startAngle: "90",
        endAngle: "-269.9999",
        splitNumber: 30,
        detail: {
          offsetCenter: [0, -20],
          formatter: " ",
        },
        pointer: {
          show: false,
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: [
              [0, "#2CFAFC"],
              [36.7 / 100, "#0ff"],
              [1, "#0f232e"],
            ],
            width: 20,
          },
        },
        axisTick: {
          show: false,
        },
        splitLine: {
          show: true,
          length: 120,
          lineStyle: {
            shadowBlur: 10,
            shadowColor: "rgba(0, 255, 255, 1)",
            shadowOffsetY: "0",
            color: "#020f18",
            width: 2,
          },
        },
        axisLabel: {
          show: false,
        },
      },
      {
        type: "pie",
        radius: ["64%", "65%"],
        hoverAnimation: false,
        clockWise: false,
        itemStyle: {
          normal: {
            color: "transparent",
          },
        },
        label: {
          show: false,
        },
        data: dashed(),
      },
      {
        type: "pie",
        radius: [0, "50%"],
        hoverAnimation: false,
        clockWise: false,
        itemStyle: {
          normal: {
            shadowBlur: 20,
            shadowColor: "#000",
            color: new echarts.graphic.RadialGradient(0.4, 0.3, 1, [
              {
                offset: 0,
                color: "#0FF",
              },
              {
                offset: 1,
                color: "#060f20",
              },
            ]),
          },
        },
        label: {
          show: false,
        },
        data: [100],
      },
      {
        type: "pie",
        radius: ["84%", "85.5%"],
        hoverAnimation: false,
        clockWise: false,
        itemStyle: {
          normal: {
            shadowBlur: 20,
            shadowColor: "rgba(0, 255, 255,.3)",
            color: "#0f232e",
          },
        },
        label: {
          show: false,
        },
        data: [100],
      },
      {
        type: "pie",
        radius: ["88%", "89.5%"],
        hoverAnimation: false,
        clockWise: false,
        itemStyle: {
          normal: {
            shadowBlur: 20,
            shadowColor: "rgba(0, 255, 255,.3)",
            color: "rgba(15, 35, 46,.6)",
          },
        },
        label: {
          show: false,
        },
        data: [100],
      },
    ],
  };
  charts.setOption(option);
  startTimer()
  window.addEventListener("resize", selfAdaption);
};

function dashed() {
  let dataArr = [];
  for (var i = 0; i < 100; i++) {
    if (i % 2 === 0) {
      dataArr.push({
        name: (i + 1).toString(),
        value: 20,
        itemStyle: {
          normal: {
            color: "rgb(0,255,255,.3)",
          },
        },
      });
    } else {
      dataArr.push({
        name: (i + 1).toString(),
        value: 20,
        itemStyle: {
          normal: {
            color: "rgb(0,0,0,0)",
            borderWidth: 1,
            borderColor: "rgba(0,255,255,1)",
          },
        },
      });
    }
  }
  return dataArr;
}
function doing() {
  let option = charts.getOption();
  option.series[1].startAngle = option.series[1].startAngle - 1;
  charts.setOption(option);
}
function startTimer() {
  if(timer) clearInterval(timer)
  timer = setInterval(doing, 100);
}

//  自适應
function selfAdaption() {
  if (!charts) return;
  charts.resize();
  drawPie();
}
</script>      

7. 雙圓 (外層展示資料)

<!-- 雙圓 -->
<template>
  <div style="width: 100%; height: 100%">
    <div :id="id" style="width: 100%; height: 100%"></div>
  </div>
</template>
<script>
import * as echarts from "echarts";
import { fontChart } from "@/utils/echartPxToRem.js";
export default {
  data() {
    return {
      charts: "",
    };
  },
  props: {
    id: {
      type: String,
      required: true, //聲明這個參數是否必須傳入
    },
    value: {
      type: Number,
      default: 14251,
    },
  },
  watch: {
    data() {
      this.drawPie();
    },
  },
  mounted() {
    this.drawPie();
  },
  destroyed() {
    window.removeEventListener("resize", this.selfAdaption);
  },
  methods: {
    drawPie() {
      this.charts = echarts.init(document.getElementById(this.id));
      // 指定圖表的配置項和資料
      var option = {
        title: {
          //   總數樣式
          text: "節能減排",
          subtext: this.value,
          x: "center",
          y: "41%",
          //   節能減排字型
          textStyle: {
            fontWeight: "600",
            fontSize: 18,
            color: "red",
          },
          //   數量樣式
          subtextStyle: {
            fontSize: 20,
            color: "orange",
          },
        },
        // 滑鼠移動到圖上時的提示消息
        tooltip: {
          show: false,
          trigger: "item",
          //   formatter可以為函數,自定義提示資訊
          //   formatter: "{b}:{c}個<br/>占比:({d}%)"
          formatter: function (item) {
            return (
              `<p>  ` +
              item.name +
              ` : 共 ` +
              item.value +
              ` 個 </p> <br>  <p> 占比 ` +
              item.rate +
              ` % </p>`
            );
          },
        },
        series: [
          {
            itemStyle: {
              normal: {},
            },
            type: "pie",
            stillShowZeroSum: true,
            radius: ["60%", "70%"],
            label: {
              normal: {
                position: "inner",
                show: false,
                textStyle: {
                  color: "#fff",
                  fontSize: 12,
                },
              },
            },
            labelLine: {
              normal: {
                show: false,
              },
            },
            data: [
              {
                name: "節能診斷",
                value: this.value,
                itemStyle: {
                  normal: { color: "#13d2ee" },
                },
              },
            ],
          },
          // 内層圓線
          {
            type: "pie",
            hoverAnimation: false, //滑鼠經過的特效
            radius: ["55%", "52%"],
            center: ["50%", "50%"],
            startAngle: 225,
            labelLine: {
              normal: {
                show: false,
              },
            },
            label: {
              normal: {
                position: "center",
              },
            },
            data: [
              {
                value: 1,
                itemStyle: {
                  normal: {
                    color: "rgba(32, 143, 173, 1)",
                  },
                },
              },
            ],
          },
        ],
      };
      // 使用剛指定的配置項和資料顯示圖表。
      this.charts.setOption(option);
      window.addEventListener("resize", this.selfAdaption);
    },
    // 自适應
    selfAdaption() {
      if (!this.charts) return;
      this.charts.resize();
      this.drawPie();
    },
  },
};
</script>