My Personal Blog

Published on

用 Web Worker 和 Blob 实现 Leaflet 地图上的动态标记

Authors
  • avatar
    Name
    Tian Haipeng

今天想跟大家分享一下如何利用 Web Worker 和 Blob 在浏览器中实现一些有趣的效果。我结合了 Leaflet.js 来创建一个带有移动标记的地图,并让这些标记在地图上自由移动。这个过程不仅会让我们了解到如何使用 Leaflet.js 处理地图和标记,还会深入探讨 Web Worker 和 Blob 的实际应用。

什么是 Web Worker?

Web Worker 是一种运行在后台的 JavaScript,它可以独立于主线程执行任务,不会影响页面的响应速度。对于那些需要处理大量数据或者执行复杂运算的场景,Web Worker 能够显著提升性能。

什么是 Blob?

Blob(Binary Large Object) 是一种表示二进制数据的对象,可以用来存储任意类型的文件或者数据。通过 Blob,可以将文本、图像等数据作为文件对象来处理。在我们的例子中,我们会用 Blob 来创建一个内联的 Web Worker。

代码示例

下面是一段简单的代码,展示了如何使用 Web Worker 和 Blob 实现地图上标记的动态移动。

<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Leaflet with Custom Overlay and Moving Markers</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" referrerpolicy="no-referrer" />
  <style>
    #map {
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="map"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js" referrerpolicy="no-referrer"></script>
  <script>
    function initMap () {
      var map = L.map('map').setView([51.5074, -0.1278], 13);

      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19
      }).addTo(map);

      const markers = [];
      const numMarkers = 500;

      for (let i = 0; i < numMarkers; i++) {
        const lat = 51.5074 + (Math.random() - 0.5) * 0.02;
        const lng = -0.1278 + (Math.random() - 0.5) * 0.02;
        const marker = L.marker([lat, lng]).addTo(map);
        markers.push(marker);
      }

      // 创建一个内联的 Web Worker
      const blob = new Blob([`
        let velocities = [];

        self.onmessage = function (e) {
          const markerPositions = e.data.markerPositions;

          if (velocities.length === 0) {
            velocities = markerPositions.map(() => ({
              lat: (Math.random() - 0.5) * 0.0002,
              lng: (Math.random() - 0.5) * 0.0002
            }));
          }

          const updatedPositions = markerPositions.map((pos, index) => {
            const newLat = pos.lat + velocities[index].lat;
            const newLng = pos.lng + velocities[index].lng;

            return { lat: newLat, lng: newLng };
          });

          self.postMessage(updatedPositions);
        };
      `], { type: "application/javascript" });

      const worker = new Worker(URL.createObjectURL(blob));
      const markerPositions = markers.map(marker => marker.getLatLng());

      worker.postMessage({ markerPositions });

      worker.onmessage = function (e) {
        const updatedPositions = e.data;
        updatedPositions.forEach((pos, index) => {
          markers[index].setLatLng(pos);
        });
        requestAnimationFrame(() => worker.postMessage({ markerPositions: updatedPositions }));
      };
    }

    initMap();
  </script>
</body>
</html>

代码解读

  1. 初始化地图:我们先用 Leaflet.js 初始化了一张显示在屏幕上的地图,并且为它设置了初始视角和缩放级别。

  2. 生成标记:然后我们在地图上随机生成了 500 个标记,这些标记会在一个小范围内移动。

  3. 使用 Blob 创建内联 Web Worker

    • 在这段代码中,我们通过 Blob 创建了一个 JavaScript 脚本,这段脚本会作为 Web Worker 的主体。这样做的好处是,我们不需要在外部文件中编写和引用 Worker 脚本,可以直接在主线程代码中创建它。
    • Worker 的作用是接收主线程发送的标记位置数组,然后基于一个随机生成的速度数组来更新这些标记的位置。
  4. 与 Worker 通信:我们通过 worker.postMessage 将标记的位置发送到 Worker,并接收 Worker 返回的更新位置,然后在地图上更新标记的位置。

总结

通过这种方式,我们将计算密集型的标记移动任务放到了 Web Worker 中执行,从而不会阻塞主线程的渲染和交互。Blob 则为我们提供了一种方便的方式来创建内联 Worker,实现了更为灵活的代码组织形式。

希望通过这个例子,大家对 Web Worker 和 Blob 有了更深入的理解,也能在自己的项目中尝试这些技术!