OpenLayers 综合案例-区域掩膜

97 阅读2分钟

看过的知识不等于学会。唯有用心总结、系统记录,并通过温故知新反复实践,才能真正掌握一二
作为一名摸爬滚打三年的前端开发,开源社区给了我饭碗,我也将所学的知识体系回馈给大家,助你少走弯路!
OpenLayers、Leaflet 快速入门 ,每周保持更新 2 个案例
Cesium 快速入门,每周保持更新 4 个案例

OpenLayers 综合案例-区域掩膜

Vue 3 + OpenLayers 实现的 WebGIS 应用提供了完整的区域掩膜功能

实现思路

  1. 主要就是考验 canvas 的使用,核心代码参考openlayers 中区域掩膜的实现
  2. 在地图容器中添加一个 canvas,设置其在 map 之上;
  3. 监听 map 的 postrender 事件,每次事件触发重新绘制掩膜;
  4. 通过 map.getPixelFromCoordinate 实现地理坐标到屏幕坐标的转换;
  5. 通过 globalCompositeOperation = 'source-out'设置反向裁剪;

在这里插入图片描述

MP4效果动画链接地址&JSON数据获取地址

技术栈

该环境下代码即拿即用

Vue 3.5.13+
OpenLayers 10.5.0+
Vite 6.3.5+
<template>
  <div ref="mapContainer" id="map"></div>
</template>

<script setup>
import { ref, onMounted } from "vue";
import Map from "ol/Map.js";
import XYZ from "ol/source/XYZ.js";
import TileLayer from "ol/layer/Tile.js";
import View from "ol/View.js";
import "ol/ol.css";
import modalData from "./320000_bj.json";

const mapContainer = ref(null);
let map = null;
let canvas = null;
let ctx = null;

const view = new View({
  center: [118.7784, 32.0647], // 南京市中心经纬度
  zoom: 7,
  projection: "EPSG:4326",
});

onMounted(async () => {
  map = new Map({
    target: mapContainer.value,
    layers: [
      new TileLayer({
        source: new XYZ({
          url: "https://webrd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}",
        }),
      }),
    ],
    view,
  });

  // 创建canvas
  const { offsetWidth, offsetHeight } = map.getViewport();
  canvas = document.createElement("canvas");
  canvas.width = offsetWidth;
  canvas.height = offsetHeight;
  canvas.style.position = "absolute";
  canvas.style.top = "0px";
  canvas.style.left = "0px";
  canvas.style.zIndex = "1";
  ctx = canvas.getContext("2d");
  map.getViewport().appendChild(canvas);

  // 注册map事件
  map.on("postrender", () => {
    addMask();
  });
});

// 添加区域掩膜
const addMask = (params) => {
  const { fillStyle, strokeStyle, lineWidth } = {
    fillStyle: "rgba(255,255,255,0.8)",
    strokeStyle: "#f00",
    lineWidth: 3,
    ...params,
  };

  ctx.clearRect(0, 0, canvas.width, canvas.height);

  // 获取整个江苏省的所有多边形
  const jiangsuPolygons = modalData.features[0].geometry.coordinates;

  // 1. 绘制整个画布为半透明白色
  ctx.fillStyle = fillStyle;
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // 2. 使用组合模式清除多边形区域
  ctx.globalCompositeOperation = "destination-out";
  ctx.fillStyle = "rgba(0,0,0,1)"; // 使用任意颜色,alpha=1确保完全清除

  // 绘制所有多边形(包括主区域和岛屿)
  jiangsuPolygons.forEach((polygon) => {
    const ring = polygon[0]; // 获取多边形外环
    const coords = ring.map((coord) => map.getPixelFromCoordinate(coord));

    ctx.beginPath();
    coords.forEach((coord, index) => {
      index === 0
        ? ctx.moveTo(coord[0], coord[1])
        : ctx.lineTo(coord[0], coord[1]);
    });
    ctx.closePath();
    ctx.fill();
  });

  // 3. 恢复组合模式并绘制边界
  ctx.globalCompositeOperation = "source-over";
  ctx.strokeStyle = strokeStyle;
  ctx.lineWidth = lineWidth;

  // 绘制所有多边形的边界
  jiangsuPolygons.forEach((polygon) => {
    const ring = polygon[0];
    const coords = ring.map((coord) => map.getPixelFromCoordinate(coord));

    ctx.beginPath();
    coords.forEach((coord, index) => {
      index === 0
        ? ctx.moveTo(coord[0], coord[1])
        : ctx.lineTo(coord[0], coord[1]);
    });
    ctx.closePath();
    ctx.stroke();
  });
};
</script>

<style scoped>
#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100vw;
  height: 100vh;
}
</style>