前置知识
效果图
使用技术
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博客 片文章