使用 Vue.js 无法在 Bulma 选项卡中加载传单地图

Leaflet map not loading in Bulma tab using Vue.js

我在使用 Vue.js 和 Bulma 选项卡组件(通过 Buefy)加载 Leaflet 地图时遇到问题。

如果地图放在选项卡内,则在调整浏览器 window 大小之前它不会加载所有图块。

如果地图放置在 Bulma 选项卡组件之外,则它可以毫无问题地加载。

调用 map.invalidateSize() 似乎有帮助,但要在选项卡更改时自动执行此操作,我必须使用 setTimeout 调用它并设置非常大的延迟,例如 1 秒 - 这非常难看。

如何在没有此 invalidateSize 解决方法的情况下使其正常工作?

问题示例:https://codepen.io/alxxnder/pen/zyYxwd

没有问题的示例:https://codepen.io/alxxnder/pen/LMYEjr

代码:

new Vue({
  el: '#app',
  data: {
    map: null,
  },
  methods: {
    invalidateSize: function() {
      this.map.invalidateSize();
    }
  },
  mounted() {
    this.map = L.map('map').setView([38.63, -90.23], 12);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);
  }
})
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Leaflet Test</title>

  <link rel="stylesheet" href="https://unpkg.com/buefy@0.7/dist/buefy.min.css">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css">
</head>

<body>


  <div class="section">
    <div class="container" id="app">
      <b-tabs position="is-centered">
        <b-tab-item label="Tab 1">
          <div class="section">
            Tab 1
            <div class="map" id="map" style="height: 400px; width: 100%"></div>
            <button class="button is-info" @click="invalidateSize()">invalidateSize</button>
          </div>
        </b-tab-item>
        <b-tab-item label="Tab 2">
          <div class="section">
            Tab 2
          </div>
        </b-tab-item>
      </b-tabs>
    </div>
  </div>


</body>

<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/buefy@0.7/dist/buefy.min.js"></script>
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"></script>

</html>

中所述,此问题是由于您的地图容器在您初始化时尚未达到其完整大小而引起的。如果您的地图位于最初隐藏的选项卡中(例如在选项卡 2 中),您可能会更容易理解它。

至于您最初激活的选项卡(即选项卡 1),Buefy / Bulma 可能仍需要一些时间才能显示选项卡内容。

由于在tab 转换完成时没有事件发生,因此在调用invalidateSize 方法之前必须等待转换持续时间。在你的情况下,300ms 似乎没问题。

那么你也应该在用户 changes the tab 时再次调用它(参见 Buefy tabs 事件),否则如果浏览器在你的标签隐藏时改变了大小,同样的问题会再次发生。

2 个选项卡中的地图演示:

new Vue({
  el: '#app',
  data: {
    map: null,
    map2: null,
    tabMaps: []
  },
  methods: {
    invalidateSize: function(tabIndex) {
      setTimeout(() => {
        if (typeof tabIndex === "number") {
          this.tabMaps[tabIndex].invalidateSize();
        } else {
          // invalidate all maps
          this.tabMaps.forEach(map => {
            map.invalidateSize();
          });
        }
      }, 300);
    }
  },
  mounted() {
    this.map = L.map('map').setView([38.63, -90.23], 12);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(this.map);

    // map2 in tab2
    this.map2 = L.map(this.$refs.map2).setView([38.63, -90.23], 12);
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(
      this.map2
    );

    this.tabMaps.push(this.map); // 0
    this.tabMaps.push(this.map2); // 1
    this.invalidateSize();
  }
})
<link rel="stylesheet" href="https://unpkg.com/buefy@0.7/dist/buefy.min.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.4/dist/leaflet.css">

<div class="section">
  <div class="container" id="app">
    <b-tabs @change="invalidateSize" position="is-centered">
      <b-tab-item label="Tab 1">
        <div class="section">
          Tab 1
          <div class="map" id="map" style="height: 400px; width: 100%"></div>
          <button class="button is-info" @click="invalidateSize()">invalidateSize</button>
        </div>
      </b-tab-item>
      <b-tab-item label="Tab 2">
        <div class="section">
          Tab 2
          <div class="map" ref="map2" style="height: 400px; width: 100%"></div>
        </div>
      </b-tab-item>
    </b-tabs>
  </div>
</div>

<script src="https://unpkg.com/vue@2"></script>
<script src="https://unpkg.com/buefy@0.7/dist/buefy.min.js"></script>
<script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"></script>