Openlayers图层按需分层渲染到不同Canvas画布

Openlayers图层按需分层渲染到不同Canvas画布

各个库版本如下:

javascript 复制代码
    "ol": "^10.9.0",
    "proj4": "^2.20.8",
    "vue3-openlayers": "^12.2.2"

1 合成渲染

Openlayers目前的渲染策略是合成渲染,即通过调度Canvas 2D和WebGL两个渲染管线,使用Composite渲染合成器将各个图层(切片图层、矢量图层等)按照不同渲染机制,最终将图像呈现在浏览器DOM中对应的Canvas画布上(默认Canvas 2D对应一个canvas,WebGL对应一个canvas)

  • 优点是默认简单场景下同一个渲染管线处理的数据只呈现在一个Canvas画布上(默认Canvas 2D对应一个canvas,WebGL对应一个canvas)
  • 缺点是使用css样式、动画对Canvas画布进行控制或者添加效果时会影响所有在画布上的元素(即所有合成在当前Canvas图层)

2 分层渲染

其实在早期版本(6.0及之前)的Openlayers中(包括其他早期版本ArcGis,Leaflet)都是按照图层分层渲染的,最终在DOM中的呈现就是每个图层对应不同DOM元素(Div,Canvas,Svg)

  • 优点是可以直接使用css样式、动画对图层进行控制或者添加效果
  • 缺点也是显而易见,当图层比较多,图层上的元素比较复杂时,会大量增加DOM元素

3 按需分层渲染

这里所谓的按需分层渲染,主要是想让Openlayers对特定图层进行分层渲染,将特定图层渲染到不同的Canvas画布上。目的就是为了方便使用CSS控制Canvas的样式

对应场景:

  1. 当选中矢量图形时,对切片图层使用黑暗模式
  2. 当选中矢量图形时,对目标图层使用CSS动画,过渡更改透明度

4 实现

实现按需分层渲染其实很简单,因为都是Openlayers已经实现的功能,只是对一些属性进行设置即可,具体如下(满足其一即可):

  1. 为图层设置不同的类名className(不设置时,默认所有图层类名为ol-layer)
  2. 为图层设置不同的背景色background
  3. 在加入地图的顺序上间隔使用Canvas 2D图层和WebGL图层

5 对比(Openlayers原生)

5.1 Openlayers默认合成渲染

5.1.1 渲染结果

结合下面的代码可以发现:

  • tiandituImgLayer,tiandituCiaLayer,vectorLayer1,vectorLayer2,vectorLayer3被合并渲染到了绿色框内的canvas中
  • webglTileLayer,webglVectorLayer被合并渲染到了蓝色框内的canvas中

作为对比,直接在DOM中

  • 删除绿色框内的canvas:
  • 删除蓝色框内的canvas:

5.1.2 代码

javascript 复制代码
<template>
  <div class="map-page">
    <h1>OpenLayers - 天地图影像底图</h1>
    <div class="info-panel">
      <h3>图层说明</h3>
      <p><strong>TileLayer:</strong> 天地图影像底图 + 影像注记</p>
      <p>
        <strong>VectorLayer:</strong> 三角形 / 矩形 / 五边形(不同描边和填充)
      </p>
      <p><strong>WebGLTile:</strong> 天地图矢量底图(透明度 0.5)</p>
      <p><strong>WebGLVector:</strong> 城市点位渲染</p>
    </div>
    <div id="tianditu-ol-map" ref="mapContainer" class="map-container"></div>
    <div class="legend">
      <h4>图例</h4>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(0, 102, 255, 0.3);
            border: 3px solid #0066ff;
          "
        ></span>
        <span>三角形 - 蓝色实线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(255, 0, 0, 0.2);
            border: 2px dashed #ff0000;
          "
        ></span>
        <span>矩形 - 红色虚线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(0, 204, 0, 0.25);
            border: 4px solid #00cc00;
          "
        ></span>
        <span>五边形 - 绿色实线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-dot"
          style="background-color: rgba(255, 165, 0, 0.8)"
        ></span>
        <span>WebGLVector 城市点位</span>
      </div>
      <div class="legend-item">
        <span class="legend-label">WebGLTile 天地图矢量底图(透明度 0.5)</span>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import VectorLayer from "ol/layer/Vector";
import VectorSource from "ol/source/Vector";
import { XYZ } from "ol/source";
import { Style, Fill, Stroke } from "ol/style";
import { Polygon, Point } from "ol/geom";
import Feature from "ol/Feature";
import { fromLonLat } from "ol/proj";
import WebGLTile from "ol/layer/WebGLTile";
import WebGLVector from "ol/layer/WebGLVector";

// 天地图 Token(请替换为自己的 Token)
const TK = "";

const mapContainer = ref<HTMLDivElement>();
let map: Map | null = null;

// ===== 天地图影像底图 =====
const tiandituImgLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
});

// 天地图影像注记层
const tiandituCiaLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
});

// ===== 三个矢量图层(不同形状、描边、填充) =====

// 图层1: 三角形 - 蓝色实线描边, 浅蓝填充
const vectorLayer1 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([100, 28]),
            fromLonLat([108, 28]),
            fromLonLat([104, 36]),
            fromLonLat([100, 28]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#0066FF", width: 3 }),
    fill: new Fill({ color: "rgba(0, 102, 255, 0.3)" }),
  }),
});

// 图层2: 矩形 - 红色虚线描边, 浅红填充
const vectorLayer2 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([110, 24]),
            fromLonLat([118, 24]),
            fromLonLat([118, 30]),
            fromLonLat([110, 30]),
            fromLonLat([110, 24]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#FF0000", width: 2, lineDash: [10, 5] }),
    fill: new Fill({ color: "rgba(255, 0, 0, 0.2)" }),
  }),
});

// 图层3: 五边形 - 绿色实线描边, 浅绿填充
const vectorLayer3 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([108, 38]),
            fromLonLat([113, 35]),
            fromLonLat([118, 38]),
            fromLonLat([115, 43]),
            fromLonLat([110, 43]),
            fromLonLat([108, 38]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#00CC00", width: 4 }),
    fill: new Fill({ color: "rgba(0, 204, 0, 0.25)" }),
  }),
});

// ===== WebGLTile 图层 - 天地图矢量底图, 透明度 0.5 =====
const webglTileLayer = new WebGLTile({
  opacity: 0.5,
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }) as any,
});

// ===== WebGLVector 图层 - 城市点位 =====
const webglVectorLayer = new WebGLVector({
  source: new VectorSource({
    features: [
      new Feature({ geometry: new Point(fromLonLat([116.4, 39.9])) }),
      new Feature({ geometry: new Point(fromLonLat([121.5, 31.2])) }),
      new Feature({ geometry: new Point(fromLonLat([113.3, 23.1])) }),
      new Feature({ geometry: new Point(fromLonLat([104.1, 30.6])) }),
      new Feature({ geometry: new Point(fromLonLat([114.3, 30.6])) }),
    ],
  }),
  style: {
    "circle-radius": 10,
    "circle-fill-color": "rgba(255, 165, 0, 0.8)",
    "circle-stroke-color": "#8B4513",
    "circle-stroke-width": 2,
  },
});

onMounted(() => {
  map = new Map({
    target: mapContainer.value!,
    layers: [
      tiandituImgLayer,
      tiandituCiaLayer,
      vectorLayer1,
      vectorLayer2,
      vectorLayer3,
      webglTileLayer,
      webglVectorLayer,
    ],
    view: new View({
      center: fromLonLat([108, 34]),
      zoom: 5,
    }),
  });
});

onUnmounted(() => {
  if (map) {
    map.setTarget(undefined);
    map = null;
  }
});
</script>

<style scoped>
.map-page {
  padding: 20px;
}

h1 {
  margin-bottom: 20px;
  color: #333;
}

.info-panel {
  background-color: #f8f9fa;
  padding: 15px;
  border-radius: 8px;
  margin-bottom: 15px;
  border-left: 4px solid #42b983;
  text-align: left;
}

.info-panel h3 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #2c3e50;
}

.info-panel p {
  margin: 5px 0;
  color: #555;
}

.map-container {
  width: 100%;
  height: 600px;
  border: 2px solid #ddd;
  border-radius: 8px;
}

.legend {
  margin-top: 15px;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #ddd;
}

.legend h4 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #333;
}

.legend-item {
  display: flex;
  align-items: center;
  margin: 5px 0;
}

.legend-color {
  width: 30px;
  height: 20px;
  margin-right: 10px;
  display: inline-block;
}

.legend-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  margin-right: 13px;
  margin-left: 8px;
  border: 2px solid #8b4513;
  display: inline-block;
}

.legend-label {
  font-size: 14px;
  color: #666;
  margin-left: 2px;
}
</style>

5.2 Openlayers按需分层渲染

5.2.1 设置类名和背景色
5.2.1.1 渲染结果

结合下面的代码可以发现:

  • tiandituImgLayer,tiandituCiaLayer,类名都是layer1,被合并渲染到了红色框内的canvas中
  • vectorLayer1,类名是layer2,被渲染到了橙色框内的canvas中
  • vectorLayer2,类名是layer3,被渲染到了绿色框内的canvas中
  • vectorLayer3,类名是layer3,但是设置背景色是rgba(0,0,0,0.5),被渲染到了蓝色框内的canvas中
  • webglTileLayer,类名是layer4,被渲染到了黑色框内的canvas中
  • webglVectorLayer,类名是layer5,被渲染到了灰色框内的canvas中
5.2.1.2 关键代码
javascript 复制代码
// 省略上方代码
// ===== 天地图影像底图 =====
const tiandituImgLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
  className: "layer1",
});

// 天地图影像注记层
const tiandituCiaLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
  className: "layer1",
});

// ===== 三个矢量图层(不同形状、描边、填充) =====

// 图层1: 三角形 - 蓝色实线描边, 浅蓝填充
const vectorLayer1 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([100, 28]),
            fromLonLat([108, 28]),
            fromLonLat([104, 36]),
            fromLonLat([100, 28]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#0066FF", width: 3 }),
    fill: new Fill({ color: "rgba(0, 102, 255, 0.3)" }),
  }),
  className: "layer2",
});

// 图层2: 矩形 - 红色虚线描边, 浅红填充
const vectorLayer2 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([110, 24]),
            fromLonLat([118, 24]),
            fromLonLat([118, 30]),
            fromLonLat([110, 30]),
            fromLonLat([110, 24]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#FF0000", width: 2, lineDash: [10, 5] }),
    fill: new Fill({ color: "rgba(255, 0, 0, 0.2)" }),
  }),
  className: "layer3",
});

// 图层3: 五边形 - 绿色实线描边, 浅绿填充
const vectorLayer3 = new VectorLayer({
  source: new VectorSource({
    features: [
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([108, 38]),
            fromLonLat([113, 35]),
            fromLonLat([118, 38]),
            fromLonLat([115, 43]),
            fromLonLat([110, 43]),
            fromLonLat([108, 38]),
          ],
        ]),
      }),
    ],
  }),
  style: new Style({
    stroke: new Stroke({ color: "#00CC00", width: 4 }),
    fill: new Fill({ color: "rgba(0, 204, 0, 0.25)" }),
  }),
  className: "layer3",
  background: "rgba(0,0,0,0.5)",
});

// ===== WebGLTile 图层 - 天地图矢量底图, 透明度 0.5 =====
const webglTileLayer = new WebGLTile({
  opacity: 0.5,
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }) as any,
  className: "layer4",
});

// ===== WebGLVector 图层 - 城市点位 =====
const webglVectorLayer = new WebGLVector({
  source: new VectorSource({
    features: [
      new Feature({ geometry: new Point(fromLonLat([116.4, 39.9])) }),
      new Feature({ geometry: new Point(fromLonLat([121.5, 31.2])) }),
      new Feature({ geometry: new Point(fromLonLat([113.3, 23.1])) }),
      new Feature({ geometry: new Point(fromLonLat([104.1, 30.6])) }),
      new Feature({ geometry: new Point(fromLonLat([114.3, 30.6])) }),
    ],
  }),
  style: {
    "circle-radius": 10,
    "circle-fill-color": "rgba(255, 165, 0, 0.8)",
    "circle-stroke-color": "#8B4513",
    "circle-stroke-width": 2,
  },
  className: "layer5",
});
// 省略下方代码
5.2.2 设置加入地图的顺序
5.2.2.1 渲染结果

结合下面的代码可以发现:

  • tiandituImgLayer,tiandituCiaLayer,类名都是ol-layer,被合并渲染到了红色框内的canvas中
  • webglTileLayer,类名是ol-layer,被渲染到了橙色框内的canvas中
  • vectorLayer1,类名是ol-layer,被渲染到了绿色框内的canvas中
  • webglVectorLayer,类名是ol-layer,被渲染到了蓝色框内的canvas中
  • vectorLayer2,vectorLayer3,类名是ol-layer,被渲染到了黑色框内的canvas中
5.2.2.2 关键代码
javascript 复制代码
// 省略上方代码
onMounted(() => {
  map = new Map({
    target: mapContainer.value!,
    layers: [
      tiandituImgLayer,
      tiandituCiaLayer,
      webglTileLayer,
      vectorLayer1,
      webglVectorLayer,
      vectorLayer2,
      vectorLayer3,
    ],
    view: new View({
      center: fromLonLat([108, 34]),
      zoom: 5,
    }),
  });
});
// 省略下方代码
5.2.3 为图层设置CSS动画
5.2.3.1 渲染结果
5.2.3.2 关键代码
javascript 复制代码
// 省略上方代码
// ===== 天地图影像底图 =====
const tiandituImgLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
  className: "layer1",
});

// 天地图影像注记层
const tiandituCiaLayer = new TileLayer({
  source: new XYZ({
    url: `https://t{0-7}.tianditu.gov.cn/DataServer?T=cia_w&x={x}&y={y}&l={z}&tk=${TK}`,
  }),
  className: "layer1",
});

// 省略中间代码
<style scoped>

// 省略上方代码
:deep(.layer1) {
  animation: fadeIn 2s ease-in infinite;
}

@keyframes fadeIn {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}
</style>

6 对比(Vue3-Openlayers版本)

6.1 默认合成渲染

6.1.1 渲染结果

结合下面的代码可以发现:

  • tiandituImgLayer,tiandituCiaLayer,vectorLayer1,vectorLayer2,vectorLayer3被合并渲染到了绿色框内的canvas中
  • webglTileLayer,webglVectorLayer被合并渲染到了蓝色框内的canvas中

而且可以发现,使用Vue3-Openlayers后,会创建很多空div,高度均为0

6.1.2 代码

javascript 复制代码
<template>
  <div class="map-page">
    <h1>Vue3-OpenLayers - 天地图影像底图</h1>
    <div class="info-panel">
      <h3>图层说明</h3>
      <p>
        <strong>OlTileLayer:</strong> 天地图影像底图 +
        影像注记(OlSourceTianditu)
      </p>
      <p>
        <strong>OlVectorLayer:</strong> 三角形 / 矩形 / 五边形(不同描边和填充)
      </p>
      <p><strong>OlWebglTileLayer:</strong> 天地图矢量底图(透明度 0.5)</p>
      <p><strong>OlWebglVectorLayer:</strong> 城市点位渲染</p>
    </div>
    <ol-map
      ref="mapRef"
      :loadTilesWhileAnimating="true"
      :loadTilesWhileInteracting="true"
      class="map-container"
    >
      <ol-view :center="center" :zoom="zoom" projection="EPSG:3857" />

      <!-- 天地图影像底图 -->
      <ol-tile-layer>
        <ol-source-tianditu layerType="img" :tk="tk" />
      </ol-tile-layer>

      <!-- 天地图影像注记 -->
      <ol-tile-layer>
        <ol-source-tianditu layerType="img" :tk="tk" :isLabel="true" />
      </ol-tile-layer>

      <!-- 矢量图层1: 三角形 - 蓝色实线描边, 浅蓝填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="triangleSourceRef">
          <ol-style>
            <ol-style-stroke color="#0066FF" :width="3" />
            <ol-style-fill color="rgba(0, 102, 255, 0.3)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- 矢量图层2: 矩形 - 红色虚线描边, 浅红填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="rectSourceRef">
          <ol-style>
            <ol-style-stroke color="#FF0000" :width="2" :lineDash="[10, 5]" />
            <ol-style-fill color="rgba(255, 0, 0, 0.2)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- 矢量图层3: 五边形 - 绿色实线描边, 浅绿填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="pentagonSourceRef">
          <ol-style>
            <ol-style-stroke color="#00CC00" :width="4" />
            <ol-style-fill color="rgba(0, 204, 0, 0.25)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- OlWebglTileLayer: 天地图矢量底图, 透明度 0.5 -->
      <ol-webgl-tile-layer :opacity="0.5">
        <ol-source-xyz :url="vecUrl" />
      </ol-webgl-tile-layer>

      <!-- OlWebglVectorLayer: 城市点位 -->
      <ol-webgl-vector-layer :styles="webglVectorStyles">
        <ol-source-vector ref="webglVectorSourceRef" />
      </ol-webgl-vector-layer>
    </ol-map>
    <div class="legend">
      <h4>图例</h4>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(0, 102, 255, 0.3);
            border: 3px solid #0066ff;
          "
        ></span>
        <span>三角形 - 蓝色实线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(255, 0, 0, 0.2);
            border: 2px dashed #ff0000;
          "
        ></span>
        <span>矩形 - 红色虚线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-color"
          style="
            background-color: rgba(0, 204, 0, 0.25);
            border: 4px solid #00cc00;
          "
        ></span>
        <span>五边形 - 绿色实线描边</span>
      </div>
      <div class="legend-item">
        <span
          class="legend-dot"
          style="background-color: rgba(255, 165, 0, 0.8)"
        ></span>
        <span>OlWebglVectorLayer 城市点位</span>
      </div>
      <div class="legend-item">
        <span class="legend-label"
          >OlWebglTileLayer 天地图矢量底图(透明度 0.5)</span
        >
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
import { fromLonLat } from "ol/proj";
import { Polygon, Point } from "ol/geom";
import Feature from "ol/Feature";

// 天地图 Token(请替换为自己的 Token)
const tk = "";

const center = ref(fromLonLat([108, 34]));
const zoom = ref(5);

const vecUrl = `https://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${tk}`;

// WebGLVector 扁平样式(flat style)
const webglVectorStyles = {
  "circle-radius": 10,
  "circle-fill-color": "rgba(255, 165, 0, 0.8)",
  "circle-stroke-color": "#8B4513",
  "circle-stroke-width": 2,
};

// Source 引用
const triangleSourceRef = ref();
const rectSourceRef = ref();
const pentagonSourceRef = ref();
const webglVectorSourceRef = ref();

onMounted(() => {
  // 三角形
  const triSource = triangleSourceRef.value?.source;
  if (triSource) {
    triSource.addFeature(
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([100, 28]),
            fromLonLat([108, 28]),
            fromLonLat([104, 36]),
            fromLonLat([100, 28]),
          ],
        ]),
      }),
    );
  }

  // 矩形
  const rectSource = rectSourceRef.value?.source;
  if (rectSource) {
    rectSource.addFeature(
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([110, 24]),
            fromLonLat([118, 24]),
            fromLonLat([118, 30]),
            fromLonLat([110, 30]),
            fromLonLat([110, 24]),
          ],
        ]),
      }),
    );
  }

  // 五边形
  const pentSource = pentagonSourceRef.value?.source;
  if (pentSource) {
    pentSource.addFeature(
      new Feature({
        geometry: new Polygon([
          [
            fromLonLat([108, 38]),
            fromLonLat([113, 35]),
            fromLonLat([118, 38]),
            fromLonLat([115, 43]),
            fromLonLat([110, 43]),
            fromLonLat([108, 38]),
          ],
        ]),
      }),
    );
  }

  // WebGLVector 城市点位
  const wvSource = webglVectorSourceRef.value?.source;
  if (wvSource) {
    wvSource.addFeatures([
      new Feature({ geometry: new Point(fromLonLat([116.4, 39.9])) }),
      new Feature({ geometry: new Point(fromLonLat([121.5, 31.2])) }),
      new Feature({ geometry: new Point(fromLonLat([113.3, 23.1])) }),
      new Feature({ geometry: new Point(fromLonLat([104.1, 30.6])) }),
      new Feature({ geometry: new Point(fromLonLat([114.3, 30.6])) }),
    ]);
  }
});
</script>

<style scoped>
.map-page {
  padding: 20px;
}

h1 {
  margin-bottom: 20px;
  color: #333;
}

.info-panel {
  background-color: #f8f9fa;
  padding: 15px;
  border-radius: 8px;
  margin-bottom: 15px;
  border-left: 4px solid #42b983;
  text-align: left;
}

.info-panel h3 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #2c3e50;
}

.info-panel p {
  margin: 5px 0;
  color: #555;
}

.map-container {
  width: 100%;
  height: 600px;
  border: 2px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
}

.legend {
  margin-top: 15px;
  padding: 15px;
  background-color: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #ddd;
}

.legend h4 {
  margin-top: 0;
  margin-bottom: 10px;
  color: #333;
}

.legend-item {
  display: flex;
  align-items: center;
  margin: 5px 0;
}

.legend-color {
  width: 30px;
  height: 20px;
  margin-right: 10px;
  display: inline-block;
}

.legend-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  margin-right: 13px;
  margin-left: 8px;
  border: 2px solid #8b4513;
  display: inline-block;
}

.legend-label {
  font-size: 14px;
  color: #666;
  margin-left: 2px;
}
</style>

6.2 按需分层渲染

6.2.1 设置类名和背景色
6.2.1.1 渲染结果
6.2.2.1 关键代码

与原生写法一样,只需要在组件上绑定className类名和background背景色即可达到效果

javascript 复制代码
 // 省略上方代码
<!-- 天地图影像底图 -->
      <ol-tile-layer className="layer1">
        <ol-source-tianditu layerType="img" :tk="tk" />
      </ol-tile-layer>

      <!-- 天地图影像注记 -->
      <ol-tile-layer className="layer1">
        <ol-source-tianditu layerType="img" :tk="tk" :isLabel="true" />
      </ol-tile-layer>

      <!-- 矢量图层1: 三角形 - 蓝色实线描边, 浅蓝填充 -->
      <ol-vector-layer className="layer2">
        <ol-source-vector ref="triangleSourceRef">
          <ol-style>
            <ol-style-stroke color="#0066FF" :width="3" />
            <ol-style-fill color="rgba(0, 102, 255, 0.3)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- 矢量图层2: 矩形 - 红色虚线描边, 浅红填充 -->
      <ol-vector-layer className="layer3">
        <ol-source-vector ref="rectSourceRef">
          <ol-style>
            <ol-style-stroke color="#FF0000" :width="2" :lineDash="[10, 5]" />
            <ol-style-fill color="rgba(255, 0, 0, 0.2)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- 矢量图层3: 五边形 - 绿色实线描边, 浅绿填充 -->
      <ol-vector-layer className="layer3" background="rgba(0,0,0,0.5)">
        <ol-source-vector ref="pentagonSourceRef">
          <ol-style>
            <ol-style-stroke color="#00CC00" :width="4" />
            <ol-style-fill color="rgba(0, 204, 0, 0.25)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- OlWebglTileLayer: 天地图矢量底图, 透明度 0.5 -->
      <ol-webgl-tile-layer :opacity="0.5" className="layer4">
        <ol-source-xyz :url="vecUrl" />
      </ol-webgl-tile-layer>

      <!-- OlWebglVectorLayer: 城市点位 -->
      <ol-webgl-vector-layer :styles="webglVectorStyles" className="layer5">
        <ol-source-vector ref="webglVectorSourceRef" />
      </ol-webgl-vector-layer>
 // 省略下方代码
6.2.2 设置加入地图的顺序
6.2.2.1 渲染结果
6.2.2.1 关键代码

只需要调整组件顺序即可

javascript 复制代码
      // 省略上方代码
     <!-- 天地图影像底图 -->
      <ol-tile-layer>
        <ol-source-tianditu layerType="img" :tk="tk" />
      </ol-tile-layer>

      <!-- 天地图影像注记 -->
      <ol-tile-layer>
        <ol-source-tianditu layerType="img" :tk="tk" :isLabel="true" />
      </ol-tile-layer>

      <!-- OlWebglTileLayer: 天地图矢量底图, 透明度 0.5 -->
      <ol-webgl-tile-layer :opacity="0.5">
        <ol-source-xyz :url="vecUrl" />
      </ol-webgl-tile-layer>

      <!-- 矢量图层1: 三角形 - 蓝色实线描边, 浅蓝填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="triangleSourceRef">
          <ol-style>
            <ol-style-stroke color="#0066FF" :width="3" />
            <ol-style-fill color="rgba(0, 102, 255, 0.3)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- OlWebglVectorLayer: 城市点位 -->
      <ol-webgl-vector-layer :styles="webglVectorStyles">
        <ol-source-vector ref="webglVectorSourceRef" />
      </ol-webgl-vector-layer>

      <!-- 矢量图层2: 矩形 - 红色虚线描边, 浅红填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="rectSourceRef">
          <ol-style>
            <ol-style-stroke color="#FF0000" :width="2" :lineDash="[10, 5]" />
            <ol-style-fill color="rgba(255, 0, 0, 0.2)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>

      <!-- 矢量图层3: 五边形 - 绿色实线描边, 浅绿填充 -->
      <ol-vector-layer>
        <ol-source-vector ref="pentagonSourceRef">
          <ol-style>
            <ol-style-stroke color="#00CC00" :width="4" />
            <ol-style-fill color="rgba(0, 204, 0, 0.25)" />
          </ol-style>
        </ol-source-vector>
      </ol-vector-layer>
      // 省略下方代码
相关推荐
用户059540174461 小时前
把对话记忆从内存搬到 Redis,长期记忆准确率从 63% 提升到 98%
前端·css
daols881 小时前
vxe-table 实现 Excel 风格向下复制填充(Ctrl + D 键)
javascript·vue.js·excel·vxe-table·vxe-ui
木斯佳1 小时前
前端八股文面经大全:字节跳动-存储部门一面(2026-05-29)·面经深度解析
前端·状态模式
fxshy1 小时前
Vue 组件中 padding 生效了,但竖线还是贴到底边的问题
javascript·vue.js·ecmascript
ayqy贾杰1 小时前
有AI了,我当超大头兵还苟得住吗?
前端·后端·架构
Aotman_1 小时前
JavaScript数组对象中指定字段转换
java·开发语言·前端·javascript·vue.js·前端框架·es6
姓蔡小朋友1 小时前
React基础
前端·react.js·前端框架
IT_陈寒1 小时前
Vue的动态组件坑了我整整一天!
前端·人工智能·后端
恋猫de小郭1 小时前
Flutter 最好的 AI 自动化测试工具:Patrol
android·前端·flutter