需求:
点击单元列表地图显示该单元的信息,重新绘点。
点击道路类型:路口、路段、其他分别显示对应信息,重新绘点。
点击事故类型:事故、拥堵、违停分别显示对应信息,重新绘点。
直接上图,最终结果如下, 分享技术点如下,如有疑问可关注博主进行提问。
1:地图区域描边。比如(杭州市、萧山区边界描绘)
2:点标记(根据数量渲染不同颜色的点,显示出数量信息(根据需求可自己变更))
3:点击点坐标显示该点信息
准备工作:安装使用高德地图
npm install vue-amap --save
// !!!引入高德地图
import VueAMap from 'vue-amap'
//!!!使用 高德地图
Vue.use(VueAMap);
//!!!高德地图初始化
VueAMap.initAMapApiLoader({
key: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
plugin: [
"AMap.Autocomplete",
"AMap.PlaceSearch",
"AMap.Scale",
"AMap.OverView",
"AMap.ToolBar",
"AMap.MapType",
"AMap.PolyEditor",
"AMap.CircleEditor",
"AMap.Geocoder",
"AMap.Geolocation",
"AMap.MarkerClusterer",
"AMap.DistrictSearch"
// "AMap.Marker",
],
// 默认高德 sdk 版本为 1.4.4
v: "1.4.4",
});
一、引入组件,使用封装的GetPosition组件,封装组件便于重复引用。(组件内容即步骤二)
<!-- 组件说明 -->
<template>
<div class="">
<GetPosition
class="map"
:pointList="pointList"
@drawClose="handleClose"
@drawOpen="handleOpen"
/>
</div>
</template>
<script>
import getPosition from "@/views/components/getLocation/map.vue";
export default {
components: {
GetPosition: getPosition,
},
data() {
return {
pointList: [
{
roadId: 331181207330,
roadTude: 30.244784,
roadLong: 120.294536,
roadTeam: 4,
roadType: 18,
roadAddr: "弘惠路-学知路交叉口",
roadTeamName: "乡镇",
roadTypeName: "镇",
roadName: "弘惠路-学知路交叉口",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207331,
roadTude: 30.22136,
roadLong: 120.290936,
roadTeam: null,
roadType: null,
roadAddr: "机场城市路-通惠北路路口",
roadTeamName: null,
roadTypeName: null,
roadName: "机场城市路-通惠北路路口",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207342,
roadTude: 30.206022,
roadLong: 120.287385,
roadTeam: null,
roadType: null,
roadAddr: "建设四路-通惠路路口",
roadTeamName: null,
roadTypeName: null,
roadName: "建设四路-通惠路路口",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207345,
roadTude: 30.236867,
roadLong: 120.282971,
roadTeam: null,
roadType: null,
roadAddr: "鸿宁路-桥园路",
roadTeamName: null,
roadTypeName: null,
roadName: "鸿宁路-桥园路",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207348,
roadTude: 30.244784,
roadLong: 120.294536,
roadTeam: null,
roadType: null,
roadAddr: "弘惠路-学知路",
roadTeamName: null,
roadTypeName: null,
roadName: "弘惠路-学知路",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207349,
roadTude: 30.244784,
roadLong: 120.294536,
roadTeam: null,
roadType: null,
roadAddr: "鸿慧路-学知路路口",
roadTeamName: null,
roadTypeName: null,
roadName: "鸿慧路-学知路路口",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
{
roadId: 331181207352,
roadTude: 30.232579,
roadLong: 120.291775,
roadTeam: null,
roadType: null,
roadAddr: "鸿达路-通城高架路口",
roadTeamName: null,
roadTypeName: null,
roadName: "鸿达路-通城高架路口",
accidentNum: 1,
percentage: 0,
colorSign: 9,
colorType: "#B9D4B9",
accidentRoadType: null,
},
],
};
},
methods: {
handleClose() {
this.hidden = false;
},
handleOpen(markId) {
this.hidden = true;
},
},
};
</script>
<style lang='scss' src=''>
</style>
其中class根据需求写特定样式。
positionList是要描绘的点信息。
方法是点击点显示的弹出窗的关闭和开启
二、(封装的map.vue组件) 地区描边+对点信息进行描绘(map.vue组件,vscode编译者建议copy代码后下载Better Comments插件查看,效果更好)
<template>
<div class="my-container">
<div id="map-demo" ref="map" v-show="showFlag"></div>
<div class="bg-loading" v-show="!showFlag"></div>
</div>
</template>
<script>
//todo引入弹出窗信息
import createInfoWindow from "@/utils/amap";
export default {
props: ["pointList"],
data() {
return {
map: null,
infoWindow: null,
mapStyle: "amap://styles/33ac9bd25289b5229362e1f52b481f49", // 使用的自定义地图样式,可以根据需求显示/隐藏内容,改变主题颜色等,具体的使用下面会写
winInfo: [],
winTitle: "",
markList: [],
color: "#fff",
showFlag: false,
};
},
watch: {
//todo 监听不同的点信息进行视图更新
pointList(val) {
this.showFlag = false;
this.carGPSIP();
setTimeout(() => {
this.showFlag = true;
}, 1000);
},
},
mounted() {
this.carGPSIP();
},
methods: {
carGPSIP() {
let self = this;
this.map = new AMap.Map("map-demo", {
resizeEnable: true,
zoom: 9, //级别
center: [120.280798, 30.218143], //中心点坐标
viewMode: "2D", //使用3D视图
mapStyle: "amap://styles/darkblue", //todo 地图颜色变更,参考官方颜色darkblue\dark等
}); //初始化地图
var district = null;
var polygons = [];
//todo 行政区域边界描边
function drawBounds() {
//加载行政区划插件
if (!district) {
//实例化DistrictSearch
var opts = {
subdistrict: 0,
//获取边界不需要返回下级行政区
extensions: "all", //返回行政区边界坐标组等具体信息
level: "district", //查询行政级别为 市
};
district = new AMap.DistrictSearch(opts);
}
//行政区查询
district.search("萧山区", function (status, result) {
self.map.remove(polygons); //清除上次结果
polygons = [];
var bounds = result.districtList[0].boundaries;
if (bounds) {
for (var i = 0, l = bounds.length; i < l; i++) {
//生成行政区划polygon
var polygon = new AMap.Polygon({
strokeWeight: 1,
path: bounds[i],
fillOpacity: 0.4,
fillColor: "#004055",
strokeColor: "#0091ea",
});
polygons.push(polygon);
}
}
self.map.add(polygons);
self.map.setFitView(polygons); //视口自适应
});
}
//todo 遍历生成多个标记点
drawBounds();
if (this.pointList) {
for (var i = 0, marker; i < this.pointList.length; i++) {
//todo 渲染点信息,颜色可以自己根据数量判断,我们是让后端返回颜色
var marker = new AMap.Marker({
position: [this.pointList[i].roadLong, this.pointList[i].roadTude], //不同标记点的经纬度
map: self.map,
content: `<div style="background:${this.pointList[i].colorType}" class="pulse pulse1" >
<div class="ring"></div>
<div class="ring"></div>
<div class="ring"></div>
<p class="num"> ${this.pointList[i].accidentNum}</p>
</div>`,
});
//todo 弹窗信息
marker.title = ``;
marker.content = `
<div class="tip-box">
<div class="box-content">
<div class="box-left">
<div>
<h2>
${this.pointList[i].accidentNum}
</h2>
<span class="num-desc">
事故总数量
</span>
</div>
</div>
<div class="box-right">
<span>
位置大类:${
this.pointList[i].roadTeamName ||
this.pointList[i].sourceTeamName ||
""
}
</span>
<span>
位置小类:${
this.pointList[i].roadTypeName ||
this.pointList[i].sourceTypeName ||
""
}
</span>
<span>
详细地址:${
this.pointList[i].roadAddr || this.pointList[i].sourceAddr
}
</span>
</div>
</div>
</div>
`;
marker.footer = `
<div class="box-unit-name">
<span>
<span>${
this.pointList[i].roadName || this.pointList[i].sourceName
}</span>
</span>
<span class="el-icon-arrow-left title">
</span>
</div>`;
marker.markId =
this.pointList[i].roadId || this.pointList[i].sourceId;
marker.accNum = this.pointList[i].accidentNum;
marker.on("click", self.markerClick);
}
}
//实例化信息窗体
this.infoWindow = new AMap.InfoWindow({
isCustom: true, //使用自定义窗体
content: self.winInfo,
footer: self.winFooter,
offset: new AMap.Pixel(15, -55),
});
this.map.setFitView();
},
// 点标记点击事件
markerClick(e) {
if (e.target.accNum > 0) {
this.$emit("drawOpen", e.target.markId);
}
let self = this;
this.winInfo = e.target.content;
this.winTitle = e.target.title;
this.winFooter = e.target.footer;
// 设置窗体内容
this.infoWindow.setContent(
createInfoWindow.createInfoWindow(
self.winTitle,
self.winInfo,
self.winFooter,
// 关闭窗体
function () {
self.map.clearInfoWindow();
self.$emit("drawClose");
}
)
);
// 打开窗体
self.infoWindow.open(self.map, e.target.getPosition());
},
},
};
</script>
<style lang="scss" scoped>
#map-demo {
width: 100%;
height: 100%;
}
::v-deep .content-window-card {
position: relative;
box-shadow: none;
bottom: 0;
left: 0;
width: auto;
padding: 0;
p {
height: 2rem;
}
}
.custom-info {
position: relative;
/* border: solid 1px silver; */
}
::v-deep .info-top {
position: relative;
background: #fc4a3d;
height: 8px;
line-height: 20px;
font-size: 14px;
color: #fff;
padding: 0 10px;
span:nth-child(2) {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
img {
position: absolute;
top: 2px;
right: 5px;
transition-duration: 0.25s;
width: 15px;
}
img:hover {
box-shadow: 0px 0px 5px #000;
}
}
::v-deep .info-middle {
background: #100b0d !important;
font-size: 12px;
padding: 0px 6px;
line-height: 20px;
color: #fff;
img {
float: left;
margin-right: 6px;
}
.tip-box {
display: flex;
flex-direction: column;
padding: 1px 0 20px 0;
.box-content {
height: 120px;
display: flex;
justify-content: space-around;
.box-left {
width: 150px;
position: relative;
h2 {
font-size: 60px;
height: 0px;
line-height: 0px;
color: #f94a37;
margin-left: 20px;
font-weight: 900;
}
.num-desc {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 16px;
color: #666;
font-weight: 800;
}
}
.box-right {
width: 200px;
display: flex;
flex-direction: column;
span {
max-width: 190px;
max-height: 50px;
padding: 0 5px;
display: inline-block;
height: 25px;
line-height: 25px;
}
span:nth-child(1) {
}
span:nth-child(2) {
// width: 100px;
color: #fb473f;
background: #4d272a;
}
span:nth-child(3) {
height: 50px;
color: rgba(255, 255, 255, 0.8);
font-size: 14px;
overflow: hidden;
/* text-overflow: ellipsis; */
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
}
}
}
}
::v-deep .info-bottom {
background: #6ad345;
width: 100%;
text-align: center;
.box-unit-name {
cursor: pointer;
position: absolute;
left: 0;
bottom: -30px;
width: 200px;
font-size: 16px;
height: 30px;
line-height: 30px;
background: #4d272a;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
span:nth-child(1) {
margin-left: 5px;
color: rgba(255, 255, 255, 0.8);
}
span:nth-child(2) {
color: #c54141;
margin-left: 20px;
}
.title {
font-size: 18px;
font-weight: 900;
}
}
}
::v-deep .pulse {
height: 20px;
width: 20px;
border-radius: 100%;
position: relative;
.num {
position: absolute;
width: 20px;
height: 20px;
top: -15px;
text-align: center;
color: #4d272a;
font-weight: 500;
font-size: 16px;
z-index: 999;
}
.ring {
position: absolute;
background-color: inherit;
height: 100%;
width: 100%;
border-radius: 100%;
opacity: 0.8;
-webkit-animation: pulsing 2s ease-out infinite;
animation: pulsing 2s ease-out infinite;
}
.ring:nth-of-type(1) {
-webkit-animation-delay: -0.5s;
animation-delay: -0.5s;
}
.ring:nth-of-type(2) {
-webkit-animation-delay: -1s;
animation-delay: -1s;
}
.ring:nth-of-type(3) {
-webkit-animation-delay: -1.5s;
animation-delay: -1.5s;
}
@-webkit-keyframes pulsing {
100% {
transform: scale(1.75);
opacity: 0;
}
}
@keyframes pulsing {
100% {
transform: scale(1.75);
opacity: 0;
}
}
}
::v-deep .pulse1 {
position: relative;
}
::v-deep .pulse2 {
position: relative;
}
::v-deep .pulse3 {
position: relative;
}
::v-deep .pulse4 {
position: relative;
}
::v-deep .pulse5 {
position: relative;
}
.bg-loading {
background: #000;
width: 100%;
height: 100%;
}
</style>
amap.js自定义窗口信息
function createInfoWindow(title, content, footer, callback) {
var info = document.createElement("div");
info.className = "custom-info input-card content-window-card";
//可以通过下面的方式修改自定义窗体的宽高
info.style.width = "400px";
// 定义顶部标题
var top = document.createElement("div");
var closeX = document.createElement("img");
top.className = "info-top";
closeX.onclick = callback;
top.innerHTML = title;
top.appendChild(closeX);
info.appendChild(top);
// 定义中部内容
var middle = document.createElement("div");
middle.className = "info-middle";
middle.style.backgroundColor = "white";
middle.innerHTML = content;
info.appendChild(middle);
// 定义底部内容
var bottom = document.createElement("div");
bottom.className = "info-bottom";
bottom.onclick = callback;
bottom.innerHTML = footer;
info.appendChild(bottom);
return info;
}
export default {
createInfoWindow,
};
有问题请留言~或联系作者,会一一解答。
前端之家企鹅群:610408494