Как создать интерактивное изображение, подобное карте, на Vue?

Вопрос или проблема

Я пытаюсь создать кастомную карту. У меня есть изображение, и я хочу выбрать некоторые места и информацию. После добавления точек, когда я меняю размер окна, местоположение точек изменяется. Возможно ли сделать так, чтобы точки не меняли своё местоположение на изображении? Вот мой код:

<template>
  <div class="px-20">
    <div class="mt-10 shadow-2xl relative" ref="imageContainer">
      <img :src="BL" class="w-full h-auto cursor-pointer" @click="addPoint" />

      <div
        v-for="(point, index) in points"
        :key="index"
        class="absolute"
        :style="{
          top: `${point.y}px`,
          left: `${point.x}px`
        }"
      >
        <div
          class="bg-red-500 text-white text-xs font-semibold py-1 px-2 rounded-full flex items-center"
        >
          {{ point.label }}
          <button
            @click.stop="removePoint(index)"
            class="bg-red-700 text-white text-xs font-bold rounded-full ml-1 w-4 h-4 flex items-center justify-center"
          >
            x
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import BL from '@/assets/Tm_Map.jpg' // Замените на путь к вашему изображению

const points = ref([])
let pointId = 0
const imageContainer = ref(null)
const resizeObserver = ref(null)

function addPoint(event) {
  const rect = event.target.getBoundingClientRect()
  const x = event.clientX - rect.left // Получаем x-позицию относительно изображения
  const y = event.clientY - rect.top // Получаем y-позицию относительно изображения

  points.value.push({
    x,
    y,
    label: `Точка ${++pointId}`
  })
}

function removePoint(index) {
  points.value.splice(index, 1)
}

</script>

Если возможно, помогите мне!

Ответ или решение

Чтобы создать интерактивное изображение, подобное карте, с фиксированными точками, которые не изменяют свои позиции при изменении размера окна, нужно скорректировать вашу реализацию. Основная проблема состоит в том, что мы должны использовать относительные координаты для размещения точек, которые зависят от размеров родительского контейнера.

Мы можем добиться этого, используя процентные соотношения для координат, а не абсолютные пиксели. Вот шаги, как это реализовать:

Шаги по реализации

  1. Измените хранение точек: Будем использовать относительные координаты для хранения положений точек на изображении.

  2. Обновите обработчик добавления точек: Вместо сохранения координат в пикселях, производите вычисления для сохранения их в процентах относительно размеров изображения.

  3. Расчет позиции при рендеринге: При отображении точек передавайте относительные величины обратно в пиксели в соответствии с текущими размерами изображения.

Вот обновленный код вашего компонента:

Обновленный код

<template>
  <div class="px-20">
    <div class="mt-10 shadow-2xl relative" ref="imageContainer">
      <img :src="BL" class="w-full h-auto cursor-pointer" @click="addPoint" />

      <div
        v-for="(point, index) in points"
        :key="index"
        class="absolute"
        :style="{
          top: `${point.y}%`,
          left: `${point.x}%
        }"
      >
        <div class="bg-red-500 text-white text-xs font-semibold py-1 px-2 rounded-full flex items-center">
          {{ point.label }}
          <button
            @click.stop="removePoint(index)"
            class="bg-red-700 text-white text-xs font-bold rounded-full ml-1 w-4 h-4 flex items-center justify-center"
          >
            x
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import BL from '@/assets/Tm_Map.jpg' // Замените на путь к вашему изображению

const points = ref([])
let pointId = 0
const imageContainer = ref(null)

function addPoint(event) {
  const rect = event.target.getBoundingClientRect()
  const x = ((event.clientX - rect.left) / rect.width) * 100 // Получаем x в процентах
  const y = ((event.clientY - rect.top) / rect.height) * 100 // Получаем y в процентах

  points.value.push({
    x,
    y,
    label: `Point ${++pointId}`
  })
}

function removePoint(index) {
  points.value.splice(index, 1)
}
</script>

<style scoped>
.absolute {
    position: absolute;
}
</style>

Объяснение изменений

  1. Хранение точек: Теперь мы сохраняем координаты как проценты (x и y). Это позволяет точкам сохранять свою позицию относительно размеров изображения, что делает их устойчивыми к изменениям размеров окна.

  2. Отображение: При рендеринге мы используем проценты для установки свойств top и left, тем самым гарантируя корректное отображение независимо от размера контейнера.

  3. Стилизация: Применение position: absolute; для точек позволяет их позиционировать по заданным координатам.

Заключение

Теперь ваши точки будут фиксированы на изображении независимо от его размера, обеспечивая интерактивный опыт. Вы можете добавлять дополнительные стили и функциональность в зависимости от ваших требований.

Оцените материал
Добавить комментарий

Капча загружается...