vue3+ts 使用amCharts展示地图,1.点击左侧国家,可以高亮并放大右侧地图对应的国家。 2.展示数据球。
效果图展示:
1.点击左侧国家,可以高亮并放大右侧地图对应的国家。
2.展示数据球。
下载依赖
yarn add @amcharts/amcharts5
其中,props.countryData的数据格式为
[{
“country”: “加拿大”,
“code”: “CA”,
“deviceCount”: 1
},{
“country”: “瑞士”,
“code”: “CH”,
“deviceCount”: 29
},{
“country”: “中国”,
“code”: “CN”,
“deviceCount”: 10774
},{
“country”: “德国”,
“code”: “DE”,
“deviceCount”: 42
}]
<template>
<div class="device-distribution-box">
<div class="header-title">{{ $t('countryDistributionDevice') }}</div>
<section class="flex-box">
<div class="country-box">
<div class="country-list" v-for="country in mapData">
<div
:class="activeCountry == country.code ? 'active-country' : ''"
@click="clickCountry(country.code)"
class="sf-ellipsis"
:title="country.country + '(' + country.deviceCount + ')'"
>
{{ country.country }}({{ country.deviceCount }})
</div>
</div>
</div>
<div id="chartdiv"></div> // 地图展示容器
</section>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, watch } from 'vue';
import useLocale from '@/utils/i18n/store';
import { storeToRefs } from 'pinia';
import am5geodata_lang_EN from '@amcharts/amcharts5-geodata/lang/EN';// 地图语言包
import am5geodata_lang_cn_ZH from '@amcharts/amcharts5-geodata/lang/cn_ZH';//地图语言包
import am5geodata_lang_PT from '@amcharts/amcharts5-geodata/lang/PT'; // 地图语言包
import * as am5map from '@amcharts/amcharts5/map';
import am5geodata_worldLow from '@amcharts/amcharts5-geodata/worldLow';
import * as am5 from '@amcharts/amcharts5';
import am5themes_Animated from '@amcharts/amcharts5/themes/Animated';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const localeStore = useLocale(); //获取本地语言
const { locale } = storeToRefs(localeStore); //获取本地语言
interface Props {
countryData: Array<any>;
}
const props = withDefaults(defineProps<Props>(), {
countryData: () => {
return [];
},
});
// 数据接口
interface MapData {
code: string;
deviceCount: number;
name: string;
country: string;
id?: string;
}
const mapData = ref<Array<MapData>>([]);
const activeCountry = ref<string>('');
let polygonSeries = reactive({}); // 地图数据
let root = reactive({}); // 地图根
// 数据更新,更新地图
watch(
() => props.countryData,
(newValue, oldValue) => {
mapData.value = props.countryData.map((item) => {
item.id = item.code;
return item;
});
refreshAmchartsMap();
},
);
// 左侧国家点击事件-点击高亮国家
const clickCountry = (countryCode: string) => {
activeCountry.value = countryCode;
// 根据点击的国家,添加该国家的颜色数据,去除无关国家的颜色数据
let tempData = mapData.value.map((item) => {
if (item.id == countryCode) item.polygonSettings = { fill: am5.color(0x3a70d3) };
else item.polygonSettings = {};
return item;
});
// 绑定数据 高亮该国家颜色
polygonSeries.data.setAll(tempData);
// 地图放大到该国家
polygonSeries.events.on('datavalidated', function () {
const zoomTargets = [countryCode];
const zoomDataItems = [];
zoomTargets.forEach(function (id) {
zoomDataItems.push(polygonSeries.getDataItemById(id));
});
polygonSeries.zoomToDataItems(zoomDataItems);
});
};
// 更新相关数据
const refreshAmchartsMap = () => {
root.setThemes([am5themes_Animated.new(root)]);
// Create the map chart 地图的样式不一样
let chart = root.container.children.push(
am5map.MapChart.new(root, {
projection: am5map.geoNaturalEarth1(),
}),
);
polygonSeries = chart.series.push(
am5map.MapPolygonSeries.new(root, {
geoJSON: am5geodata_worldLow, // 地图展示的国家区域
exclude: ['AQ'], //该操作将会从地图上删除南极洲。
fill: am5.color(0xbbbbbb),
geodataNames: // 根据本地语言配置地图语言
locale.value == 'zh'
? am5geodata_lang_cn_ZH
: locale.value == 'en'
? am5geodata_lang_EN
: locale.value == 'pt'
? am5geodata_lang_PT
: '',
}),
);
// ====================================
// Create pins
// ====================================
// 点符号
let pointSeries = chart.series.push(
am5map.MapPointSeries.new(root, {
// ...
// autoScale: true,
polygonIdField: 'code', // 根据code值来映射点的位置,code的值就是地图中类似“CN“的国家代码
}),
);
let colorSet = am5.ColorSet.new(root, { step: 2 });
// 点符号样式设置
pointSeries.bullets.push(function (root, series, dataItem) {
let value = dataItem.dataContext.value;
let container = am5.Container.new(root, {});
let color = colorSet.next();
let radius = value / 5 < 15 ? 15 : value / 5 > 50 ? 50 : value / 5; //球的大小!!!!!!!!!!!!需求动态修改
let circle = container.children.push(
am5.Circle.new(root, {
radius: radius,
fill: color,
dy: -radius * 2,
}),
);
let pole = container.children.push(
am5.Line.new(root, {
stroke: color,
height: -radius * 2,
strokeGradient: am5.LinearGradient.new(root, {
stops: [{ opacity: 1 }, { opacity: 1 }, { opacity: 0 }],
}),
}),
);
let label = container.children.push(
am5.Label.new(root, {
text: value,
fill: am5.color(0xffffff),
fontWeight: '400',
centerX: am5.p50,
centerY: am5.p50,
dy: -radius * 2,
}),
);
let titleLabel = container.children.push(
am5.Label.new(root, {
text: dataItem.dataContext.title,
fill: color,
fontWeight: '500',
fontSize: '1em',
centerY: am5.p50,
dy: -radius * 2,
dx: radius,
}),
);
return am5.Bullet.new(root, {
sprite: container,
});
});
// 映射点数据
for (var i = 0; i < mapData.value.length; i++) {
let d = mapData.value[i];
pointSeries.data.push({
code: d.code,
value: d.deviceCount,
});
}
// 鼠标移入和点击的效果
polygonSeries.mapPolygons.template.setAll({
tooltipText: '{name}',
toggleKey: 'active',
interactive: true,
templateField: 'polygonSettings', //多边形也可以使用模板字段从数据中获取其设置的值。模板字段允许将系列数据中的对象属性绑定到多边形模板的设置。
});
// 绑定数据
polygonSeries.data.setAll(mapData.value);
// hover及active高亮颜色
polygonSeries.mapPolygons.template.states.create('hover', {
fill: root.interfaceColors.get('primaryButtonHover'),
});
polygonSeries.mapPolygons.template.states.create('active', {
fill: root.interfaceColors.get('primaryButtonHover'),
});
// Set clicking on "water" to zoom out
// 点击背景空白处,恢复到初始大小
chart.chartContainer.get('background').events.on('click', function () {
chart.goHome();
});
// Make stuff animate on load
chart.appear(1000, 100);
};
//新建国家地图 Create root element
const newAmchartsMap = () => {
root = am5.Root.new('chartdiv');
};
onMounted(() => {
newAmchartsMap();
});
</script>
<style lang="scss" scoped>
.device-distribution-box {
.header-title {
font-family:
PingFang SC,
PingFang SC;
font-weight: 500;
font-size: 16px;
color: #191919;
line-height: 24px;
margin-bottom: 16px;
}
.flex-box {
display: flex;
height: calc(100% - 40px);
.country-box {
// height: 328px;
overflow-y: auto;
overflow-x: hidden;
.country-list {
min-width: 160px;
max-width: 200px;
div {
cursor: pointer;
margin-bottom: 16px;
font-family:
PingFang SC,
PingFang SC;
font-weight: 500;
font-size: 14px;
color: #7f7f7f;
line-height: 22px;
}
.active-country {
font-weight: bold;
font-size: 14px;
color: #3a70d3 !important;
line-height: 22px;
}
}
}
}
#chartdiv {
width: 100%;
// height: 328px;
background-color: #fbfbfb;
}
}
</style>