Popup 弹窗
TIP
鼠标单击标注点,弹出 Popup 标注。
代码如下:
点我查看代码
vue
<template>
<div id="map">
<div id="popup" class="ol-popup">
<a
href="javascript:void(0)"
id="popup-closer"
class="ol-popup-closer"
></a>
<div id="popup-content"></div>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, onBeforeUnmount } from "vue";
import { Map, View, Feature } from "ol";
import { Tile as TileLayer, Vector as VectorLayer } from "ol/layer";
import { defaults, FullScreen } from "ol/control";
import { XYZ, Vector as VectorSource } from "ol/source";
import { Point } from "ol/geom";
import { ATTRIBUTIONS, SHENZHEN, MAPURL } from "../../../constants";
import {
createLabelStyle,
createPopup,
featuerInfo,
addFeatrueInfo,
} from "./tools";
const raster = new TileLayer({
source: new XYZ({
attributions: ATTRIBUTIONS,
url: MAPURL,
maxZoom: 20,
}),
});
let map: Map | null = null;
const initMap = () => {
map = new Map({
//初始化map
target: "map",
//地图容器中加载的图层
layers: [
//加载瓦片图层数据
raster,
],
view: new View({
projection: "EPSG:4326", // 坐标系,有EPSG:4326和EPSG:3 857
center: SHENZHEN, // 深圳坐标
//地图初始显示级别
zoom: 5,
}),
//加载控件到地图容器中
controls: defaults().extend([
new FullScreen(), //加载全屏显示控件(目前支持非IE内核浏览器)
]),
});
//实例化Vector要素,通过矢量图层添加到地图容器中
const iconFeature = new Feature({
geometry: new Point(SHENZHEN),
//名称属性
name: "深圳市",
});
iconFeature.setStyle(createLabelStyle(iconFeature));
//矢量标注的数据源
const vectorSource = new VectorSource({
features: [iconFeature],
});
//矢量标注图层
const vectorLayer = new VectorLayer({
source: vectorSource,
});
map.addLayer(vectorLayer);
/**
* 实现popup的html元素
*/
const container = document.getElementById("popup");
const content = document.getElementById("popup-content");
const closer = document.getElementById("popup-closer");
if (container && content && closer) {
/**
* 在地图容器中创建一个Overlay
*/
const popup = createPopup(container);
map.addOverlay(popup);
/**
* 添加关闭按钮的单击事件(隐藏popup)
* @return {boolean} Don't follow the href.
*/
closer.onclick = () => {
//未定义popup位置
popup.setPosition(undefined);
//失去焦点
closer.blur();
};
/**
* 为map添加点击事件监听,渲染弹出popup
*/
map.on("click", (evt) => {
if (map === null) return;
//判断当前单击处是否有要素,捕获到要素时弹出popup
const feature = map.forEachFeatureAtPixel(
evt.pixel,
(feature) => feature
);
if (feature) {
//清空popup的内容容器
content.innerHTML = "";
//在popup中加载当前要素的具体信息
addFeatrueInfo({ info: featuerInfo, content });
if (popup.getPosition() == undefined) {
//设置popup的位置
popup.setPosition(featuerInfo.geo);
}
}
});
/**
* 为map添加鼠标移动事件监听,当指向标注时改变鼠标光标状态
*/
map.on("pointermove", (e) => {
if (map === null) return;
const pixel = map.getEventPixel(e.originalEvent);
const hit = map.hasFeatureAtPixel(pixel);
map.getTargetElement().style.cursor = hit ? "pointer" : "";
});
}
};
onMounted(() => {
initMap();
});
onBeforeUnmount(() => {
if (map) {
map.dispose();
map = null;
}
});
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#map {
position: relative;
height: 650px;
}
.ol-popup {
position: absolute;
width: 200px;
background-color: white;
-webkit-filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
filter: drop-shadow(0 1px 4px rgba(0, 0, 0, 0.2));
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 45px;
left: -50px;
color: #000;
}
.ol-popup:after,
.ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: "✖";
}
#popup-content {
font-size: 14px;
font-family: "微软雅黑";
}
#popup-content .markerInfo {
font-weight: bold;
}
:deep(.markerImg) {
width: 210px;
}
</style>
tools 代码如下:
点我查看代码
ts
import { Style, Icon, Text, Fill, Stroke } from 'ol/style'
import { Overlay, Feature } from 'ol'
import { SHENZHEN, AUTHOR_INFO } from '../../../constants'
export interface FeatuerInfo {
geo: number[];
att: {
title: string;
titleURL: string;
text: string;
imgURL: string;
};
}
//示例标注点北京市的信息对象
export const featuerInfo: FeatuerInfo = {
geo: SHENZHEN,
att: {
//标注信息的标题内容
title: AUTHOR_INFO.NAME,
//标注详细信息链接
titleURL: AUTHOR_INFO.JUEJIN,
//标注内容简介
text: `
<br/>
年龄:${AUTHOR_INFO.AGE}<br/>
性别:${AUTHOR_INFO.SEX}<br/>
目前居住地:${AUTHOR_INFO.LOCATION}<br/>
QQ:${AUTHOR_INFO.QQ}<br/>
WX:${AUTHOR_INFO.WX}<br/>
<br/>
`,
//标注的图片
imgURL: AUTHOR_INFO.AVATAR,
},
}
export const createPopup = (container: HTMLElement): Overlay =>
new Overlay(
/** @type {olx.OverlayOptions} */
({
//要转换成overlay的HTML元素
element: container,
//当前窗口可见
autoPan: true,
//Popup放置的位置
positioning: 'bottom-center',
//是否应该停止事件传播到地图窗口
stopEvent: false,
autoPanAnimation: {
//当Popup超出地图边界时,为了Popup全部可见,地图移动的速度
duration: 250,
},
}),
)
/**
* 创建矢量标注样式函数,设置image为图标ol.style.Icon
* @param {ol.Feature} feature 要素
*/
export const createLabelStyle = (feature: Feature): Style =>
new Style({
image: new Icon(
/** @type {olx.style.IconOptions} */
({
//设置图标点
anchor: [0.5, 60],
//图标起点
anchorOrigin: 'top-right',
//指定x值为图标点的x值
anchorXUnits: 'fraction',
//指定Y值为像素的值
anchorYUnits: 'pixels',
//偏移
offsetOrigin: 'top-right',
// offset:[0,10],
//图标缩放比例
scale: 0.4,
//透明度
opacity: 0.75,
//图标的url
src: '/image/blue.png',
}),
),
text: new Text({
//位置
textAlign: 'center',
//基准线
textBaseline: 'middle',
//文字样式
font: 'normal 14px 微软雅黑',
//文本内容
text: feature.get('name'),
//文本填充样式(即文字颜色)
fill: new Fill({ color: '#aa3300' }),
stroke: new Stroke({ color: '#ffcc33', width: 2 }),
}),
})
/**
* 动态创建popup的具体内容
* @param {Object} info 弹框信息
* @param {Element} content 弹框内容节点
*/
export const addFeatrueInfo = ({ info, content }: { info: FeatuerInfo, content: HTMLElement }) => {
//新增a元素
const elementA = document.createElement('a')
elementA.className = 'markerInfo'
elementA.href = info.att.titleURL
setInnerText(elementA, info.att.title)
// 新建的div元素添加a子节点
content.appendChild(elementA)
//新增div元素
const elementDiv = document.createElement('div')
elementDiv.className = 'markerText'
elementDiv.innerHTML = info.att.text
// 为content添加div子节点
content.appendChild(elementDiv)
//新增img元素
const elementImg = document.createElement('img')
elementImg.className = 'markerImg'
elementImg.src = info.att.imgURL
// 为content添加img子节点
content.appendChild(elementImg)
}
/**
* 动态设置元素文本内容(兼容)
*/
function setInnerText(element: HTMLElement, text: string) {
if (typeof element.textContent == 'string') {
element.textContent = text
} else {
element.innerText = text
}
}