Skip to content

继承

上次更新 2025年8月20日星期三 3:16:20 字数 0 字 时长 0 分钟

OpenLayers 提供了强大的继承机制,可以通过继承现有类来创建自定义的图层、控件等。

示例

新标签页预览

代码实现

vue
<template>
  <div id="map" class="w-full h-96"></div>
  <!-- <div class="layer-name">当前图层: 天地图矢量</div> -->
</template>

<script setup>
import { onMounted } from "vue";
import "ol/ol.css";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import { fromLonLat } from "ol/proj";
import { Zoom, Attribution, ScaleLine, Control } from "ol/control";
import { createXYZ } from "ol/tilegrid";
import TileDebug from "ol/source/TileDebug";

onMounted(() => {
  // 创建地图
  const map = new Map({
    target: "map",
    view: new View({
      center: fromLonLat([117.33083, 39.094899]), // 天津
      zoom: 4,
    }),
    controls: [],
  });

  // 添加缩放控件
  const zoom = new Zoom({
    className: "ol-zoom",
    target: document.getElementById("map"),
  });
  map.addControl(zoom);

  // 添加版权控件
  const attribution = new Attribution({
    collapsible: false,
    html: '<a href="https://hx-docs.com" target="_blank" style="color: #336699;">hxDocs</span>',
  });
  map.addControl(attribution);

  // 添加比例尺控件
  const scaleControl = new ScaleLine({
    units: "metric",
  });
  map.addControl(scaleControl);

  // 天地图图层
  const tdtVec = new TileLayer({
    title: "天地图矢量",
    source: new XYZ({
      url: "http://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=d32c6748c80f81a44acd8633cea41dfd",
      maxZoom: 18,
      minZoom: 0,
    }),
  });

  const tdtImg = new TileLayer({
    title: "天地图影像",
    visible: false,
    source: new XYZ({
      url: "http://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=d32c6748c80f81a44acd8633cea41dfd",
      maxZoom: 18,
      minZoom: 0,
    }),
  });

  map.addLayer(tdtVec);
  map.addLayer(tdtImg);

  // 添加图层切换控件
  const layerSwitcher = new Control({
    element: document.createElement("div"),
    target: document.getElementById("map"),
  });

  // 创建切换按钮容器
  const switcherContainer = document.createElement("div");
  switcherContainer.className = "layer-switcher";

  // 创建缩略图
  const vecThumb = document.createElement("img");
  vecThumb.className = "vec-thumb";
  vecThumb.src =
    "https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=4&TILEROW=6&TILECOL=13&tk=d32c6748c80f81a44acd8633cea41dfd";

  const imgThumb = document.createElement("img");
  imgThumb.className = "img-thumb";
  imgThumb.src =
    "https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX=4&TILEROW=6&TILECOL=13&tk=d32c6748c80f81a44acd8633cea41dfd";

  switcherContainer.appendChild(vecThumb);
  switcherContainer.appendChild(imgThumb);

  switcherContainer.onclick = function () {
    this.classList.toggle("switched");

    const currentVecOpacity = tdtVec.getOpacity();
    const currentImgOpacity = tdtImg.getOpacity();

    // 使用透明度动画切换
    let start = null;
    const duration = 500;

    function animate(timestamp) {
      if (!start) start = timestamp;
      const progress = (timestamp - start) / duration;

      if (progress < 1) {
        if (tdtVec.getVisible()) {
          tdtVec.setOpacity(1 - progress);
          tdtImg.setOpacity(progress);
        } else {
          tdtVec.setOpacity(progress);
          tdtImg.setOpacity(1 - progress);
        }
        requestAnimationFrame(animate);
      } else {
        tdtVec.setVisible(!tdtVec.getVisible());
        tdtImg.setVisible(!tdtImg.getVisible());
        tdtVec.setOpacity(1);
        tdtImg.setOpacity(1);
      }
    }

    requestAnimationFrame(animate);
  };

  layerSwitcher.element.appendChild(switcherContainer);
  map.addControl(layerSwitcher);

  // 自定义网格图层
  class DebugTileLayer extends TileLayer {
    constructor(options) {
      super(options);
      this.source = new TileDebug({
        projection: "EPSG:3857",
        tileGrid: createXYZ({
          maxZoom: 22,
        }),
      });
    }
  }

  const debugLayer = new DebugTileLayer({
    title: "Debug Grid",
  });
  map.addLayer(debugLayer);
});
</script>

<style scoped>
.w-full {
  width: 100%;
}
.h-96 {
  height: 24rem;
}

.layer-name {
  position: absolute;
  right: 10px;
  bottom: 60px;
  background: rgba(255, 255, 255, 0.9);
  padding: 5px 10px;
  border-radius: 4px;
  font-size: 14px;
  color: #333;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.layer-switcher {
  position: absolute;
  right: 10px;
  bottom: 20px;
  width: 140px;
  height: 70px;
}

.layer-switcher img {
  position: absolute;
  width: calc(100% - 20px);
  height: 100%;
  border: 2px solid #fff;
  border-radius: 4px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  transition: all 0.3s ease;
  cursor: pointer;
}

.layer-switcher .vec-thumb {
  top: 0;
  left: 0;
  z-index: 2;
}

.layer-switcher .img-thumb {
  top: 10px;
  left: 10px;
  z-index: 1;
}

.layer-switcher.switched .vec-thumb {
  top: 10px;
  left: 10px;
  z-index: 1;
}

.layer-switcher.switched .img-thumb {
  top: 0;
  left: 0;
  z-index: 2;
}
</style>
关注公众号