openlayers增加移动功能
移动说明
- 支持移动点或线元素
- 移动时虚化原来的点(利用
translatestart和translateend事件,在拖拽开始时创建要素副本(虚影),拖拽结束时移除副本。)
思路:
移动的元素放到一个图层(move_vector_source)里去维护
效果图

示例代码
places.json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "昆明"
},
"geometry": {
"type": "Point",
"coordinates": [
102.7128,
25.0406
]
}
},
{
"type": "Feature",
"properties": {
"name": "贵阳"
},
"geometry": {
"type": "Point",
"coordinates": [
106.7139,
26.5784
]
}
},
{
"type": "Feature",
"properties": {
"name": "广州"
},
"geometry": {
"type": "Point",
"coordinates": [
113.280637,
23.125178
]
}
}
]
}
index.vue
<template>
<div class="app">
<div class="box">
<h3>初始化图形数据</h3>
<div class="operate-btns">
<div
v-for="(item, index) in graph_btn_list"
:key="index"
:class="['btn-item']"
@click="onGraphBtnClick(item)"
>
{{ item.name }}
</div>
</div>
</div>
<div class="box">
<h3>移动功能</h3>
<div class="operate-btns">
<div
v-for="(item, index) in move_btn_list"
:key="index"
:class="['btn-item']"
@click="onMoveBtnClick(item)"
>
{{ item.name }}
</div>
</div>
</div>
<div class="map-container" ref="map_ref"></div>
</div>
</template>
<script>
import * as test_ol from "ol";
import GeoJSON from "ol/format/GeoJSON";
import { Map, View, Overlay, Feature } from "ol";
import { XYZ, Vector as SourceVector } from "ol/source";
import { Vector as LayerVector, Tile } from "ol/layer";
import { Text, Fill, Stroke, Circle, Style, Icon } from "ol/style";
import { toLonLat, fromLonLat } from "ol/proj";
import { Draw, Translate } from "ol/interaction";
import { Point, LineString, Polygon } from "ol/geom";
// console.log("Translate ==>", Translate);
const ol = {
source: {
Vector: SourceVector,
XYZ,
},
format: {
GeoJSON,
},
style: {
Text,
Fill,
Stroke,
Circle,
Style,
Icon,
},
layer: {
Vector: LayerVector,
Tile,
},
proj: {
toLonLat,
fromLonLat,
},
interaction: {
Draw,
Translate,
},
Overlay,
Feature,
geom: {
Point,
LineString,
Polygon,
},
};
export default {
data() {
return {
map: null,
zoom: 6,
// 控制标注显示的最小缩放级别
label_zoom_level: 5,
draw_btn_list: [
{
name: "点",
alias: "point",
type: "Point",
},
{
name: "线",
alias: "line",
type: "LineString",
},
{
name: "多边形",
alias: "polygon",
type: "Polygon",
},
{
name: "清除所有绘制",
alias: "clear",
type: "Clear",
},
],
active_draw_btn_index: -1,
// 绘制的实例
draw_instance: null,
// 存放矢量数据源和图层实例
draw_vector_source: null,
graph_btn_list: [
{
name: "点",
alias: "point",
},
{
name: "线",
alias: "line",
},
{
name: "多边形",
alias: "polygon",
},
],
// 初始化图形的数据源和图层
init_vector_source: null, // 添加初始化图形数据源
init_vector_layer: null, // 添加初始化图形图层
move_btn_list: [
{
name: "点",
alias: "point",
},
{
name: "线",
alias: "line",
},
],
// 移动图层
move_vector_source: null,
move_vector_layer: null,
moveFeature: null,
};
},
mounted() {
this.initMap();
},
methods: {
initMap() {
const that = this;
// 创建卫星图层
const satelliteLayer = new ol.layer.Tile({
source: new ol.source.XYZ({
// 这里使用高德影像图层
url: "http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
maxZoom: 18,
}),
opacity: 0.9,
zIndex: 1,
});
const map = new Map({
target: this.$refs.map_ref,
layers: [satelliteLayer],
view: new View({
// 113.24269373938198,23.1299597462194
center: ol.proj.fromLonLat([113.24269373938198, 23.1299597462194]),
zoom: this.zoom,
}),
});
this.map = map;
// 初始化绘制图层
var vectorSource = new ol.source.Vector({
url: "/assets/json/places.json",
format: new ol.format.GeoJSON(),
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: function (feature) {
// 为每个地名要素设置样式,包括点样式和文字标签
var textStyle = new ol.style.Text({
text: feature.get("name"), // 获取地名属性
font: "bold 12px Arial",
fill: new ol.style.Fill({
color: "#fff",
}),
stroke: new ol.style.Stroke({
color: "#000",
width: 2,
}),
offsetY: -15, // 文字相对于点的垂直偏移
});
var pointStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: "#ff0000",
}),
stroke: new ol.style.Stroke({
color: "#ffffff",
width: 2,
}),
}),
text: textStyle,
});
return pointStyle;
},
zIndex: 999,
});
map.addLayer(vectorLayer);
// 初始化图形图层
const init_vector_source = new ol.source.Vector();
const init_vector_layer = new ol.layer.Vector({
source: init_vector_source,
// 动态设置标记
style: function (feature) {
// 检查几何类型
const geometry = feature.getGeometry();
const graph = geometry.getType();
if (graph === "Point") {
// 获取要素的图片URL
const img_config = feature.get("img_config");
// 如果有图片URL则使用图标,否则使用默认的圆形
if (img_config.url || img_config.scale) {
return new ol.style.Style({
image: new ol.style.Icon({
src: img_config.url,
scale: img_config.scale || 0.8,
anchor: [0.5, 1], // 图标锚点设置在底部中心
}),
});
} else {
// 默认圆形标记
return new ol.style.Style({
image: new ol.style.Circle({
radius: 8,
fill: new ol.style.Fill({ color: "#ff0000" }),
stroke: new ol.style.Stroke({ color: "white", width: 2 }),
}),
});
}
} else if (graph == "LineString") {
// 获取线配置
const line_config = Object.assign(
{
strokeColor: "#f00",
width: 3,
},
feature.get("line_config")
);
const stroke = new ol.style.Stroke({
color: line_config.strokeColor,
width: line_config.width || 3,
});
return new ol.style.Style({
fill: new ol.style.Fill({ color: "rgba(255, 0, 0, 0.3)" }),
stroke,
});
} else if (graph == "Polygon") {
// 获取多边形配置
const polygon_config = Object.assign(
{
bgColor: "rgba(255, 0, 0, 0.3)",
strokeColor: "#f00",
width: 3,
},
feature.get("polygon_config")
);
const stroke = new ol.style.Stroke({
color: polygon_config.strokeColor,
width: polygon_config.width || 3,
});
const fill = new ol.style.Fill({ color: polygon_config.bgColor });
return new ol.style.Style({
fill,
stroke,
});
} else {
// 线和多边形保持原有样式
return new ol.style.Style({
fill: new ol.style.Fill({ color: "rgba(255, 0, 0, 0.3)" }),
stroke: new ol.style.Stroke({ color: "#ff0000", width: 3 }),
});
}
},
zIndex: 998,
});
this.init_vector_source = init_vector_source;
this.init_vector_layer = init_vector_layer;
map.addLayer(init_vector_layer);
map.on("click", function (evt) {
var coordinate = evt.coordinate;
// 这里只是模拟,实际需根据点击位置获取街景资源并展示
// console.log("点击位置经纬度:" + ol.proj.toLonLat(coordinate));
// 检查点击位置是否有要素
map.forEachFeatureAtPixel(evt.pixel, function (feature) {
// 核心过滤逻辑:排除临时要素(只有带 isFinished: true 的才是正式要素)
// 如果点击的是多边形
// if (feature.get("isFinished") && feature.getGeometry() instanceof ol.geom.Polygon) {
// showPopup(evt.coordinate, feature);
// return true; // 停止遍历
// }
if (feature.get("isFinished")) {
// console.log("ok");
that.showPopup(evt.coordinate, feature);
return true; // 停止遍历
}
});
});
function updateLabelsVisibility() {
var currentZoom = map.getView().getZoom();
that.zoom = currentZoom;
vectorLayer.setVisible(currentZoom >= that.label_zoom_level);
}
updateLabelsVisibility();
map.on("moveend", updateLabelsVisibility);
// 绘制
// 创建矢量数据源和图层
const vector_source = new ol.source.Vector();
// 创建矢量图层并关联数据源
const vector_layer = new ol.layer.Vector({
source: vector_source,
style: new ol.style.Style({
fill: new ol.style.Fill({ color: "rgba(0, 153, 255, 0.2)" }),
stroke: new ol.style.Stroke({ color: "#0099ff", width: 2 }),
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({ color: "#0099ff" }),
stroke: new ol.style.Stroke({ color: "white", width: 1 }),
}),
}),
zIndex: 999,
});
this.draw_vector_source = vector_source;
map.addLayer(vector_layer);
// 创建移动图层
var moveVectorSource = new ol.source.Vector();
var moveVectorLayer = new ol.layer.Vector({
source: moveVectorSource,
style: function (feature) {
// 为每个地名要素设置样式,包括点样式和文字标签
var textStyle = new ol.style.Text({
text: feature.get("name"), // 获取地名属性
font: "bold 12px Arial",
fill: new ol.style.Fill({
color: "#fff",
}),
stroke: new ol.style.Stroke({
color: "#000",
width: 2,
}),
offsetY: -15, // 文字相对于点的垂直偏移
});
var pointStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: "#ff0000",
}),
stroke: new ol.style.Stroke({
color: "#ffffff",
width: 2,
}),
}),
text: textStyle,
});
return pointStyle;
},
zIndex: 9999,
});
this.move_vector_source = moveVectorSource;
this.move_vector_layer = moveVectorLayer;
map.addLayer(moveVectorLayer);
// // 创建拖拽交互
const translateInteraction = new ol.interaction.Translate({
// 只允许拖拽当前数据源中的要素
// sources: [moveVectorSource],
// sources: [this.move_vector_source],
// 可选:通过filter限制可拖拽的要素类型
filter: (feature, layer) => {
// 只允许点要素被拖拽
// return feature.getGeometry() instanceof ol.geom.Point;
return layer === this.move_vector_layer && feature === this.moveFeature;
},
});
// // 添加拖拽交互到地图
map.addInteraction(translateInteraction);
// 存储虚影要素的变量
let ghostFeature = null;
// 定义虚化虚影的样式
const ghostPointStyle = new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
fill: new ol.style.Fill({ color: "rgba(100, 100, 100, 0.3)" }), // 半透明灰色
stroke: new ol.style.Stroke({ color: "rgba(255, 255, 255, 0.5)", width: 1 }),
}),
});
const ghostLineStyle = new ol.style.Style({
stroke: new ol.style.Stroke({
color: "rgba(0, 0, 0, 0.8)", // 半透明灰色
width: 2,
lineDash: [5, 5], // 虚线样式
}),
});
// 拖拽开始时创建虚影
translateInteraction.on("translatestart", (event) => {
const feature = event.features.item(0);
const geometry = feature.getGeometry();
const geometry_type = geometry.getType();
// 只处理点的拖拽
if (feature === this.moveFeature) {
// 复制当前要素作为虚影
ghostFeature = this.moveFeature.clone();
if (geometry_type == "Point") {
// 设置虚影样式
ghostFeature.setStyle(ghostPointStyle);
} else if (geometry_type == "LineString") {
// 设置虚影样式(半透明)
ghostFeature.setStyle(ghostLineStyle);
}
// // 添加虚影到数据源
this.move_vector_source.addFeature(ghostFeature);
}
});
// // 监听拖拽结束事件
translateInteraction.on("translateend", (event) => {
const feature = event.features.item(0); // 获取被拖拽的要素
if (ghostFeature) {
// 从数据源中移除虚影
this.move_vector_source.removeFeature(ghostFeature);
ghostFeature = null;
}
// 获取新坐标(经纬度)
const geometry = feature.getGeometry();
const geometry_type = geometry.getType();
if (geometry_type === "LineString") {
// 线的坐标数组
const lineCoords = geometry
.getCoordinates()
.map((coord) => ol.proj.toLonLat(coord))
.map((coord) => `[${coord[0].toFixed(6)}, ${coord[1].toFixed(6)}]`);
console.warn("拖拽结束:", `新路径点: [\n ${lineCoords.join(",\n ")}\n]`);
} else if (geometry_type == "Point") {
// 点的情况
const coords = ol.proj.toLonLat(feature.getGeometry().getCoordinates());
console.warn(
"拖拽结束:",
`新坐标:经度 ${coords[0].toFixed(6)}, 纬度 ${coords[1].toFixed(6)}`
);
}
});
},
onGraphBtnClick(item) {
this.initGraph(item.alias);
},
initGraph(type) {
// 根据不同类型创建对应的几何图形
switch (type) {
case "point":
this.initPointGraph();
break;
case "line":
this.initLineGraph();
break;
case "polygon":
this.initPolygonGraph();
break;
}
},
initPointGraph() {
const test_point_data = [
{
longitude: 113.746262,
latitude: 23.050419,
name: "黄花风铃木林",
image_url: require("@/assets/imgs/1.png"),
image_scale: 0.2,
},
{
longitude: 113.89061,
latitude: 22.882174,
name: "松山湖科学公园",
},
];
test_point_data.forEach((item) => {
const coord = ol.proj.fromLonLat([item.longitude, item.latitude]);
const pointGeometry = new ol.geom.Point(coord);
const feature = new ol.Feature({
geometry: pointGeometry,
test_data: {
name: "初始化点",
age: 25,
friend: ["张三", "李四"],
},
// 添加图片URL到要素属性中
img_config: {
url: item.image_url,
scale: item.image_scale,
},
// imageUrl: item.imageUrl,
});
feature.set("isFinished", true);
// 将要素添加到绘制图层的数据源中
this.init_vector_source.addFeature(feature);
});
},
initLineGraph() {
const test_line_data = [
{
name: "线条测试-1",
list: [
{
longitude: 113.746262,
latitude: 23.050419,
name: "黄花风铃木林",
},
{
longitude: 113.89061,
latitude: 22.882174,
name: "松山湖科学公园",
},
],
line_config: {
strokeColor: "#0f0",
},
},
];
test_line_data.forEach((item) => {
const line_arr = [];
item.list.forEach((l) => {
const coord = ol.proj.fromLonLat([l.longitude, l.latitude]);
line_arr.push(coord);
});
const lineGeometry = new ol.geom.LineString(line_arr);
const feature = new ol.Feature({
geometry: lineGeometry,
test_data: {
name: "初始化线",
age: 25,
friend: ["张三", "李四"],
},
line_config: item.line_config,
});
feature.set("isFinished", true);
// 将要素添加到绘制图层的数据源中
this.init_vector_source.addFeature(feature);
});
},
initPolygonGraph() {
const test_polygon_data = [
{
name: "多边形测试-1",
list: [
{
longitude: 113.746262,
latitude: 23.050419,
name: "黄花风铃木林",
},
{
longitude: 113.89061,
latitude: 22.882174,
name: "松山湖科学公园",
},
{
longitude: 113.89061,
latitude: 22.891999,
name: "游船码头",
},
{
longitude: 113.746262,
latitude: 23.050419,
name: "黄花风铃木林",
},
],
polygon_config: {
bgColor: "#00f",
strokeColor: "#ff0",
},
},
];
test_polygon_data.forEach((item) => {
const polygon_arr = [];
item.list.forEach((l) => {
const coord = ol.proj.fromLonLat([l.longitude, l.latitude]);
polygon_arr.push(coord);
});
const polygonGeometry = new ol.geom.Polygon([polygon_arr]);
const feature = new ol.Feature({
geometry: polygonGeometry,
test_data: {
name: "初始化多边形",
age: 25,
friend: ["张三", "李四"],
},
polygon_config: item.polygon_config,
});
feature.set("isFinished", true);
// 将要素添加到绘制图层的数据源中
this.init_vector_source.addFeature(feature);
});
},
onMoveBtnClick({ alias }) {
if (alias == "point") {
// 创建一个点要素
const pointFeature = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([114.280637, 23.125178]) // 点的初始位置
),
name: "可移动点",
});
const pointFeature2 = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([116.280637, 23.125178]) // 点的初始位置
),
name: "不可移动点",
});
// 设置点的样式
pointFeature.setStyle(
new ol.style.Style({
image: new ol.style.Circle({
radius: 8,
fill: new ol.style.Fill({ color: "red" }),
stroke: new ol.style.Stroke({ color: "white", width: 2 }),
}),
text: new ol.style.Text({
text: "移动点",
fill: new ol.style.Fill({
color: "yellow",
}),
}),
})
);
this.move_vector_source.addFeature(pointFeature);
this.move_vector_source.addFeature(pointFeature2);
// 声明移动的对象
this.moveFeature = pointFeature;
} else if (alias == "line") {
// 创建线的坐标点数组
const lineCoordinates = [
ol.proj.fromLonLat([113.746262, 23.050419]), // 起点
ol.proj.fromLonLat([113.89061, 22.882174]), // 中间点
ol.proj.fromLonLat([114.280637, 23.125178]), // 终点
];
// 创建线几何对象
const lineGeometry = new ol.geom.LineString(lineCoordinates);
// 创建线要素
const lineFeature = new ol.Feature({
geometry: lineGeometry,
name: "示例线",
description: "这是一条测试线",
});
// 设置线的样式
lineFeature.setStyle(
new ol.style.Style({
stroke: new ol.style.Stroke({
color: "#00ff00", // 线条颜色 (绿色)
width: 3, // 线条宽度
lineDash: [5, 5], // 虚线样式 (可选)
}),
// 可选:在线的端点或中间点添加图标
// image: new ol.style.Circle({
// radius: 5,
// fill: new ol.style.Fill({ color: '#ff0000' })
// })
})
);
// 将线要素添加到数据源中
this.move_vector_source.addFeature(lineFeature);
this.moveFeature = lineFeature;
}
},
},
};
</script>
<style lang="less" scoped>
.app {
padding: 10px;
}
.box {
margin-bottom: 10px;
background-color: #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.2);
padding: 10px;
border-radius: 5px;
h3 {
margin-bottom: 10px;
}
}
.map-container {
height: calc(100vh - 50px);
// background-color: orange;
}
.operate-btns {
display: flex;
.btn-item {
margin: 0 10px 10px 0;
padding: 8px 12px;
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 3px;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
&:hover {
background-color: #e0e0e0;
}
&.active {
background-color: #e0e0e0;
}
}
}
/* 信息弹窗样式 */
.popup {
position: absolute;
background-color: white;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
pointer-events: auto;
display: none;
.popup-content {
min-width: 200px;
}
.popup-closer {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
font-size: 16px;
}
}
</style>

浙公网安备 33010602011771号