【问题标题】:How can I highlight which marker has been clicked in my leaflet map (nuxt/vue)如何突出显示在我的传单地图(nuxt/vue)中单击了哪个标记
【发布时间】:2021-04-23 00:44:19
【问题描述】:

我有一张传单地图,上面显示了一个地区的不同餐厅和远足径。 当用户单击标记时,我会在下面的 div 中显示有关它的信息。

但是,如果当您单击标记时,它会在地图上突出显示自己,这样您就可以准确地看到您当前正在阅读的标记,这将是一个很好的用户体验。

但是我似乎找不到办法。

标记是通过使用 v-for 循环通过“位置”对象的 JSON 文件创建的,JSON 示例:

    "locations": {
            "restaurants": [{
                    "id": "R1",
                    "name": "El Rancho Grande",
                    "latlong": [
                        "36.90316216998795",
                        "-4.115129907833307"
                    ],
                    "type": "Restaurant",
                    "description": "This is a description of the location",
                    "img": "https://www.viewranger.com/routelibrary/discoverywalks/axarquia/0115.jpg"
                }
...

在我看来,一种方法可能是:

  1. 创建所有标记并添加到数组中。

  2. 将它们全部添加到地图中。

  3. 当一个被点击时,在数组中找到那个标记,更新它的图标以显示它当前被选中。

  4. 删除所有标记。

  5. 用现在更新的标记重新绘制它们。

但是这种方法似乎太过分了,所以我对此表示怀疑。

谁能给我一些想法?干杯!

这是我在“了解该地区”页面中的地图:

   ...
        <!-- ############# INTERACTIVE MAP ############# -->
        <div class="area__map">
          <div id="map-wrap">
            <no-ssr>
              <l-map :zoom="13" :center="[36.9023, -4.1139]" ref="map">
                <l-tile-layer
                  url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
                ></l-tile-layer>
                <!-- ############# CREATE RESTAURANT MARKERS ############# -->
                <l-marker
                  v-for="restaurant in restaurants"
                  :key="restaurant.id"
                  :lat-lng="restaurant.latlong"
                  :icon="eatIcon"
                  v-on:click="getPin(restaurant.id)"
                ></l-marker>
                <!-- ############# CREATE HIKING TRAIL MARKERS ############# -->
                <l-marker
                  v-for="walk in walks"
                  :key="walk.id"
                  :lat-lng="walk.latlong"
                  :icon="walkIcon"
                  v-on:click="getPin(walk.id)"
                ></l-marker>
              </l-map>
            </no-ssr>
          </div>
          <!-- ############# SHOW CURRENT SELECTED MARKER INFO ############# -->
          <div class="area__map__selected-pin">
            <h3>{{ selectedPin.name }}</h3>
            <h4>{{ selectedPin.type }}</h4>
            <p>{{ selectedPin.description }}</p>
            <img
              v-if="selectedPin.img != ''"
              :src="selectedPin.img"
              class="area__map__selected-pin__selected-img"
            />
          </div>
          <div class="area__subheader triangle triangle-yellow"></div>
        </div>
    </template>
    
    <script>
    import json from '@/static/json/locations.json'
    import { latLng, icon } from 'leaflet'
    export default {
      name: 'Area',
      data: function () {
        return {
          // ############# INTRO VIDEO SETTINGS #############
          videoLanguage: 'English',
          englishSrc: 'https://www.youtube.com/embed/lRKmJqDbVsY',
          spanishSrc: 'https://www.youtube.com/embed/q0WkEkIMmQo',
          currentSrc: 'https://www.youtube.com/embed/lRKmJqDbVsY',
          // ############# IMAGE GALLERY SETTINGS #############
          swiperOption: {
            grabCursor: true,
            loop: true,
            autoplay: {
              delay: 5000,
              disableOnInteraction: false,
            },
            pagination: {
              el: '.swiper-pagination',
            },
            map: this.$refs.map,
          },
          // ############# ARRAY TO STORE GALLERY IMAGES #############
          images: [],
          locations: json.locations,
          // ############# ARRAY TO STORE RESTAURANT OBJECTS #############
          restaurants: [],
          // ############# ARRAY TO STORE HIKING TRAIL OBJECTS #############
          walks: [],
          // ############# LEAFLET HIKING TRAIL MARKER ICON #############
          walkIcon: icon({
            iconUrl: '/MapMarkers/hiking.png',
            iconSize: [20, 20],
            iconAnchor: [16, 37],
          }),
          // ############# LEAFLET RESTAURANT MARKER ICON #############
          eatIcon: icon({
            iconUrl: '/MapMarkers/restaurant.png',
            iconSize: [20, 20],
            iconAnchor: [16, 37],
          }),
          // ############# STORE THE CURRENTLY SELECTED LOCATION DATA  #############
          selectedPin: {
            name: 'Please click on a map pin for more information',
          },
        }
      },
      // ############# IMPORT THE IMAGES ON MOUNT #############
      mounted() {
        this.importImages(require.context('~/assets/AreaPictures/', true))
        this.importLocations(this.locations)
        console.log(this.fas)
      },
      methods: {
        changeSrc() {
          if (this.videoLanguage === 'English') {
            this.currentSrc = this.englishSrc
          } else {
            this.currentSrc = this.spanishSrc
          }
        },
        // ############# IMPORT GALLERY IMAGES #############
        importImages(r) {
          r.keys().forEach((key) => {
            var path = key.substring(1) //-----THE PATH FOR SOME REASON CONTAINS A . SO I REMOVE IT
            this.images.push({
              imageURL: path, //----- CREATE A NEW OBJECT AND ADD IT TO IMAGES
            })
          })
        },
        // ############# SPLIT JSON DATA INTO RESTAURANT AND HIKING TRAIL ARRAYS #############
        importLocations() {
          this.restaurants = this.locations.restaurants
          this.walks = this.locations.walks
        },
        // ############# FIND THE DATA OF THE CURRENTLY SELECTED MAP MARKER BY USING ITS ID #############
        getPin(id) {
          let item
          if (id.includes('W')) {
            for (let i = 0; i < this.walks.length; i++) {
              item = this.walks[i]
              if (item.id === id) {
                this.selectedPin = item
              }
            }
          } else if (id.includes('R')) {
            for (let i = 0; i < this.restaurants.length; i++) {
              item = this.restaurants[i]
              if (item.id === id) {
                this.selectedPin = item
              }
            }
          } else {
            window.alert('Error, no info on marker found')
          }
        },
      },
    }
    </script>
    
    <style lang="scss">
    ...

编辑: 上下文图片

【问题讨论】:

    标签: javascript json vue.js leaflet nuxt.js


    【解决方案1】:

    我的建议是:

    当一个标记被选中时,给 html 元素添加一个类,它会添加一个投影,并缩放图标:

    .markerSelected {
      transform: scale(1.5);
      filter: drop-shadow(0px 0px 10px rgba(0,0,0,.5));
    }
    

    另外,为了捕获事件,我建议使用事件冒泡。为此,请将您的@click 添加到父元素,然后使用event.target 找到单击的图标。

    一旦您发出了事件,请将 selectedId 数据参数设置为所选项目的 ID。我在这里假设walking vs restaurant id 是独一无二的,不会有冲突。

    然后,按如下方式更新 HTML:

    <l-marker
                      v-for="restaurant in restaurants"
                      :key="restaurant.id"
                      :lat-lng="restaurant.latlong"
                      :icon="eatIcon"
                      :class="restaurant.id === selectedId ? 'markerSelected' : ''"
                    ></l-marker>
    

    阅读:Event.Target

    这里还有另一个好帖子:How to bubble events in vue

    【讨论】:

    • 非常感谢您的回复。所以根据我的理解,你推荐使用 $emit?它将向父级发送事件。
    • 我已经实现了这个,我可以从标记调用一个函数到父级,但是我对如何将该类添加到标记感到困惑。另外,如果用户单击另一个标记,我如何将其从标记中删除并将其添加到另一个标记中
    • 您可以向l-marker 添加一个响应式类。我已经添加到上面的回复中
    猜你喜欢
    • 2015-10-14
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多