前言:

        vue3+openlayers5实现地图的展示与图片渲染拖拽等,之前写过vue2版本的,这里说下vue3版本的

实现效果:

实现代码:

1、安装插件

pnpm i ol ol-ext
推荐版本
"ol": "^6.15.1",
"ol-ext": "^4.0.36",

2、不同地图瓦片地址

https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}
https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=8&x={x}&y={y}&z={z}

3、主要代码

<template>
  <div id="map" class="map" ref="mapDom"></div>
</template>

<script setup>
import "ol/ol.css";
import View from "ol/View";
import Map from "ol/Map";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import { Vector as VectorSource } from "ol/source";
import { Feature } from "ol";
import { Vector as VectorLayer } from "ol/layer";
import { Polygon } from "ol/geom";
import ExtTransform from 'ol-ext/interaction/Transform';
import ImageLayer from 'ol/layer/Image';
import Static from 'ol/source/ImageStatic';
import Projection from 'ol/proj/Projection';
import { Style, Fill, Stroke } from "ol/style";
import { ref, onMounted, onUnmounted } from 'vue';

// 使用 ref 定义响应式变量
const map = ref(null);
const switchVal = ref(false);
const center = ref([116.39702518856394, 39.918590567855425]);
const centerSize = ref(11.5);
const projection = ref('EPSG:4326');
const polygon_vectorSource = ref(new VectorSource());
const pointCenter = ref([116.2925480012459, 39.87353495433062]);
const Coordinate_arr = ref([
  [116.33272829555995, 39.893308222194946], // 右上
  [116.33272829555995, 39.85376168646629],  // 右下
  [116.25236770693185, 39.85376168646629],  // 左下
  [116.25236770693185, 39.893308222194946], // 左上
]);
const Pixel_arr = ref([]);
const imgStatic = ref(null);
const mapDom = ref(null); // 修正引用名称

// 初始化地图
const initMap = () => {
  if (!mapDom.value) return;

  // 渲染地图
  const layers = [
    new TileLayer({
      source: new XYZ({
        url: "https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}",
      }),
    }),
  ];

  map.value = new Map({
    layers: layers,
    target: mapDom.value, // 使用 ref 而不是字符串 ID
    view: new View({
      center: center.value,
      projection: projection.value,
      zoom: centerSize.value,
      maxZoom: 17,
      minZoom: 8,
    }),
  });

  map.value.on("moveend", function(evt) {
    const zoom = map.value.getView().getZoom();
    console.log("zoom = " + zoom);

    if (Pixel_arr.value.length > 0 && !switchVal.value) {
      let arr = changeGetCoordinateFromPixel(Pixel_arr.value);
      let left_lon = arr[2][0];
      let bottom_lat = arr[2][1];
      let right_lon = arr[0][0];
      let top_lat = arr[0][1];
      createImg(left_lon, bottom_lat, right_lon, top_lat);
      createPolygon();
    }
  });

  // 图片值
  setTimeout(() => {
    changeGetPixelFromCoordinate(Coordinate_arr.value);
  }, 800);
};

// 创建多边形
const createPolygon = () => {
  // 清除现有的
  polygon_vectorSource.value.clear();

  let polygon = new Feature({
    geometry: new Polygon([Coordinate_arr.value]),
  });

  polygon.setStyle(new Style({
    fill: new Fill({
      color: 'rgba(255, 255, 255, 0.2)'
    }),
    stroke: new Stroke({
      width: 4,
      color: [255, 0, 0, 1],
    }),
  }));

  polygon_vectorSource.value.addFeature(polygon);

  // 检查是否已经添加了图层,避免重复添加
  let vectorLayer = map.value.getLayers().getArray().find(layer =>
      layer instanceof VectorLayer && layer.getSource() === polygon_vectorSource.value
  );

  if (!vectorLayer) {
    map.value.addLayer(new VectorLayer({
      source: polygon_vectorSource.value,
    }));
  }
};

// 切换坐标,实际坐标 -> 界面坐标
const changeGetPixelFromCoordinate = (Coordinate_arr) => {
  let Pixel_arr = [];
  Pixel_arr.push(map.value.getPixelFromCoordinate(Coordinate_arr[0]));
  Pixel_arr.push(map.value.getPixelFromCoordinate(Coordinate_arr[1]));
  Pixel_arr.push(map.value.getPixelFromCoordinate(Coordinate_arr[2]));
  Pixel_arr.push(map.value.getPixelFromCoordinate(Coordinate_arr[3]));
  Pixel_arr.value = Pixel_arr;
  return Pixel_arr;
};

// 切换坐标,界面坐标 -> 实际坐标
const changeGetCoordinateFromPixel = (Pixel_arr) => {
  let Coordinate_arr = [];
  Coordinate_arr.push(map.value.getCoordinateFromPixel(Pixel_arr[0]));
  Coordinate_arr.push(map.value.getCoordinateFromPixel(Pixel_arr[1]));
  Coordinate_arr.push(map.value.getCoordinateFromPixel(Pixel_arr[2]));
  Coordinate_arr.push(map.value.getCoordinateFromPixel(Pixel_arr[3]));
  Coordinate_arr.value = Coordinate_arr;
  return Coordinate_arr;
};

// 创建图片图层
const createImg = (left_lon, bottom_lat, right_lon, top_lat, arr) => {
  if (!left_lon || !bottom_lat || !right_lon || !top_lat) {
    return;
  }

  if (arr) {
    let Coordinate_arr = [];
    Coordinate_arr[0] = [arr[0], arr[1]];
    Coordinate_arr[1] = [arr[2], arr[3]];
    Coordinate_arr[2] = [arr[4], arr[5]];
    Coordinate_arr[3] = [arr[6], arr[7]];
    let newArr = changeGetPixelFromCoordinate(Coordinate_arr);
    console.log(newArr);
  }

  var extent = [left_lon, bottom_lat, right_lon, top_lat];
  console.log('图片范围:', extent);

  var customProjection = new Projection({
    code: 'xkcd-image',
    units: 'degrees', // 使用 degrees 而不是 pixels
    extent: extent,
  });

  // 移除现有图片图层
  if (imgStatic.value) {
    map.value.removeLayer(imgStatic.value);
    imgStatic.value = null;
  }

  // 创建新的图片图层
  imgStatic.value = new ImageLayer({
    source: new Static({
      url: 'https://imgs.xkcd.com/comics/online_communities.png',
      projection: customProjection,
      imageExtent: extent,
    }),
  });

  map.value.addLayer(imgStatic.value);
};

// 操作事件
const onEdit = () => {
  const transform = new ExtTransform({
    enableRotatedTransform: false,
    hitTolerance: 2,
    translate: true, // 拖拽
    stretch: false, // 拉伸
    scale: true, // 缩放
    rotate: true, // 旋转
    translateFeature: false,
    noFlip: true,
  });

  map.value.addInteraction(transform);

  // 开始事件
  transform.on(['rotatestart', 'translatestart'], function(e) {
    console.log('开始变换', e);
  });

  // 旋转
  transform.on('rotating', (e) => {
    const angle = ((e.angle * 180 / Math.PI - 180) % 360 + 180).toFixed(2);
    console.log("旋转角度: " + angle);

    updateImageFromFeature(e.feature);
  });

  // 移动
  transform.on('translating', (e) => {
    console.log('移动中');
    updateImageFromFeature(e.feature);
  });

  // 缩放
  transform.on('scaling', (e) => {
    console.log('缩放中');
    updateImageFromFeature(e.feature);
  });

  // 事件结束
  transform.on(['rotateend', 'translateend', 'scaleend'], (e) => {
    console.log('变换结束', e);
    pointCenter.value = e.target.center_;
    createImg();
  });
};

// 从要素更新图片(提取公共逻辑)
const updateImageFromFeature = (feature) => {
  const geometry = feature.getGeometry();
  if (geometry instanceof Polygon) {
    const coordinates = geometry.getCoordinates()[0]; // 获取多边形坐标
    const extent = calculateExtentFromPolygon(coordinates);
    createImg(extent[0], extent[1], extent[2], extent[3], coordinates.flat());
  }
};

// 从多边形坐标计算范围
const calculateExtentFromPolygon = (coordinates) => {
  let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;

  coordinates.forEach(coord => {
    minX = Math.min(minX, coord[0]);
    minY = Math.min(minY, coord[1]);
    maxX = Math.max(maxX, coord[0]);
    maxY = Math.max(maxY, coord[1]);
  });

  return [minX, minY, maxX, maxY];
};

// 清理资源
const cleanup = () => {
  if (map.value) {
    map.value.setTarget(null);
    map.value = null;
  }
};

onMounted(() => {
  initMap();
  createPolygon();

  let left_lon = 116.25236770693185;
  let bottom_lat = 39.85376168646629;
  let right_lon = 116.33272829555995;
  let top_lat = 39.893308222194946;
  createImg(left_lon, bottom_lat, right_lon, top_lat);

  onEdit();
});

onUnmounted(() => {
  cleanup();
});
</script>

<style scoped lang="scss">
.map {
  width: 100%;
  height: 400px;
  border: 1px solid #ccc;
}
</style>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐