在vue3中,如何优雅的使用echarts之实现大屏项目

news2024/11/30 2:49:18

前置知识 

效果图

使用技术 

Vue3 + Echarts + Gasp

Gasp:是一个 JavaScript动画库,它支持快速开发高性能的 Web 动画。在本项目中,主要是用于做轨迹运动

所需安装的插件
npm i echarts
npm i countup.js 数字滚动特效
npm i gsap javascript动画库
npm i axios
npm i normalize.css 初始化样式文件 
npm i lodash javascript工具函数库    https://www.lodashjs.com/
npm i sass css预处理工具 

适配目标

1920*1080px 设计稿尺寸

目标设备:16:9

前期准备

封装echarts

在大屏中,如果每个图表都去单独写 init 以及进行resize监听机会显得很麻烦,这里我们可以将这些重复的步骤封装成一个hooks,这样在使用的时候就可以统一的进行初始化和进行resize监听。

具体代码如下:

src/hooks/useEcharts.js

import * as echarts from "echarts";
import { ref, onMounted, onUnmounted } from "vue";

export default function useEchart(divEl) {
  // 参数一:DOM容器,参数二:主题色,参数三:渲染方式
  let echartInstance = echarts.init(divEl, null, { renderer: "svg" });

  onUnmounted(() => {
    // 销毁实例
    echartInstance.dispose()
  })

  const setOption = (option) => {
    echartInstance.setOption(option)
  }

  const resizeEchart = () => {
    echartInstance.resize()
  }

  return {
    echartInstance,
    setOption,
    resizeEchart
  }
}

 

封装sclae

这里我们可以新建一个 useScalePage hooks,目的在于随着浏览器视口的变化是,大屏能够随之跟着变化,但是为了限制用户在一定时间内去频繁的改变浏览器视口,因此这里我们可以利用lodash的throttle节流阀进行限制

主要思路:

  • 根据当前设计稿的宽高以及宽高比,再获取当前设备的即浏览器的宽高。
  • 通过当前设备的宽度比上当前设计稿的宽度,从而获取到默认情况下的一个宽高比例
  • 再通过当前设备的宽度比上当前设备的高度,从而获取到当前情况下的一个宽高比例

代码入下:

useScalePage.js

import { onMounted, onUnmounted } from 'vue';
import _ from 'lodash' 

/**
  大屏适配的 hooks
 */
export default function useScalePage(option) {

  const resizeFunc = _.throttle(function() {
    triggerScale() // 动画缩放网页
  }, 100)

  onMounted(()=>{
    triggerScale()  // 动画缩放网页
    window.addEventListener('resize', resizeFunc)
  })

  // 释放资源
  onUnmounted(()=>{
    window.removeEventListener('resize', resizeFunc) // 释放
  })

  // 大屏的适配
  function triggerScale() {
    // 1.设计稿的尺寸
    let targetX = option.targetX ||  1920
    let targetY = option.targetY || 1080
    let targetRatio = option.targetRatio ||  16 / 9 // 宽高比率

    // 2.拿到当前设备(浏览器)的宽度
    let currentX = document.documentElement.clientWidth || document.body.clientWidth
    let currentY = document.documentElement.clientHeight || document.body.clientHeight

    // 3.计算缩放比例
    let scaleRatio = currentX / targetX; // 参照宽度进行缩放 ( 默认情况 )
    let currentRatio = currentX / currentY // 宽高比率

    // 超宽屏
    if(currentRatio > targetRatio) {
      // 4.开始缩放网页
      scaleRatio = currentY / targetY // 参照高度进行缩放
      document.body.style = `width:${targetX}px; height:${targetY}px;transform: scale(${scaleRatio}) translateX(-50%); left: 50%`
    } else {
      // 4.开始缩放网页
      document.body.style = `width:${targetX}px; height:${targetY}px; transform: scale(${scaleRatio})`
    }
  }
}

 

请求方法封装

services/request/config.js  该文件的目的就是存放一些常量配置,比如:请求地址、超时时间等

export const BASE_URL = "http://123.207.32.32:9060/beike/api"
export const TIMEOUT = 10000

services/index.js

import axios from 'axios'
import { BASE_URL, TIMEOUT } from './config'
class LWJRequest {
  constructor(baseURL = BASE_URL, timeout= TIMEOUT) {
    this.instance = axios.create({
      baseURL,
      timeout
    })
  }
  request(config) {
    return new Promise((resolve, reject) => {
      this.instance.request(config).then(res => {
        resolve(res.data)
      }).catch(err => {
        reject(err)
      })
    })
  }
  get(config) {
    return this.request({ ...config, method: "get" })
  }
  post(config) {
    return this.request({ ...config, method: "post" })
  }
}
export default new LWJRequest()


 

图表封装 

这里封装的组件放置在 components 里,具体代码如下所示:

饼图封装

pie-echarts.vue

<template>
  <div :style="{ width: width, height: height }" ref="divRef"></div>
</template>

<script setup>
import { ref, onMounted, watch } from "vue";
import useEchart from "@/hooks/useEchart";

const props = defineProps({
  width: {
    type: String,
    default: "100%",
  },
  height: {
    type: String,
    default: "100%",
  },
  echartsChargingPileData: {
    type: Array,
    default: function () {
      return [];
    },
  },
});

// 监听 echartDatas 的变化
watch(() => props.echartsChargingPileData, (newV, oldV) => {
    setupEchart(newV);
  }
);

// 获取dom元素
let divRef = ref(null);
let myChart = null;

onMounted(() => {
  setupEchart(props.echartsChargingPileData); // 第一次走这里
});

function setupEchart(echartDatas = []) {
  if (!myChart) {
    myChart = useEchart(divRef.value);
  }
  let option = getOption(echartDatas); // 准备数据
  myChart.setOption(option);
}

function getOption(pieDatas = []) {
  let colors = pieDatas.map((item) => {
    return item.color;
  });

  let data = pieDatas.map((item) => {
    return {
      value: item.value,
      name: item.name,
    };
  });

  let total = pieDatas.reduce((a, b) => {
    return a + b.value * 1;
  }, 0);

  const option = {
    colors: colors,
    title: {
      text: `{nameSty| 充电桩总数}\n{numberSty|${total}}`,
      top: "50%",
      left: "30%",
      textStyle: {
        rich: {
          nameSty: {
            fontSize: 19,
            color: "white",
            padding: [10, 0],
          },
          numberSty: {
            fontSize: 24,
            color: "white",
            padding: [4, 0, 0, 20],
          },
        },
      },
    },
    legend: {
      orient: "vertical",
      right: "10%",
      top: "18%",
      itemGap: 20,
      itemWidth: 16,
      itemHeigth: 16,
      icon: "rect",
      // 格式化图例文本
      formatter: function (name) {
        var currentItem = pieDatas.find((item) => item.name === name);
        return (
          "{nameSty|" +
          currentItem.name +
          "}\n" +
          "{numberSty|" +
          currentItem.value +
          "个 }" +
          "{preSty|" +
          currentItem.percentage +
          "}"
        );
      },
      textStyle: {
        rich: {
          nameSty: {
            fontSize: 12,
            color: "#FFFFFF",
            padding: [10, 14],
          },
          numberSty: {
            fontSize: 12,
            color: "#40E6ff",
            padding: [0, 0, 0, 14],
          },
          preSty: {
            fontSize: 12,
            color: "#40E6ff",
          },
        },
      },
    },
    series: [
      {
        type: "pie",
        center: ["40%", "57%"],
        radius: ["30%", "75%"],
        label: {
          show: false,
        },
        data: data,
        roseType: "area",
      },
    ],
  };

  return option;
}
</script>

<style lang="scss" scoped></style>
折线图

line-echarts.js

<template>
  <div ref="divRef" :style="{ width: width, height: height }"></div>
</template>

<script setup>
import { ref, onMounted, watch } from "vue";
import useEchart from "@/hooks/useEchart";

const props = defineProps({
  width: {
    type: String,
    default: "100%",
  },
  height: {
    type: String,
    default: "100%",
  },
  echartsPrecessMonitoringData: {
    type: Array,
    default: function () {
      return [];
    },
  },
});

// 监听 echartDatas 的变化
watch(() => props.echartsPrecessMonitoringData, (newV, oldV) => {
    setupEchart(newV);
  }
);

// 获取dom元素
let divRef = ref(null);
let myChart = null;

onMounted(() => {
  setupEchart(props.echartsPrecessMonitoringData); // 第一次走这里
});

function setupEchart(echartDatas = []) {
  if (!myChart) {
    myChart = useEchart(divRef.value);
  }
  let option = getOption(echartDatas); // 准备数据
  myChart.setOption(option);
}

function getOption(echartDatas = []) {
  let option = {
    // backgroundColor: 'rbg(40,46,72)',
    grid: {
      left: "5%",
      right: "1%",
      top: "20%",
      bottom: "15%",
      containLabel: true, // grid 区域是否包含坐标轴的刻度标签
    },
    legend: {
      right: "center",
      bottom: "5%",
      itemGap: 20,
      itemWidth: 13,
      itemHeigth: 12,
      textStyle: {
        color: "#64BCFF",
      },
      icon: "rect",
    },
    tooltip: {
      trigger: "axis",
      axisPointer: {
        type: "line",
        lineStyle: {
          color: "#20FF89",
        },
      },
    },
    xAxis: [
      {
        type: "category",
        axisLine: {
          show: false,
        },
        axisLabel: {
          color: "#64BCFF",
        },
        splitLine: {
          show: false,
        },
        axisTick: {
          show: false,
        },
        data: [
          "1月",
          "2月",
          "3月",
          "4月",
          "5月",
          "6月",
          "7月",
          "8月",
          "9月",
          "10月",
          "11月",
          "12月",
        ],
      },
    ],
    yAxis: [
      {
        type: "value",
        splitLine: {
          show: false,
        },
        axisLine: {
          show: false,
        },
        axisLabel: {
          show: true,
          color: "#64BCFF",
        },
      },
    ],
    series: [
      {
        name: echartDatas[0].name,
        type: "line",
        smooth: true,
        stack: "总量",
        symbolSize: 5,
        showSymbol: false,
        itemStyle: {
          color: "#20FF89",
          lineStyle: {
            color: "#20FF89",
            width: 2,
          },
        },
        areaStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#20FF89",
              },
              {
                offset: 1,
                color: "rgba(255, 255, 255, 0)",
              },
            ],
          },
        },
        data: echartDatas[0].data,
      },
      {
        name: echartDatas[1].name,
        type: "line",
        smooth: true,
        stack: "总量",
        symbolSize: 5,
        showSymbol: false,
        itemStyle: {
          color: "#EA9502",
          lineStyle: {
            color: "#EA9502",
            width: 2,
          },
        },
        areaStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#EA9502",
              },
              {
                offset: 1,
                color: "rgba(255, 255, 255, 0)",
              },
            ],
          },
        },
        data:  echartDatas[1].data,
      },
    ],
  };
  return option
}
</script>

<style lang="scss" scoped></style>

柱状图

bar-echarts.vue

<template>
  <div ref="divRef" :style="{ width: width, height: height }"></div>
</template>

<script setup>
import { ref, onMounted, watch } from "vue";
import useEchart from "@/hooks/useEchart";

const props = defineProps({
  width: {
    type: String,
    default: "100%",
  },
  height: {
    type: String,
    default: "100%",
  },
  echartsChargingStatisticsData: {
    type: Array,
    default: function () {
      return [];
    },
  },
});

// 监听 echartDatas 的变化
watch(
  () => props.echartsChargingStatisticsData,
  (newV, oldV) => {
    setupEchart(newV);
  }
);

// 获取dom元素
let divRef = ref(null);
let myChart = null;

onMounted(() => {
  setupEchart(props.echartsChargingStatisticsData); // 第一次走这里
});

function setupEchart(echartDatas = []) {
  if (!myChart) {
    myChart = useEchart(divRef.value);
  }
  let option = getOption(echartDatas); // 准备数据
  myChart.setOption(option);
}

function getOption(echartDatas = []) {
  let category = echartDatas.map(item => item.name)
  let categoryData = echartDatas.map(item => item.value)

  const option = {
    // backgroundColor: 'rbg(40,46,72)',
    grid: {
      left: "5%",
      right: "5%",
      top: "30%",
      bottom: "5%",
      containLabel: true, // grid 区域是否包含坐标轴的刻度标签
    },
    tooltip: {},
    xAxis: {
      axisLine: {
        show: true,
        lineStyle: {
          color: "#42A4FF",
        },
      },
      axisTick: {
        show: false,
      },
      axisLabel: {
        color: "white",
      },

      // data: ["一月", "二月", "三月", "四月", "五月", "六月", "七月"],
      data: category
    },
    yAxis: {
      name: "个",
      nameTextStyle: {
        color: "white",
        fontSize: 13,
      },
      axisLine: {
        show: true,
        lineStyle: {
          color: "#42A4FF",
        },
      },
      axisTick: {
        show: false,
      },
      splitLine: {
        show: true,
        lineStyle: {
          color: "#42A4FF",
        },
      },
      axisLabel: {
        color: "white",
      },
    },
    series: [
      {
        name: "销量",
        type: "bar",
        barWidth: 17,
        itemStyle: {
          color: {
            type: "linear",
            x: 0,
            y: 0,
            x2: 0,
            y2: 1,
            colorStops: [
              {
                offset: 0,
                color: "#01B1FF", // 0% 处的颜色
              },
              {
                offset: 1,
                color: "#033BFF", // 100% 处的颜色
              },
            ],
            global: false, // 缺省为 false
          },
        },
        // data: [500, 2000, 3600, 1000, 1000, 2000, 4000],
        data: categoryData
      },
    ],
  };

  return option;
}
</script>

<style lang="scss" scoped></style>

其他区域封装

center-svg.vue
<template>
  <div class="center-svg">
    <svg
      id="dongxiao"
      data-name="dongxiao"
      xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      width="787.781"
      height="652"
      viewBox="0 0 787.781 652"
    >
      <defs>
        <linearGradient
          id="linear-gradient"
          x1="177.891"
          y1="126.344"
          x2="177.891"
          y2="51.375"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#030000" />
          <stop offset="1" stop-color="#fff" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-2"
          x1="177.891"
          y1="110.344"
          x2="177.891"
          y2="44.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-3"
          x1="177.969"
          y1="90.719"
          x2="177.969"
          y2="-0.625"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#26bbff" />
          <stop offset="1" stop-color="#26bbff" stop-opacity="0" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-4"
          x1="177.969"
          y1="91.219"
          x2="177.969"
          y2="46.875"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="-0.055" stop-color="#26bbff" />
          <stop offset="1.044" stop-color="#26bbff" stop-opacity="0" />
          <stop offset="1.055" stop-color="#26bbff" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-5"
          x1="178.109"
          y1="87.563"
          x2="178.109"
          y2="38"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#fff" />
          <stop offset="1" stop-color="#fff" stop-opacity="0" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-6"
          x1="718.89"
          y1="414.344"
          x2="718.89"
          y2="339.375"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-7"
          x1="718.89"
          y1="398.344"
          x2="718.89"
          y2="332.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-8"
          x1="718.969"
          y1="378.719"
          x2="718.969"
          y2="287.375"
          xlink:href="#linear-gradient-3"
        />
        <linearGradient
          id="linear-gradient-9"
          x1="718.968"
          y1="379.219"
          x2="718.968"
          y2="334.875"
          xlink:href="#linear-gradient-4"
        />
        <linearGradient
          id="linear-gradient-10"
          x1="719.109"
          y1="375.562"
          x2="719.109"
          y2="326"
          xlink:href="#linear-gradient-5"
        />
        <linearGradient
          id="linear-gradient-11"
          x1="67.891"
          y1="325.344"
          x2="67.891"
          y2="250.375"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-12"
          x1="67.891"
          y1="309.344"
          x2="67.891"
          y2="243.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-13"
          x1="67.969"
          y1="289.719"
          x2="67.969"
          y2="198.375"
          xlink:href="#linear-gradient-3"
        />
        <linearGradient
          id="linear-gradient-14"
          x1="67.969"
          y1="290.219"
          x2="67.969"
          y2="245.875"
          xlink:href="#linear-gradient-4"
        />
        <linearGradient
          id="linear-gradient-15"
          x1="68.109"
          y1="286.562"
          x2="68.109"
          y2="237"
          xlink:href="#linear-gradient-5"
        />
        <linearGradient
          id="linear-gradient-16"
          x1="159.891"
          y1="596.344"
          x2="159.891"
          y2="521.375"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-17"
          x1="159.891"
          y1="580.344"
          x2="159.891"
          y2="514.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-18"
          x1="159.969"
          y1="560.719"
          x2="159.969"
          y2="469.375"
          xlink:href="#linear-gradient-3"
        />
        <linearGradient
          id="linear-gradient-19"
          x1="159.969"
          y1="561.219"
          x2="159.969"
          y2="516.875"
          xlink:href="#linear-gradient-4"
        />
        <linearGradient
          id="linear-gradient-20"
          x1="160.109"
          y1="557.562"
          x2="160.109"
          y2="508"
          xlink:href="#linear-gradient-5"
        />
        <linearGradient
          id="linear-gradient-21"
          x1="581.89"
          y1="623.344"
          x2="581.89"
          y2="548.375"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-22"
          x1="581.89"
          y1="607.344"
          x2="581.89"
          y2="541.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-23"
          x1="581.969"
          y1="587.719"
          x2="581.969"
          y2="496.375"
          xlink:href="#linear-gradient-3"
        />
        <linearGradient
          id="linear-gradient-24"
          x1="581.968"
          y1="588.219"
          x2="581.968"
          y2="543.875"
          xlink:href="#linear-gradient-4"
        />
        <linearGradient
          id="linear-gradient-25"
          x1="582.109"
          y1="584.562"
          x2="582.109"
          y2="535"
          xlink:href="#linear-gradient-5"
        />
        <linearGradient
          id="linear-gradient-26"
          x1="633.89"
          y1="145.344"
          x2="633.89"
          y2="70.375"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-27"
          x1="633.89"
          y1="129.344"
          x2="633.89"
          y2="63.719"
          xlink:href="#linear-gradient"
        />
        <linearGradient
          id="linear-gradient-28"
          x1="633.969"
          y1="109.719"
          x2="633.969"
          y2="18.375"
          xlink:href="#linear-gradient-3"
        />
        <linearGradient
          id="linear-gradient-29"
          x1="633.968"
          y1="110.219"
          x2="633.968"
          y2="65.875"
          xlink:href="#linear-gradient-4"
        />
        <linearGradient
          id="linear-gradient-30"
          x1="634.109"
          y1="106.562"
          x2="634.109"
          y2="57"
          xlink:href="#linear-gradient-5"
        />
        <linearGradient
          id="linear-gradient-31"
          x1="273.219"
          y1="315.25"
          x2="524.25"
          y2="443.156"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#fbab00" />
          <stop offset="0.342" stop-color="#f98100" />
          <stop offset="0.75" stop-color="#f86d00" stop-opacity="0" />
          <stop offset="1" stop-color="#f86000" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-32"
          x1="565.875"
          y1="379.485"
          x2="235.062"
          y2="379.485"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#1b90ff" />
          <stop offset="0.745" stop-color="#0752ff" stop-opacity="0" />
          <stop offset="1" stop-color="#003dff" />
        </linearGradient>
        <linearGradient
          id="linear-gradient-33"
          x1="628.344"
          y1="445.411"
          x2="169.406"
          y2="278.371"
          gradientUnits="userSpaceOnUse"
        >
          <stop offset="0" stop-color="#fbab00" />
          <stop offset="0.342" stop-color="#f98100" />
          <stop offset="1" stop-color="#f86000" stop-opacity="0" />
        </linearGradient>
        <filter
          id="filter"
          x="291"
          y="214"
          width="61"
          height="60"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-2"
          x="258"
          y="155"
          width="61"
          height="61"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <!-- 蓝色点滤镜效果 -->
        <filter id="blue-filter-2" filterUnits="userSpaceOnUse">
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <!-- 橙色点滤镜特效 -->
        <filter id="orange-filter-2" filterUnits="userSpaceOnUse">
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#f97a00" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-3"
          x="126"
          y="296"
          width="61"
          height="61"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-4"
          x="255"
          y="269"
          width="61"
          height="61"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-5"
          x="226"
          y="510"
          width="60"
          height="61"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-6"
          x="256"
          y="452"
          width="60"
          height="60"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-7"
          x="257"
          y="407"
          width="61"
          height="61"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-8"
          x="247"
          y="83"
          width="59"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-9"
          x="262"
          y="122"
          width="60"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-10"
          x="278"
          y="199"
          width="59"
          height="60"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-11"
          x="112"
          y="294"
          width="59"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-12"
          x="206"
          y="274"
          width="59"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-13"
          x="255"
          y="436"
          width="59"
          height="60"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-14"
          x="238"
          y="500"
          width="59"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <filter
          id="filter-15"
          x="279"
          y="364"
          width="59"
          height="59"
          filterUnits="userSpaceOnUse"
        >
          <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
          <feComposite result="composite" />
          <feComposite result="composite-2" />
          <feComposite result="composite-3" />
          <feFlood result="flood" flood-color="#1783ff" flood-opacity="0.9" />
          <feComposite result="composite-4" operator="in" in2="composite-3" />
          <feBlend result="blend" in2="SourceGraphic" />
          <feBlend result="blend-2" in="SourceGraphic" />
        </filter>
        <image
          id="image"
          width="23"
          height="23"
          xlink:href="../assets/images/center/image-1.png"
        />
        <image
          id="image-2"
          width="15"
          height="15"
          xlink:href="../assets/images/center/image-2.png"
        />
      </defs>
      <g id="dongxiao1">
        <g id="icon">
          <path
            id="base_o"
            class="cls-1"
            d="M177.9,51.386c33.127,0,63.736,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948s-70.671-19.711-67.113-40.948C114.162,65.225,144.771,51.386,177.9,51.386Z"
          />
          <path
            id="base_t"
            class="cls-2"
            d="M177.9,44.728c33.127,0,63.736,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844s-70.671-17.254-67.113-35.844C114.162,56.842,144.771,44.728,177.9,44.728Z"
          />
          <path
            id="right_o"
            class="cls-3"
            d="M122.484-.626H233.455L208.447,90.7H147.491Z"
          />
          <path
            id="right_t"
            class="cls-4"
            d="M112.064,46.866H243.875l-35.428,44.36H147.491Z"
          />
          <path
            id="line"
            class="cls-5"
            d="M178.105,38c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.087-28.5C126.527,46.072,152.194,38,178.105,38Z"
          />
          <image
            id="icon_star_guangzhou"
            x="121"
            y="-3"
            width="112"
            height="112"
            xlink:href="../assets/images/center/star.png"
          />
          <path
            id="base_o-2"
            data-name="base_o"
            class="cls-6"
            d="M718.9,339.386c33.127,0,63.736,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948s-70.671-19.711-67.113-40.948C655.162,353.225,685.771,339.386,718.9,339.386Z"
          />
          <path
            id="base_t-2"
            data-name="base_t"
            class="cls-7"
            d="M718.9,332.728c33.127,0,63.736,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844s-70.671-17.254-67.113-35.844C655.162,344.842,685.771,332.728,718.9,332.728Z"
          />
          <path
            id="right_o-2"
            data-name="right_o"
            class="cls-8"
            d="M663.484,287.374H774.455L749.447,378.7H688.491Z"
          />
          <path
            id="right_t-2"
            data-name="right_t"
            class="cls-9"
            d="M653.064,334.866H784.875l-35.428,44.36H688.491Z"
          />
          <path
            id="line-2"
            data-name="line"
            class="cls-10"
            d="M719.105,326c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.087-28.5C667.527,334.072,693.194,326,719.105,326Z"
          />
          <image
            id="icon_location_zhongshan"
            x="666"
            y="287"
            width="106"
            height="107"
            xlink:href="../assets/images/center/location.png"
          />
          <path
            id="base_o-3"
            data-name="base_o"
            class="cls-11"
            d="M67.9,250.386c33.127,0,63.736,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948S-2.773,305.62.785,284.383C4.162,264.225,34.771,250.386,67.9,250.386Z"
          />
          <path
            id="base_t-3"
            data-name="base_t"
            class="cls-12"
            d="M67.9,243.728c33.127,0,63.736,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844S-2.773,292.078.785,273.488C4.162,255.842,34.771,243.728,67.9,243.728Z"
          />
          <path
            id="right_o-3"
            data-name="right_o"
            class="cls-13"
            d="M12.484,198.374H123.455L98.447,289.7H37.491Z"
          />
          <path
            id="right_t-3"
            data-name="right_t"
            class="cls-14"
            d="M2.064,245.866H133.875l-35.428,44.36H37.491Z"
          />
          <path
            id="line-3"
            data-name="line"
            class="cls-15"
            d="M68.105,237c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.088-28.5C16.527,245.072,42.194,237,68.105,237Z"
          />
          <image
            id="icon_pie_shenzhen"
            x="13"
            y="194"
            width="107"
            height="108"
            xlink:href="../assets/images/center/pie.png"
          />
          <path
            id="base_o-4"
            data-name="base_o"
            class="cls-16"
            d="M159.9,521.386c33.127,0,63.736,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948S89.227,576.62,92.785,555.383C96.162,535.225,126.771,521.386,159.9,521.386Z"
          />
          <path
            id="base_t-4"
            data-name="base_t"
            class="cls-17"
            d="M159.9,514.728c33.127,0,63.736,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844s-70.671-17.254-67.113-35.844C96.162,526.842,126.771,514.728,159.9,514.728Z"
          />
          <path
            id="right_o-4"
            data-name="right_o"
            class="cls-18"
            d="M104.484,469.374H215.455L190.447,560.7H129.491Z"
          />
          <path
            id="right_t-4"
            data-name="right_t"
            class="cls-19"
            d="M94.064,516.866H225.875l-35.428,44.36H129.491Z"
          />
          <path
            id="line-4"
            data-name="line"
            class="cls-20"
            d="M160.1,508c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.088-28.5C108.527,516.072,134.194,508,160.1,508Z"
          />
          <image
            id="icon_earth_dongguan"
            x="106"
            y="460"
            width="111"
            height="112"
            xlink:href="../assets/images/center/earth.png"
          />
          <path
            id="base_o-5"
            data-name="base_o"
            class="cls-21"
            d="M581.9,548.386c33.127,0,63.736,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948s-70.671-19.711-67.113-40.948C518.162,562.225,548.771,548.386,581.9,548.386Z"
          />
          <path
            id="base_t-5"
            data-name="base_t"
            class="cls-22"
            d="M581.9,541.728c33.127,0,63.736,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844s-70.671-17.254-67.113-35.844C518.162,553.842,548.771,541.728,581.9,541.728Z"
          />
          <path
            id="right_o-5"
            data-name="right_o"
            class="cls-23"
            d="M526.484,496.374H637.455L612.447,587.7H551.491Z"
          />
          <path
            id="right_t-5"
            data-name="right_t"
            class="cls-24"
            d="M516.064,543.866H647.875l-35.428,44.36H551.491Z"
          />
          <path
            id="line-5"
            data-name="line"
            class="cls-25"
            d="M582.105,535c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.087-28.5C530.527,543.072,556.194,535,582.105,535Z"
          />
          <image
            id="icon_hot_zhuhai"
            x="530"
            y="495"
            width="105"
            height="105"
            xlink:href="../assets/images/center/hot.png"
          />
          <path
            id="base_o-6"
            data-name="base_o"
            class="cls-26"
            d="M633.9,70.386c33.127,0,63.737,13.839,67.114,34,3.557,21.237-26.042,40.948-67.114,40.948s-70.671-19.711-67.113-40.948C570.162,84.225,600.771,70.386,633.9,70.386Z"
          />
          <path
            id="base_t-6"
            data-name="base_t"
            class="cls-27"
            d="M633.9,63.728c33.127,0,63.737,12.114,67.114,29.76,3.557,18.59-26.042,35.844-67.114,35.844s-70.671-17.254-67.113-35.844C570.162,75.842,600.771,63.728,633.9,63.728Z"
          />
          <path
            id="right_o-6"
            data-name="right_o"
            class="cls-28"
            d="M578.484,18.374H689.455L664.447,109.7H603.491Z"
          />
          <path
            id="right_t-6"
            data-name="right_t"
            class="cls-29"
            d="M568.064,65.866H699.875l-35.428,44.36H603.491Z"
          />
          <path
            id="line-6"
            data-name="line"
            class="cls-30"
            d="M634.105,57c25.911,0,51.58,8.076,56.087,21.076,4.881,14.075-19.143,28.5-56.083,28.5s-60.966-14.426-56.087-28.5C582.527,65.072,608.194,57,634.105,57Z"
          />
          <image
            id="icon_home_foshan"
            x="582"
            y="17"
            width="103"
            height="103"
            xlink:href="../assets/images/center/home.png"
          />
        </g>
        <g id="circle">
          <path
            id="white"
            class="cls-31"
            d="M414.057,225.629c96.638,0,183.089,41.729,194.7,100.322C621.763,391.613,533.734,450,417.167,450c-116.623,0-206.208-58.413-195.026-124.044C232.116,267.4,317.339,225.629,414.057,225.629Z"
          />
          <path
            id="orange_2"
            class="cls-32"
            d="M397.666,306.9c63.817,0,120.7,28.166,126.181,67.065,5.811,41.258-50.026,77.528-124.026,77.528-74.041,0-130.915-36.294-126.336-77.528C277.8,335.086,333.819,306.9,397.666,306.9Z"
          />
          <path
            id="blue"
            class="cls-33"
            d="M399.082,284.02c82.745,0,156.818,36,165.9,86.375,10,55.486-64.543,104.544-163.1,104.544-98.607,0-174.539-49.087-166.169-104.544C243.317,320.056,316.275,284.02,399.082,284.02Z"
          />
          <path
            id="orange_1"
            class="cls-34"
            d="M396.909,228.952c111.911,0,212.629,47.606,229.169,115.472,19.27,79.068-85.494,150.425-225.217,150.425-139.785,0-246.6-71.391-229.688-150.425C185.681,276.624,284.878,228.952,396.909,228.952Z"
          />
          <image
            id="light_o_3"
            x="520"
            y="324"
            width="72"
            height="92"
            xlink:href="../assets/images/center/light_0_3.png"
          />
          <image
            id="light_o_2"
            x="141"
            y="261"
            width="99"
            height="137"
            xlink:href="../assets/images/center/light_0_2.png"
          />
          <image
            id="light_o_1"
            x="436"
            y="385"
            width="99"
            height="66"
            xlink:href="../assets/images/center/light_0_1.png"
          />
        </g>
        <image
          id="center-houes"
          x="103"
          y="120"
          width="539"
          height="419"
          xlink:href="../assets/images/center/house.png"
        />
        <g id="right">
          <g id="组_380" data-name="组 380">
            <path
              id="line_b_3"
              class="cls-35"
              d="M254.251,93.032s47.68,24.959,35.63,74.5,43.548,85.6,43.548,85.6"
            />
            <path
              id="line_b_2"
              class="cls-35"
              d="M122.023,310.584s19.568,33.927,95.014-1.189,85.512,3.963,85.512,3.963"
            />
            <path
              id="line_b_1"
              class="cls-35"
              d="M236.832,551.912s54.438-25.6,47.506-82.424,34.839-88.368,34.839-88.368"
            />
            <path
              id="line_o_3"
              class="cls-36"
              d="M558.295,108.09s-49.1,12.55-54.633,70.932-39.193,77.669-39.193,77.669"
            />

            <path
              id="line_o_2"
              class="cls-36"
              d="M653,387s-39.424,20.259-79-30-70.444-42.177-79-40"
            />
            <path
              id="line_o_1"
              class="cls-36"
              d="M505,579s-31.47-27.546-17-86c12.415-50.151-42-83-42-83"
            />

            <g id="dots_b">
              <circle
                id="dots39"
                class="cls-37"
                cx="256.219"
                cy="94.234"
                r="2.781"
              />
              <path
                id="dots38"
                class="cls-38"
                d="M331.062,248.777a2.766,2.766,0,1,1-2.765,2.766A2.766,2.766,0,0,1,331.062,248.777Z"
              />
              <path
                id="dots37"
                class="cls-38"
                d="M120.828,306.621a2.781,2.781,0,1,1-2.781,2.781A2.781,2.781,0,0,1,120.828,306.621Z"
              />
              <circle
                id="dots36"
                class="cls-39"
                cx="300.938"
                cy="311.781"
                r="2.781"
              />
              <circle
                id="dots35"
                class="cls-37"
                cx="236.422"
                cy="552.313"
                r="2.766"
              />
              <path
                id="dots34"
                class="cls-38"
                d="M318.391,380.34a2.766,2.766,0,1,1-2.782,2.765A2.773,2.773,0,0,1,318.391,380.34Z"
              />
              <image
                id="dots28"
                x="255"
                y="91"
                width="22"
                height="23"
                xlink:href="../assets/images/center/dots28.png"
              />
              <circle
                id="dots27"
                class="cls-40"
                cx="321.25"
                cy="243.625"
                r="2.375"
              />
              <circle
                id="dots26"
                class="cls-41"
                cx="288"
                cy="184.969"
                r="2.375"
              />
              <circle
                id="dots25"
                class="cls-42"
                cx="155.766"
                cy="326.047"
                r="2.359"
              />
              <circle
                id="dots24"
                class="cls-43"
                cx="284.828"
                cy="298.703"
                r="2.359"
              />
              <path
                id="dots23"
                class="cls-44"
                d="M255.547,537.652a2.375,2.375,0,1,1-2.375,2.375A2.375,2.375,0,0,1,255.547,537.652Z"
              />
              <path
                id="dots22"
                class="cls-45"
                d="M285.641,479a2.375,2.375,0,1,1-2.375,2.375A2.375,2.375,0,0,1,285.641,479Z"
              />
              <circle
                id="dots21"
                class="cls-46"
                cx="286.813"
                cy="436.985"
                r="2.375"
              />
              <circle
                id="dots20"
                class="cls-47"
                cx="276.125"
                cy="112.078"
                r="1.594"
              />
              <path
                id="dots19"
                class="cls-48"
                d="M291.578,149.308a1.579,1.579,0,1,1-1.594,1.578A1.587,1.587,0,0,1,291.578,149.308Z"
              />
              <path
                id="dots18"
                class="cls-49"
                d="M306.625,226.965a1.594,1.594,0,1,1-1.578,1.593A1.585,1.585,0,0,1,306.625,226.965Z"
              />
              <circle
                id="dots17"
                class="cls-50"
                cx="140.734"
                cy="323.266"
                r="1.578"
              />
              <circle
                id="dots16"
                class="cls-51"
                cx="234.938"
                cy="302.672"
                r="1.594"
              />
              <circle
                id="dots15"
                class="cls-52"
                cx="284.422"
                cy="465.516"
                r="1.578"
              />
              <circle
                id="dots14"
                class="cls-53"
                cx="266.625"
                cy="528.938"
                r="1.594"
              />
              <circle
                id="dots13"
                class="cls-54"
                cx="307.797"
                cy="393.016"
                r="1.578"
              />
            </g>
            <g id="dots_o">
              <path
                id="dots33"
                class="cls-55"
                d="M555.906,103.715a2.766,2.766,0,1,1-2.765,2.765A2.765,2.765,0,0,1,555.906,103.715Z"
              />
              <path
                id="dots32"
                class="cls-55"
                d="M466.437,252.34a2.766,2.766,0,1,1-2.765,2.765A2.765,2.765,0,0,1,466.437,252.34Z"
              />
              <circle
                id="dots31"
                class="cls-56"
                cx="652.093"
                cy="385.89"
                r="2.781"
              />
              <path
                id="dots30"
                class="cls-55"
                d="M494.937,312.558a2.766,2.766,0,1,1-2.765,2.766A2.766,2.766,0,0,1,494.937,312.558Z"
              />
              <circle
                id="dots29"
                class="cls-56"
                cx="505.625"
                cy="576.485"
                r="2.781"
              />
              <circle
                id="dots29-2"
                data-name="dots29"
                class="cls-56"
                cx="446.25"
                cy="409.64"
                r="2.781"
              />
              <use id="dots12" x="516" y="118" xlink:href="#image" />
              <use id="dots11" x="505" y="304" xlink:href="#image" />
              <use id="dots10" x="572" y="353" xlink:href="#image" />
              <use id="dots09" x="486" y="555" xlink:href="#image" />
              <use id="dots08" x="479" y="465" xlink:href="#image" />
              <use id="dots07" x="499" y="158" xlink:href="#image-2" />
              <use id="dots06" x="483" y="221" xlink:href="#image-2" />
              <use id="dots05" x="470" y="239" xlink:href="#image-2" />
              <use id="dots04" x="632" y="383" xlink:href="#image-2" />
              <use id="dots03" x="564" y="346" xlink:href="#image-2" />
              <use id="dots02" x="455" y="415" xlink:href="#image-2" />
              <use id="dots01" x="482" y="455" xlink:href="#image-2" />
            </g>

            <!-- 开始给点制作动画特效 1个点-->
            <!-- <circle cx="0" xy="0" r="3" class="cus-cls-blue"> -->
            <!-- 做动画 -->
            <!-- <animateMotion
              dur="6s"
              begin="0s"
              repeatCount="indefinite"
              rotate="auto"
            > -->
            <!-- 沿着哪条线/路径进行 -->
            <!-- <mpath href="#line_b_3"></mpath>
            </animateMotion>
          </circle> -->
            <!-- n个点--左侧 -->
            <template v-for="item in [1, 2, 3]">
              <circle cx="0" xy="0" r="3" class="cus-cls-blue">
                <!-- 做动画 -->
                <animateMotion
                  dur="6s"
                  begin="0s"
                  repeatCount="indefinite"
                  rotate="auto"
                >
                  <!-- 沿着哪条线/路径进行 -->
                  <mpath :href="`#line_b_${item}`"></mpath>
                </animateMotion>
              </circle>
              <circle cx="0" xy="0" r="3" class="cus-cls-blue">
                <!-- 做动画 -->
                <animateMotion
                  dur="6s"
                  begin="-3s"
                  repeatCount="indefinite"
                  rotate="auto"
                >
                  <!-- 沿着哪条线/路径进行 -->
                  <mpath :href="`#line_b_${item}`"></mpath>
                </animateMotion>
              </circle>
            </template>
            <!-- n个点--右侧 -->
            <template v-for="item in [1, 2, 3]">
              <circle cx="0" xy="0" r="3" class="cus-cls-orange">
                <!-- 做动画 -->
                <animateMotion
                  dur="6s"
                  begin="0s"
                  repeatCount="indefinite"
                  rotate="auto"
                >
                  <!-- 沿着哪条线/路径进行 -->
                  <mpath :href="`#line_o_${item}`"></mpath>
                </animateMotion>
              </circle>
              <circle cx="0" xy="0" r="3" class="cus-cls-orange">
                <!-- 做动画 -->
                <animateMotion
                  dur="6s"
                  begin="-3s"
                  repeatCount="indefinite"
                  rotate="auto"
                >
                  <!-- 沿着哪条线/路径进行 -->
                  <mpath :href="`#line_o_${item}`"></mpath>
                </animateMotion>
              </circle>
            </template>
          </g>
        </g>
        <g id="text">
          <text
            id="text6"
            class="cls-57"
            transform="translate(179.049 154.026) scale(0.481 0.481)"
          >
            广州
          </text>
          <text
            id="text5"
            class="cls-57"
            transform="translate(63.771 351.7) scale(0.481 0.481)"
          >
            深圳
          </text>
          <text
            id="text4"
            class="cls-57"
            transform="translate(161.75 622.618) scale(0.481 0.481)"
          >
            东莞
          </text>
          <text
            id="text3"
            class="cls-57"
            transform="translate(583.87 649.734) scale(0.481 0.481)"
          >
            珠海
          </text>
          <text
            id="text2"
            class="cls-57"
            transform="translate(719.835 441.012) scale(0.481 0.481)"
          >
            中山
          </text>
          <text
            id="text1"
            class="cls-57"
            transform="translate(637.843 173.315) scale(0.481 0.481)"
          >
            佛山
          </text>
        </g>
      </g>
    </svg>

    <!-- 烟花特效 -->
    <img class="lingxA" src="../assets/images/ling/lingxA.png" />
    <img class="lingxB" src="../assets/images/ling/lingxB.png" />
    <img class="lingxC" src="../assets/images/ling/lingxC.png" />
    <img class="lingxD" src="../assets/images/ling/lingxD.png" />
    <img class="lingxE" src="../assets/images/ling/lingxE.png" />
    <img class="lingxF" src="../assets/images/ling/lingxF.png" />
  </div>
</template>

<script setup>
import gsap from "gsap";
import { onMounted } from "vue";

onMounted(() => {
  let timeline = gsap.timeline({});

  timeline
    .fromTo(
      "#dongxiao",
      {
        duration: 1,
        scale: 0.8,
        y: 40,
      },
      {
        duration: 1,
        scale: 1,
        y: 0,
      }
    )
    .fromTo(
      ["#center-houes"],
      {
        duration: 1,
        opacity: 0.5,
        scale: 0.7,
        transformOrigin: "bottom",
        y: 20,
      },
      {
        duration: 1,
        opacity: 1,
        scale: 1,
        transformOrigin: "bottom",
        y: 0,
      },
      "-=1"
    );
});
</script>

<style lang="scss" scoped>
.cls-1,
.cls-11,
.cls-12,
.cls-16,
.cls-17,
.cls-2,
.cls-21,
.cls-22,
.cls-26,
.cls-27,
.cls-31,
.cls-6,
.cls-7 {
  stroke: #fff;
}

.cls-1,
.cls-10,
.cls-11,
.cls-12,
.cls-15,
.cls-16,
.cls-17,
.cls-2,
.cls-20,
.cls-21,
.cls-22,
.cls-25,
.cls-26,
.cls-27,
.cls-30,
.cls-31,
.cls-5,
.cls-6,
.cls-7 {
  stroke-width: 3px;
}

.cls-1,
.cls-10,
.cls-11,
.cls-12,
.cls-13,
.cls-14,
.cls-15,
.cls-16,
.cls-17,
.cls-18,
.cls-19,
.cls-2,
.cls-20,
.cls-21,
.cls-22,
.cls-23,
.cls-24,
.cls-25,
.cls-26,
.cls-27,
.cls-28,
.cls-29,
.cls-3,
.cls-30,
.cls-31,
.cls-32,
.cls-33,
.cls-34,
.cls-35,
.cls-36,
.cls-38,
.cls-4,
.cls-44,
.cls-45,
.cls-48,
.cls-49,
.cls-5,
.cls-55,
.cls-6,
.cls-7,
.cls-8,
.cls-9 {
  fill-rule: evenodd;
}

.cls-1,
.cls-11,
.cls-16,
.cls-21,
.cls-26,
.cls-6 {
  opacity: 0.15;
}

.cls-1 {
  fill: url(#linear-gradient);
}

.cls-12,
.cls-13,
.cls-14,
.cls-17,
.cls-18,
.cls-19,
.cls-2,
.cls-22,
.cls-23,
.cls-24,
.cls-27,
.cls-28,
.cls-29,
.cls-3,
.cls-4,
.cls-7,
.cls-8,
.cls-9 {
  opacity: 0.3;
}

.cls-2 {
  fill: url(#linear-gradient-2);
}

.cls-3 {
  fill: url(#linear-gradient-3);
}

.cls-4 {
  fill: url(#linear-gradient-4);
}

.cls-10,
.cls-15,
.cls-20,
.cls-25,
.cls-30,
.cls-31,
.cls-32,
.cls-33,
.cls-34,
.cls-35,
.cls-36,
.cls-5 {
  fill: none;
}

.cls-10,
.cls-15,
.cls-20,
.cls-25,
.cls-30,
.cls-5 {
  stroke-dasharray: 12 12;
  opacity: 0.7;
}

.cls-5 {
  stroke: url(#linear-gradient-5);
}

.cls-6 {
  fill: url(#linear-gradient-6);
}

.cls-7 {
  fill: url(#linear-gradient-7);
}

.cls-8 {
  fill: url(#linear-gradient-8);
}

.cls-9 {
  fill: url(#linear-gradient-9);
}

.cls-10 {
  stroke: url(#linear-gradient-10);
}

.cls-11 {
  fill: url(#linear-gradient-11);
}

.cls-12 {
  fill: url(#linear-gradient-12);
}

.cls-13 {
  fill: url(#linear-gradient-13);
}

.cls-14 {
  fill: url(#linear-gradient-14);
}

.cls-15 {
  stroke: url(#linear-gradient-15);
}

.cls-16 {
  fill: url(#linear-gradient-16);
}

.cls-17 {
  fill: url(#linear-gradient-17);
}

.cls-18 {
  fill: url(#linear-gradient-18);
}

.cls-19 {
  fill: url(#linear-gradient-19);
}

.cls-20 {
  stroke: url(#linear-gradient-20);
}

.cls-21 {
  fill: url(#linear-gradient-21);
}

.cls-22 {
  fill: url(#linear-gradient-22);
}

.cls-23 {
  fill: url(#linear-gradient-23);
}

.cls-24 {
  fill: url(#linear-gradient-24);
}

.cls-25 {
  stroke: url(#linear-gradient-25);
}

.cls-26 {
  fill: url(#linear-gradient-26);
}

.cls-27 {
  fill: url(#linear-gradient-27);
}

.cls-28 {
  fill: url(#linear-gradient-28);
}

.cls-29 {
  fill: url(#linear-gradient-29);
}

.cls-30 {
  stroke: url(#linear-gradient-30);
}

.cls-31 {
  opacity: 0.1;
}

.cls-32,
.cls-33,
.cls-34 {
  stroke-width: 4px;
}

.cls-32 {
  stroke: url(#linear-gradient-31);
}

.cls-33 {
  stroke: url(#linear-gradient-32);
}

.cls-34 {
  stroke: url(#linear-gradient-33);
}

.cls-35 {
  stroke: #1374f1;
}

.cls-35,
.cls-36 {
  stroke-width: 2px;
  opacity: 0.8;
}

.cls-36 {
  stroke: #f97a00;
}

.cls-37,
.cls-38,
.cls-39 {
  fill: #0e68ff;
}

.cls-39 {
  opacity: 0.36;
}

.cls-40,
.cls-41,
.cls-42,
.cls-43,
.cls-44,
.cls-45,
.cls-46,
.cls-47,
.cls-48,
.cls-49,
.cls-50,
.cls-51,
.cls-52,
.cls-53,
.cls-54 {
  fill: #fff;
  opacity: 0.9;
}

/* 编写点的样式--蓝色 */
.cus-cls-blue {
  fill: #fff;
  opacity: 0.9;
  filter: url(#blue-filter-2);
  /* 告诉浏览器该元素将会改变哪些属性,让浏览器提前做好优化的准备( 比较消耗内存 ) */
  will-change: opacity;
}
/* 编写点的样式--橙色 */
.cus-cls-orange {
  fill: #fff;
  opacity: 0.9;
  filter: url(#orange-filter-2);
  /* 告诉浏览器该元素将会改变哪些属性,让浏览器提前做好优化的准备( 比较消耗内存 ) */
  will-change: opacity;
}

.cls-40 {
  filter: url(#filter);
}

.cls-41 {
  filter: url(#filter-2);
}

.cls-42 {
  filter: url(#filter-3);
}

.cls-43 {
  filter: url(#filter-4);
}

.cls-44 {
  filter: url(#filter-5);
}

.cls-45 {
  filter: url(#filter-6);
}

.cls-46 {
  filter: url(#filter-7);
}

.cls-47 {
  filter: url(#filter-8);
}

.cls-48 {
  filter: url(#filter-9);
}

.cls-49 {
  filter: url(#filter-10);
}

.cls-50 {
  filter: url(#filter-11);
}

.cls-51 {
  filter: url(#filter-12);
}

.cls-52 {
  filter: url(#filter-13);
}

.cls-53 {
  filter: url(#filter-14);
}

.cls-54 {
  filter: url(#filter-15);
}

.cls-55,
.cls-56 {
  fill: #f97a00;
}

.cls-57 {
  font-size: 41.603px;
  fill: #338ed5;
  text-anchor: middle;
  font-family: "Source Han Sans CN";
}

/* ICON 悬浮动画特效--开始 */
#icon_star_guangzhou {
  animation: updown 2.2s ease-in infinite;
}

#icon_home_foshan {
  animation: updown 1.9s ease-in infinite;
}

#icon_location_zhongshan {
  animation: updown 2s ease-in infinite;
}

#icon_hot_zhuhai {
  animation: updown 2s ease-in infinite;
}

#icon_earth_dongguan {
  animation: updown 1.7s ease-in infinite;
}

#icon_pie_shenzhen {
  animation: updown 1.7s ease-in infinite;
}

@keyframes updown {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-10px);
  }
}
/* ICON 悬浮动画特效--结束 */

/*=======城市的灯光效果========*/
/* 广州 */
.center .cls-3,
.center .cls-4,

/* 佛山 */
.center .cls-28,
.center .cls-29,

/* 中山 */
.center .cls-8,
.center .cls-9,

/* 珠海 */
.center .cls-23,
.center .cls-24,

/* 东莞 */
.center .cls-18,
.center .cls-19,

/* 深圳 */
.center .cls-13,
.center .cls-14 {
  animation: light-beam 3s linear infinite;
  opacity: 0.2;
}

@keyframes light-beam {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
/*=======城市的灯光效果========*/

/* 烟花特效--开始 */
.center-svg {
  position: relative;
}
.center-svg .lingxA {
  position: absolute;
  top: 30%;
  left: 47%;
}
.center-svg .lingxB {
  position: absolute;
  top: 35%;
  left: 58%;
}
.center-svg .lingxC {
  position: absolute;
  top: 40%;
  left: 40%;
}
.center-svg .lingxD {
  position: absolute;
  top: 28%;
  left: 41%;
}
.center-svg .lingxE {
  position: absolute;
  top: 28%;
  left: 54%;
}
.center-svg .lingxF {
  position: absolute;
  top: 40%;
  left: 53%;
}
.center-svg .lingxA {
  opacity: 0;
  animation: lingxA 2s linear infinite;
}
.center-svg .lingxB {
  opacity: 0;
  animation: lingxB 2.2s linear infinite;
}
.center-svg .lingxC {
  opacity: 0;
  animation: lingxC 1.7s linear infinite;
}
.center-svg .lingxD {
  opacity: 0;
  animation: lingxC 2.7s linear infinite;
}
.center-svg .lingxE {
  opacity: 0;
  animation: lingxB 1.2s linear infinite;
}
.center-svg .lingxF {
  opacity: 0;
  animation: lingxA 1.4s linear infinite;
}
/* 向上移动的烟花 */
@keyframes lingxA {
  from {
    transform: translateY(0px);
    opacity: 1;
  }
  60% {
    opacity: 1;
  }
  to {
    transform: translateY(-160px);
    opacity: 0;
  }
}

@keyframes lingxB {
  from {
    transform: translateY(0px);
    opacity: 1;
  }

  40% {
    opacity: 1;
  }

  60%,
  to {
    transform: translateY(-120px);
    opacity: 0;
  }
}

@keyframes lingxC {
  from {
    transform: translateY(0px);
    opacity: 1;
  }

  30% {
    opacity: 1;
  }

  50%,
  to {
    transform: translateY(-90px);
    opacity: 0;
  }
}
/* 烟花特效--结束 */
</style>

bottom-panle.vue
<template>
  <div class="bottom-content">
    <template v-for="(item, index) in panelItems" :key="item">
      <div :class="['item', `panel${item.id}`]">
        <div class="pan-left">
          <div class="title">{{ item.title }}</div>
          <!-- 数据动画 -->
          <span :id="`total-num-${item.id}`" class="number">
            {{
            item.totalNum
          }}
          </span>
          <span class="unit">{{ item.unit }}</span>
        </div>
        <div class="pan-right">
          <span
            :class="['triangle', item.isUp ? 'up-triangle' : 'down-triangle']"
          ></span>
          <!-- 数据动画 -->
          <span :id="`percentage-num-${item.id}`" class="percentage">{{
            item.percentage
          }}</span>
        </div>
      </div>
    </template>
  </div>
</template>

<script setup>
import { watch, nextTick } from "vue";
import { CountUp } from "countup.js";

const props = defineProps({
  panelItems: {
    type: Array,
    default: function () {
      return [];
    },
  },
});

watch(() => props.panelItems, (newV, oldV) => {
  // 在下一次DOM更新完成之后会回调
  nextTick(() => {
    startAnimation(newV)
  })
})

// 数字滚动动画函数
function startAnimation(panelItems = []) {
  let option1 = {
    decimalPlaces: 1, // 保留两位小数
    duration: 1.5, // 持续时长
    useGrouping: false,  // 是否需要分组
  }
  let option2 = {
    decimalPlaces: 1, // 保留两位小数
    duration: 1.5, // 持续时长
    useGrouping: false,  // 是否需要分组
    suffix: '%', // 后缀
  }
  panelItems.forEach(item => {
    // 数据滚动:CountUp(id选择器或元素对象,数字,配置){  }
    new CountUp(`total-num-${item.id}`, item.totalNum, option1).start()
    // 百分比滚动
    new CountUp(`percentage-num-${item.id}`, item.percentage, option2).start()
  })
}

</script>

<style scoped lang="scss">
.bottom-content {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
  /* padding-top: 40px; */
}

.bottom-content .item {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  margin-top: 60px;

  flex: 1;
  height: 100%;
  padding: 0 10px 0 35px;
  /* border: 1px solid red; */
}

.bottom-content .pan-left {
  font-size: 16px;
  color: #ffffff;
  opacity: 0.8;
}
.bottom-content .pan-left .title {
  color: white;
}
.bottom-content .pan-left .number {
  font-size: 36px;
  font-weight: bold;
  color: #23aeff;
  line-height: 60px;
}

.bottom-content .pan-left .unit {
  font-size: 18px;
  color: #23aeff;
}

.bottom-content .pan-right {
  margin-top: 35px;
}

.bottom-content .panel1 .pan-right .up-triangle {
  display: inline-block;
  margin-bottom: 4px;
  width: 0;
  height: 0;
  border-bottom: 8px solid #c70013;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
}
.bottom .panel1 .pan-right .percentage {
  color: #c70013;
}
.bottom .panel2 .pan-right .up-triangle {
  display: inline-block;
  margin-bottom: 4px;
  width: 0;
  height: 0;
  border-bottom: 8px solid #c70013;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
}
.bottom-content .panel2 .pan-right .percentage {
  color: #c70013;
}

.bottom-content .panel3 .pan-right .down-triangle {
  display: inline-block;
  margin-bottom: 1px;
  width: 0;
  height: 0;
  border-top: 8px solid #37a73f;
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
}
.bottom-content .panel3 .pan-right .percentage {
  color: #37a73f;
}
</style>

water-ball.vue
<template>
  <div class="water-ball">
    <!-- 波浪icon -->
    <svg
      version="1.1"
      xmlns="https://www.w3.org/2000/svg"
      xmlns:xlink="https://www.w3.org/1999/xlink"
      x="0px"
      y="0px"
      style="display: none"
    >
      <symbol id="wave">
        <path
          d="M420,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C514,6.5,518,4.7,528.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H420z"
        ></path>
        <path
          d="M420,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C326,6.5,322,4.7,311.5,2.7C304.3,1.4,293.6-0.1,280,0c0,0,0,0,0,0v20H420z"
        ></path>
        <path
          d="M140,20c21.5-0.4,38.8-2.5,51.1-4.5c13.4-2.2,26.5-5.2,27.3-5.4C234,6.5,238,4.7,248.5,2.7c7.1-1.3,17.9-2.8,31.5-2.7c0,0,0,0,0,0v20H140z"
        ></path>
        <path
          d="M140,20c-21.5-0.4-38.8-2.5-51.1-4.5c-13.4-2.2-26.5-5.2-27.3-5.4C46,6.5,42,4.7,31.5,2.7C24.3,1.4,13.6-0.1,0,0c0,0,0,0,0,0l0,20H140z"
        ></path>
      </symbol>
    </svg>

    <!-- 水球 -->
    <div class="box" :style="{ width: size, height: size }">
      <div class="percent" :style="{ fontSize: countSize }">
        <div class="percentNum" id="count" ref="countRef">0</div>
        <div class="percentB">%</div>
      </div>
      <!-- 这个元素往上移 -->
      <div id="water" class="water" ref="waterRef">
        <svg viewBox="0 0 560 20" class="water_wave water_wave_back">
          <use xlink:href="#wave"></use>
        </svg>
        <svg viewBox="0 0 560 20" class="water_wave water_wave_front">
          <use xlink:href="#wave"></use>
        </svg>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch } from "vue";

const props = defineProps({
  size: {
    type: String,
    default: "190px",
  },
  // 目标进度
  percentage: {
    type: Number,
    default: 0,
  },
  countSize: {
    type: String,
    default: "40px",
  },
});

const countRef = ref(null);
const waterRef = ref(null);

watch(() => props.percentage, (newV, oldV) => {
  startAnimation(newV)
});

// 水球动画函数
function startAnimation(percentage) {
  let countEl = countRef.value;
  let waterEl = waterRef.value;
  let percent = countEl.innerText; // 数据初始值 0
  let interval;
  // 1.定时更新数据
  interval = setInterval(function () {
    countEl.innerHTML = percent;
    if (percent <= 100) {
      // 2.超过100不能移动
      waterEl.style.transform = "translate(0" + "," + (100 - percent) + "%)";
    }
    // 3.停止定时
    if (percent >= percentage) {
      countEl.innerHTML = percentage;
      clearInterval(interval);
    }
    percent++;
  }, 60);
}
</script>

<style scoped>
.water-ball {
  position: relative;
}
.box {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 100%;
  overflow: hidden;
}
.box .percent {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 3;
  width: 100%;
  height: 100%;

  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
}
.box .water {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  transform: translate(0, 100%);
  background: #00c6ff;
}
/* 波浪定位 */
.box .water_wave {
  width: 200%;
  position: absolute;
  bottom: 100%;
}

/* ============波浪动画============ */
.box .water_wave_back {
  right: 0;
  fill: #c7eeff;
  animation: wave-back 1.4s infinite linear;
}
.box .water_wave_front {
  left: 0;
  fill: #00c6ff;
  margin-bottom: -1px;
  animation: wave-front 0.7s infinite linear;
}
@keyframes wave-front {
  100% {
    transform: translateX(-50%);
  }
}
@keyframes wave-back {
  100% {
    transform: translateX(50%);
  }
}
/* ============波浪动画============ */
</style>

right-top-panle.vue
<template>
  <div class="right-top-panel">
    <!-- 1. 水球 -->
    <water-ball class="right-water-ball" :percentage="percentage"></water-ball>
    <!-- 2. 图例 -->
    <div class="legend">
      <template v-for="item in panelItems" :key="item">
        <div class="leg-name">
          <span :class="['dot', `area${item.id}`]"></span>
          <span class="name">{{ item.name }}</span>
          <span class="percentage">{{ item.percentage }}</span>
        </div>
      </template>
    </div>
  </div>
</template>

<script setup>
import WaterBall from "./water-ball.vue";

const props = defineProps({
  percentage: {
    type: Number,
    default: 0,
  },
  panelItems: {
    type: Array,
    default: function () {
      return [];
    },
  },
});
</script>

<style scoped>
.right-top-panel {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  align-items: center;
}

.right-top-panel .right-water-ball {
  width: 269px;
  height: 199px;
  margin-top: 50px;
  margin-left: 40px;
}

.right-top-panel .legend {
  flex: 1;
}
.right-top-panel .leg-name {
  margin-top: 23px;
  margin-left: 40px;
}
.right-top-panel .leg-name span {
  display: inline-block;
  font-size: 20px;
  margin-right: 11px;
}
.right-top-panel .legend .dot {
  width: 17px;
  height: 17px;
  border-radius: 50%;
}
.right-top-panel .legend .area1 {
  background-color: #209393;
}
.right-top-panel .legend .area2 {
  background-color: #1a54a5;
}
.right-top-panel .legend .area3 {
  background-color: #85caf0;
}
.right-top-panel .legend .area4 {
  background-color: #f5b64a;
}
.right-top-panel .legend .area5 {
  background-color: #ee792e;
}
.right-top-panel .legend .name {
  color: white;
}
.right-top-panel .legend .percentage {
  color: #0cd2ea;
}
</style>

right-bottom.vue
<template>
  <svg
    id="yichang"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="456"
    height="127"
    viewBox="0 0 456 127"
  >
    <defs>
      <linearGradient
        id="lwj-linear-gradient"
        x1="432"
        y1="61.625"
        x2="2"
        y2="61.625"
        gradientUnits="userSpaceOnUse"
      >
        <stop offset="0" stop-color="#26cadd" />
        <stop offset="0.301" stop-color="#26a3ff" />
        <stop offset="0.624" stop-color="#26cadd" />
        <stop offset="0.994" stop-color="#26a3ff" />
        <stop offset="1" stop-color="#26a3ff" />
      </linearGradient>
      <linearGradient
        id="lwj-linear-gradient-2"
        x1="432"
        y1="61.625"
        x2="2"
        y2="61.625"
        gradientUnits="userSpaceOnUse"
      >
        <stop offset="0" stop-color="#2b6bdc" />
        <stop offset="0.031" stop-color="#2b6bdc" />
        <stop offset="0.597" stop-color="#2bdcd2" />
        <stop offset="1" stop-color="#2b6bdc" />
      </linearGradient>
      <filter id="lwj-filter" filterUnits="userSpaceOnUse">
        <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
        <feComposite result="composite" />
        <feComposite result="composite-2" />
        <feComposite result="composite-3" />
        <feFlood result="flood" flood-color="#f98800" flood-opacity="0.6" />
        <feComposite result="composite-4" operator="in" in2="composite-3" />
        <feBlend result="blend" in2="SourceGraphic" />
        <feBlend result="blend-2" in="SourceGraphic" />
      </filter>
      <filter
        id="lwj-filter-2"
        x="67"
        y="-6"
        width="50"
        height="49"
        filterUnits="userSpaceOnUse"
      >
        <feGaussianBlur result="blur" stdDeviation="6.667" in="SourceAlpha" />
        <feComposite result="composite" />
        <feComposite result="composite-2" />
        <feComposite result="composite-3" />
        <feFlood result="flood" flood-color="#f98800" flood-opacity="0.6" />
        <feComposite result="composite-4" operator="in" in2="composite-3" />
        <feBlend result="blend" in2="SourceGraphic" />
        <feBlend result="blend-2" in="SourceGraphic" />
      </filter>
    </defs>
    <path
      id="curve_right"
      class="lwj-cls-1"
      d="M2.006,86.083H31.461l3.006-9.664,4.208,13.288,4.208-10.268,3.006,6.644H73.541l4.208-10.872L83.76,103l7.871-82.748,10.163,71.876,3.607-6.644h11.421l3.607,13.892L124.637,72.8l5.41,20.536,3.006-7.852h46.888l4.208-15.7,6.011,28.388,5.41-19.932,4.208,7.248h37.815L241,76l4,14,5-11,3,6h27l4-11,6,29,10-69,8,57,3-6h13l3,14,4-27,6,20,2-7h46l6-16,5,29,7-20,4,7h25"
    />
    <path
      id="curve"
      class="lwj-cls-2"
      d="M2.006,86.083H31.461l3.006-9.664,4.208,13.288,4.208-10.268,3.006,6.644H73.541l4.208-10.872L83.76,103l7.871-82.748,10.163,71.876,3.607-6.644h11.421l3.607,13.892L124.637,72.8l5.41,20.536,3.006-7.852h46.888l4.208-15.7,6.011,28.388,5.41-19.932,4.208,7.248h37.815L241,76l4,14,5-11,3,6h27l4-11,6,29,10-69,8,57,3-6h13l3,14,4-27,6,20,2-7h46l6-16,5,29,7-20,4,7h25"
    />

    <!-- 
        <circle
        id="curve_d_2"
        class="lwj-cls-3"
        cx="299.563"
        cy="34.734"
        r="4.813"
      />
      <ellipse
        id="curve_d_1"
        class="lwj-cls-4"
        cx="91.578"
        cy="17.844"
        rx="4.828"
        ry="4.844"
      /> 
      -->

    <!-- 只有一个点 -->
    <!-- <circle  class="lwj-cls-3" cx="0" cy="0" r="5" fill="red">
        <animateMotion
          dur="10s"
          repeatCount="indefinite"
          rotate="auto"
          begin="0s"
        >
          <mpath href="#curve"></mpath>
        </animateMotion>
      </circle> -->

    <!-- n 个点 -->
    <template v-for="(item, index) in dots" :key="item">
      <circle class="lwj-cls-3" cx="0" cy="0" :r="item.value">
        <animateMotion
          :dur="item.dur"
          repeatCount="indefinite"
          rotate="auto"
          :begin="item.begin"
        >
          <mpath href="#curve"></mpath>
        </animateMotion>
      </circle>
    </template>
  </svg>
</template>

<script setup>
const props = defineProps({
  dots: {
    type: Array,
    default: function () {
      return [];
    },
  },
});
</script>

<style scoped>
.lwj-cls-1,
.lwj-cls-2 {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 4px;
  fill-rule: evenodd;
}

.lwj-cls-1 {
  opacity: 0.8;
  stroke: url(#lwj-linear-gradient);
}

.lwj-cls-2 {
  stroke: url(#lwj-linear-gradient-2);
}

.lwj-cls-3,
.lwj-cls-4 {
  fill: #f98800;
  /* 告诉浏览器该元素将会改变哪些属性,让浏览器提前做好优化的准备( 比较消耗内存 ) */
  will-change: opacity;
}

.lwj-cls-3 {
  filter: url(#lwj-filter);
}

.lwj-cls-4 {
  filter: url(#lwj-filter-2);
}
</style>

在组件中使用

模拟数据

views/config/homeData.js   

// 充电桩数据
export const chargingPileData = [
  {
    // value: 100,
    value: 0,
    name: "广州占比",
    // percentage: "5%",
    percentage: "0%",
    color: "#34D160",
  },
  {
    // value: 200,
    value: 0,
    name: "深圳占比",
    // percentage: "4%",
    percentage: "0%",
    color: "#027FF2",
  },
  {
    // value: 300,
    value: 0,
    name: "东莞占比",
    // percentage: "8%",
    percentage: "0%",
    color: "#8A00E1",
  },
  {
    // value: 400,
    value: 0,
    name: "佛山占比",
    // percentage: "10%",
    percentage: "0%",
    color: "#F19610",
  },
  {
    // value: 500,
    value: 0,
    name: "中山占比",
    // percentage: "20%",
    percentage: "0%",
    color: "#6054FF",
  },
  {
    // value: 600,
    value: 0,
    name: "珠海占比",
    // percentage: "40%",
    percentage: "0%",
    color: "#00C6FF",
  },
];

// 流程监控数据
export const precessMonitoringData = [
  {
    name: "正常",
    data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    // data: [220, 182, 191, 234, 290, 330, 310, 201, 154, 190, 330, 410],
  },
  {
    name: "异常",
    data: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    // data: [20, 12, 11, 24, 90, 330, 10, 1, 154, 90, 330, 10],
  },
];

// 充电数据统计
export const chargingStatisticsData = [
  {
    name: "一月",
    value: 0,
    // value: 500
  },
  {
    name: "二月",
    value: 0,
    // value: 2000
  },
  {
    name: "三月",
    value: 0,
    // value:  3600
  },
  {
    name: "四月",
    value: 0,
    // value: 1000
  },
  {
    name: "五月",
    value: 0,
    // value: 1000
  },
  {
    name: "六月",
    value: 0,
    // value: 2000
  },
  {
    name: "七月",
    value: 0,
    // value: 4000
  },
];

// 异常监控
export const exceptionMonitoringData = [
  // {
  //     "id": 1,
  //     "name": "异常1",
  //     "value": 5,
  //     "dur": "10s",
  //     "begin": "0s"
  // },
  // {
  //     "id": 2,
  //     "name": "异常2",
  //     "value": 3,
  //     "dur": "10s",
  //     "begin": "-3s"
  // },
  // {
  //     "id": 3,
  //     "name": "异常3",
  //     "value": 5,
  //     "dur": "10s",
  //     "begin": "-5s"
  // }
];

// 充电桩数据分析
export const dataAnalysisData = [
  // {
  //   id: 1,
  //   title: "充电桩总数(个)",
  //   totalNum: 8579.9,
  //   unit: "万",
  //   percentage: 79.5,
  //   isUp: true,
  // },
  // {
  //   id: 2,
  //   title: "年增长总数(个)",
  //   totalNum: 2856.6,
  //   unit: "万",
  //   percentage: 88.9,
  //   isUp: true,
  // },
  // {
  //   id: 3,
  //   title: "月增长总数(个)",
  //   totalNum: 1189.3,
  //   unit: "万",
  //   percentage: 62,
  //   isUp: false,
  // },
  {
    id: 1,
    title: "充电桩总数(个)",
    totalNum: 0,
    unit: "万",
    percentage: 0,
    isUp: true,
  },
  {
    id: 2,
    title: "年增长总数(个)",
    totalNum: 0,
    unit: "万",
    percentage: 0,
    isUp: true,
  },
  {
    id: 3,
    title: "月增长总数(个)",
    totalNum: 0,
    unit: "万",
    percentage: 0,
    isUp: false,
  },
];

// 充电桩Top4占比
export const chargingTop4Data = [
  {
    id: 1,
    name: "深圳",
    percentage: "0%",
  },
  {
    id: 2,
    name: "广州",
    percentage: "0%",
  },
  {
    id: 3,
    name: "东莞",
    percentage: "0%",
  },
  {
    id: 4,
    name: "佛山",
    percentage: "0%",
  },
  {
    id: 5,
    name: "其它",
    percentage: "0%",
  },
  // {
  //   id: 1,
  //   name: "深圳",
  //   percentage: "30%",
  // },
  // {
  //   id: 2,
  //   name: "广州",
  //   percentage: "20%",
  // },
  // {
  //   id: 3,
  //   name: "东莞",
  //   percentage: "10%",
  // },
  // {
  //   id: 4,
  //   name: "佛山",
  //   percentage: "10%",
  // },
  // {
  //   id: 5,
  //   name: "其它",
  //   percentage: "30%",
  // },
];

真实请求

views/powerView.vue

<template>
  <main class="screen-bg">
    <div class="header"></div>

    <div class="left-top">
      <PieEcharts :echartsChargingPileData="echartsChargingPileData" />
    </div>
    <div class="left-bottom">
      <LineEcharts :echartsPrecessMonitoringData="echartsPrecessMonitoringData" />
    </div>

    <div class="right-top">
      <RightTopPanel :panelItems="echartChargingTop4Data" :percentage="percentage" />
    </div>
    <div class="right-center">
      <BarEchaarts :echartsChargingStatisticsData="echartsChargingStatisticsData" />
    </div>
    <div class="right-bottom">
      <RightBottomSvg :dots="echartsExceptionMonitoringData" />
    </div>

    <div class="center">
      <CenterSvg />
    </div>

    <div class="bottom">
      <BottomPanel :panelItems="echartDataAnalysisData" />
    </div>
  </main>
</template>

<script setup>
import PieEcharts from "@/components/pie-echarts.vue";
import LineEcharts from "@/components/line-echarts.vue";
import BarEchaarts from "@/components/bar-echarts.vue"
import {
  chargingPileData,
  precessMonitoringData,
  chargingStatisticsData,
  exceptionMonitoringData,
  dataAnalysisData,
  chargingTop4Data
} from '@/views/config/homeData'
import {getPowerScreenData} from '@/services'
import RightBottomSvg from '@/components/right-bottom-svg.vue'
import CenterSvg from '@/components/center-svg.vue'
import BottomPanel from '@/components/bottom-panel.vue'
import RightTopPanel from '@/components/right-top-panel.vue'

import { ref } from "vue";


// 充电桩饱和比例数据
let echartsChargingPileData = ref(chargingPileData);

// 流程监控数据
let echartsPrecessMonitoringData = ref(precessMonitoringData)

// 充电数据统计
let echartsChargingStatisticsData  = ref(chargingStatisticsData)

// 异常监控
let echartsExceptionMonitoringData = ref(exceptionMonitoringData)

// 充电桩数据分析
let echartDataAnalysisData = ref(dataAnalysisData)

// 充电桩TOP4占比
let echartChargingTop4Data = ref(chargingTop4Data)
let percentage = ref(0) // 水球的百分比

// 发送网络请求获取数据
getPowerScreenData().then(res => {
  echartsChargingPileData.value = res.data.chargingPile.data
  echartsPrecessMonitoringData.value = res.data.processMonitoring.data
  echartsChargingStatisticsData.value = res.data.chargingStatistics.data
  echartsExceptionMonitoringData.value = res.data.exceptionMonitoring.data
  echartDataAnalysisData.value = res.data.dataAnalysis.data
  echartChargingTop4Data.value = res.data.chargingTop4.data
  percentage.value = res.data.chargingTop4.totalPercentage
})
</script>

<style lang="scss" scoped>
.screen-bg {
  position: absolute;
  width: 100%;
  height: 100%;

  background-image: url(@/assets/images/bg.png);
  background-repeat: no-repeat;
  background-size: 100% 100%;
  background-color: #070a3c;

  .header {
    position: absolute;
    top: 21px;
    left: 0;
    right: 0;
    height: 56px;

    background-image: url(@/assets/images/bg_header.svg);
  }

  .left-top {
    position: absolute;
    top: 116px;
    left: 16px;
    width: 536px;
    height: 443px;

    background-image: url(@/assets/images/bg_left-top.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }

  .left-bottom {
    position: absolute;
    bottom: 40px;
    left: 16px;
    width: 536px;
    height: 443px;

    background-image: url(@/assets/images/bg_left_bottom.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }

  .right-top {
    position: absolute;
    right: 17px;
    top: 96px;
    width: 519px;
    height: 327px;

    background-image: url(@/assets/images/bg_right_top.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }
  .right-center {
    position: absolute;
    right: 17px;
    top: 443px;
    width: 519px;
    height: 327px;

    background-image: url(@/assets/images/bg_right_center.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }
  .right-bottom {
    position: absolute;
    right: 17px;
    bottom: 49px;
    width: 519px;
    height: 242px;

    display: flex;
    justify-content: center;
    align-items: center;

    background-image: url(@/assets/images/bg_right_bottom.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }

  .center {
    position: absolute;
    right: 540px;
    bottom: 272px;
    width: 823px;
    height: 710px;

    /* border: 5px solid pink; */
  }

  .bottom {
    position: absolute;
    right: 540px;
    bottom: 49px;
    width: 823px;
    height: 209px;

    background-image: url(@/assets/images/bg_bottom.svg);
    background-repeat: no-repeat;
    background-size: 100% 100%;
  }
}
</style>

这里如果想进一步优化,比如如何按需引入echarts ,可参考: vue3中如何更优雅的使用echarts?-CSDN博客 片文章

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1667896.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

全新时代的降临——比亚迪,助力未来出行

近日&#xff0c;世界舞台中央聚焦&#xff0c;比亚迪登上欧洲顶级赛事赞助席位&#xff0c;让全球见证中国新能源汽车传奇崛起&#xff01;作为新能源领袖品牌&#xff0c;比亚迪现已累计销售突破730万辆&#xff0c;全球每售出五辆新能源汽车&#xff0c;便有一辆来自比亚迪。…

vivado Virtex-7 配置存储器器件

Virtex-7 配置存储器器件 下表所示闪存器件支持通过 Vivado 软件对 Virtex -7 器件执行擦除、空白检查、编程和验证等配置操作。 本附录中的表格所列赛灵思系列非易失性存储器将不断保持更新 &#xff0c; 并支持通过 Vivado 软件对其中所列非易失性存储器 进行擦除、…

【中航证券军工】北摩高科2023年报2024Q1点评:聚焦航空及军工主赛道,民机业务有望成为第二曲线

事件 公司4月24日公告&#xff0c;2024Q1实现营收&#xff08;2.40亿元&#xff0c;同比-23.71%)&#xff0c;归母净利润&#xff08;0.73亿元&#xff0c;同比-45.63%)&#xff0c;毛利率&#xff08;62.63%&#xff0c;同比-7.22pcts)&#xff0c;净利率&#xff08;37.34%&…

安装conda并搭建python环境(入门教程)

文章目录 1. 什么是 conda&#xff1f;1.1 Conda 与 Anaconda 的区别1.2 Conda 与 pip 的区别 2. 下载安装3. 配置并使用 conda3.1 配置下载源3.2 环境管理3.2.1 创建&#xff08;删除&#xff09;环境3.2.2 激活&#xff08;切换&#xff09;环境3.2.2 下载&#xff08;卸载&a…

Sping源码(七)—ConfigurationClassPostProcessor ——@PropertySources解析

序言 先来简单回顾一下ConfigurationClassPostProcessor大致的一个处理流程&#xff0c;再来详细的讲解PropertySources注解的处理逻辑。 详细的步骤可参考ConfigurationClassPostProcessor这篇帖子。 流程图 从获取所有BeanDefinition -> 过滤、赋值、遍历 -> 解析 -&…

常用的简单友好的工单系统(免费)- WGCAT

最近在项目中&#xff0c;有工单系统的需求场景&#xff0c;所以想寻找一款轻量简单的运维工单软件&#xff0c;主要用来记录和处理工作中的一些故障、维护&#xff0c;主要用来记录设备的维护状态&#xff0c;包括服务器、主机、交换机那些 WGCAT&#xff0c;是一款简单轻量的…

2024中国(重庆)人工智能展览会8月举办

2024中国(重庆)人工智能展览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 【报名I59交易会 2351交易会 9466】 展会背景&#xff1a; 2024中国航空科普大会暨第八届全国青少年无人机大赛在…

macOS12安装 php8.1和apache

1. 安装php 8.1 macOS12不再自带php brew tap shivammathur/php 查看可安装版本 brew search php 安装指定版本 brew install php8.1 环境配置 vim ~/.zshrc export PATH"/usr/local/opt/php8.1/bin:$PATH" export PATH"/usr/local/opt/php8.1/sbin:$PAT…

Git Bash和Git GUI设置中文的方法

0 前言 Git是一个分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。一般默认语言为英文&#xff0c;本文介绍修改Git Bash和Git GUI语言为中文的方法。 1 Git Bash设置中文方法 &#xff08;1&#xff09;鼠标右键&#xff0c;单击“Git B…

每日两题 / 108. 将有序数组转换为二叉搜索树 543. 二叉树的直径(LeetCode热题100)

108. 将有序数组转换为二叉搜索树 - 力扣&#xff08;LeetCode&#xff09; 每次将数组对半分&#xff0c;数组的中点作为树的节点 先选择整个数组的中点作为根节点&#xff0c;然后选择对半分后的两个子数组的中点作为根节点的左右节点… /*** Definition for a binary tre…

【操作系统期末速成】​内存管理|内存的装入模块在装入内存的方式|分配管理方式|页面置换算法|页面置换

&#x1f3a5; 个人主页&#xff1a;深鱼~&#x1f525;收录专栏&#xff1a;操作系统&#x1f304;欢迎 &#x1f44d;点赞✍评论⭐收藏 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到…

Django开发实战之定制管理后台界面及知识梳理(上)

不知道不觉写博客已经半个月了&#xff0c;涨了164个粉丝&#xff0c;在一边分享笔记的过程&#xff0c;一边收获粉丝&#xff0c;感觉很开心也很幸福&#xff0c;希望看我博客的小伙伴都能有所收获&#xff0c;大家共同成长进步&#xff0c;好拉&#xff0c;话不多说&#xff…

hcip实验6:BGP综合实验

实验拓扑&#xff1a; 实验配置&#xff1a; ip地址配置&#xff1a; #R1 [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [R1-GigabitEthernet0/0/0]int l0 [R1-LoopBack0]ip add 172.16.0.1 32 [R1-LoopBack0]int l1 [R1-LoopBack1]ip add 192.168.1.1 24#R2…

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测

回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测 目录 回归预测 | Matlab实现GA-LSSVM遗传算法优化最小二乘支持向量机多输入单输出回归预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 Matlab实现GA-LSSVM遗传算法优化最小…

【RAG论文】RAG中半结构化数据的解析和向量化方法

论文简介 论文题目&#xff1a; 《A Method for Parsing and Vectorization of Semi-structured Data used in Retrieval Augmented Generation》 论文链接&#xff1a; https://arxiv.org/abs/2405.03989 代码: https://github.com/linancn/TianGong-AI-Unstructure/tree/m…

阮怀俊参与五龙乡黄沙村村企联办“强村公司”

为走好海岛县高质量发展共同富裕特色之路&#xff0c;探索村级集体经济发展新路径、扶持新模式、运行新机制&#xff0c;嵊泗县五龙乡黄沙村股份经济合作社与杭州山舍乡建乡村产业发展有限责任公司联办成“强村公司”。 创始人阮怀俊表示&#xff0c;双方就融合乡域发展和文旅产…

Linux 操作系统MySQL 数据库1

1.MySQL 数据库 数据库是“按照数据结构来组织、 存储和管理数据的仓库”。 是一个长期存储在计算机内的、 有组织的、 可共享的、 统一管理的大量数据的集合。 它的存储空间很大&#xff0c; 可以存放百万条、 千万条、 上亿条数据。 但是数据库并不是随意地将数据进行…

python如何单步调试

Python怎么单步调试&#xff1f;下面给大家介绍一下单步调试&#xff1a; 方法一&#xff1a;执行 python -m pdb myscript.py (Pdb) 会自己主动停在第一行。等待调试&#xff0c;这时你能够看看帮助。 方法二&#xff1a;在所调试程序的开头中&#xff1a;import pdb 并在你…

MySQL中逗号分隔字段查询方法

MySQL中逗号分隔字段查询 select * FROM th_work_gand_up where FIND_IN_SET(11,lane_code) ; select * from th_work_gand_up where lane_code regexp (^|,)(11|1)(,|$);

金融业开源软件应用 管理指南

金融业开源软件应用 管理指南 1 范围 本文件提供了金融机构在应用开源软件时的全流程管理指南&#xff0c;对开源软件的使用和管理提供了配套 组织架构、配套管理规章制度、生命周期流程管理、风险管理、存量管理、工具化管理等方面的指导。 本文件适用于金融机构规范自身对开…