Skip to content

Добавление маркеров на карту

Совет

Чтобы поведение обычных маркеров соответствовало Yandex Maps 2, задайте:

  • positionX на left-center
  • positionY на top.

position будет сформирован как left-center top.

html
<yandex-map
    :height="height"
    :settings="{
        location: {
            center,
            zoom,
        },
        theme,
        showScaleInCopyrights: true,
    }"
    :width="width"
>
    <yandex-map-default-scheme-layer/>
    <yandex-map-default-features-layer/>
    <yandex-map-controls :settings="{ position: 'right' }">
        <yandex-map-zoom-control/>
    </yandex-map-controls>

    <yandex-map-controls :settings="{ position: 'top left' }">
        <yandex-map-control-button>
            <label>
                Position (X)

                <select v-model="positionX">
                    <option
                        v-for="(key, value) in positionsX"
                        :key="key"
                        :value="value"
                    >
                        {{ key }}
                    </option>
                </select>
            </label>
        </yandex-map-control-button>
        <yandex-map-control-button>
            <label>
                Position (Y)

                <select v-model="positionY">
                    <option
                        v-for="(key, value) in positionsY"
                        :key="key"
                        :value="value"
                    >
                        {{ key }}
                    </option>
                </select>
            </label>
        </yandex-map-control-button>
    </yandex-map-controls>

    <template
        v-for="(point, index) in POINTS"
        :key="index"
    >
        <yandex-map-marker
            v-if="'element' in point"
            :position="`${ positionX } ${ positionY }` as any"
            :settings="point"
            :style="{
                '--color': 'color' in point && point.color,
                '--image': 'colors' in point && diagramBackground(point.colors),
            }"
        >
            <template v-if="point.element === 'diagram'">
                <div
                    v-if="'title' in point"
                    class="pie-marker-title"
                >
                    {{ point.title }}
                </div>
                <div
                    class="pie-marker"
                />
            </template>
            <div
                v-else-if="point.element === 'circle'"
                class="circle"
                :style="{
                    '--radius': 'radius' in point ? point.radius : '20px',
                    '--color': 'color' in point ? point.color : undefined,
                    '--icon': 'icon' in point ? point.icon : '#fff',
                    '--image': 'icon' in point ? point.icon : undefined,
                }"
                :title="'title' in point && point.title"
            >
                <div class="circle_element"/>
            </div>
            <div
                v-else-if="point.element === 'icon'"
                class="icon"
                :style="{
                    '--size': 'size' in point ? point.size : '20px',
                    '--color': 'color' in point ? point.color : undefined,
                    '--icon': 'icon' in point ? `url(${ point.icon })` : undefined,
                }"
            >
                <div
                    v-if="'title' in point"
                    class="icon_title"
                    v-html="point.title"
                />
            </div>
        </yandex-map-marker>
        <yandex-map-default-marker
            v-else
            :settings="point"
        />
    </template>
    <yandex-map-default-marker :settings="{ coordinates: INC_POINT.coordinates, title: markerTitle }"/>
</yandex-map>
ts
import {
    YandexMap,
    YandexMapControlButton,
    YandexMapControls,
    YandexMapDefaultFeaturesLayer,
    YandexMapDefaultMarker,
    YandexMapDefaultSchemeLayer,
    YandexMapMarker,
    YandexMapZoomControl,
} from 'vue-yandex-maps';
import { onMounted, onUnmounted, ref } from 'vue';
import type { LngLat } from '@yandex/ymaps3-types';

type PartialRecord<K extends keyof any, T> = {
    [P in K]?: T;
};

const INC_POINT = {
    coordinates: [37.95, 55.9] as LngLat,
    title: 'Marker inc #0',
};

const markerTitle = ref('');

const positionsX = {
    left: 'left',
    right: 'right',
    'right-center': 'right-center',
    'left-center': 'left-center',

    custom: '-25%',

    default: 'default',
} satisfies PartialRecord<YandexMapMarkerPosition | 'custom', string>;

const positionsY = {
    top: 'top',
    bottom: 'bottom',
    'top-center': 'top-center',
    'bottom-center': 'bottom-center',

    custom: '-25%',

    default: 'default',
} satisfies PartialRecord<YandexMapMarkerPosition | 'custom', string>;

const positionX = ref<keyof typeof positionsX>('default');
const positionY = ref<keyof typeof positionsY>('default');

onMounted(() => {
    let inc = 0;
    const updateTitle = () => {
        inc++;
        markerTitle.value = `Marker inc #${ inc }`;
    };
    updateTitle();
    const timer = setInterval(updateTitle, 1000);
    onUnmounted(() => {
        clearInterval(timer);
    });
});

const diagramBackground = (colors: { percentage: number; color: string }[]): string => {
    const gradient = [];
    let previous = 0;
    for (let i = 0; i < colors.length; i += 1) {
        const p = colors[i];
        const deg = (360 / 100) * p.percentage;
        gradient.push(`${ p.color } ${ previous }deg ${ previous + deg }deg`);
        previous += deg;
    }

    return `conic-gradient(${ gradient.join(', ') })`;
};

const POINTS: any[] = [
    { coordinates: [37.8, 55.8] },
    {
        coordinates: [37.6, 55.847],
        title: 'Diagram',
        color: '#0E4779',
        draggable: true,
        colors: [
            {
                percentage: 30,
                color: '#0E4779',
            },
            {
                percentage: 20,
                color: '#1E98FF',
            },
            {
                percentage: 40,
                color: '#82CDFF',
            },
            {
                percentage: 10,
                color: '#ff9f82',
            },
        ],
        element: 'diagram',
    },
    {
        coordinates: [37.738521, 55.684758],
        color: '#0095b6',
        title: 'color <strong>bondi beach water<strong>',
        draggable: true,
    },
    {
        coordinates: [37.715175, 55.833436],
        color: '#735184',
        title: '<strong>Silver crimson<strong> color',
        draggable: true,
    },
    {
        coordinates: [37.529789, 55.687086],
        color: '#3caa3c',
        title: 'love toad color',
        draggable: true,
        element: 'circle',
    },
    {
        coordinates: [37.95, 55.782392],
        color: 'yellow',
        title: 'color <strong>sun<strong>',
        draggable: true,
        onClick: () => alert('click'),
    },
    {
        coordinates: [37.656123, 55.642063],
        title: 'color <strong>red<strong>',
        size: '60px',
        icon: 'data:image/png;base64,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',
        draggable: true,
        element: 'icon',
        onDoubleClick: () => alert('Double click'),
    },
    {
        coordinates: [37.487208, 55.826479],
        title: 'the color of <strong>Pacific Ocean<strong>',
        color: '#3b5998',
        draggable: true,
        mapFollowsOnDrag: true,
    },
    {
        coordinates: [37.435023, 55.694843],
        color: '#477510',
        title: 'nose color Donatello',
        subtitle: 'Very long but incredibly interesting text',
        draggable: true,
    },
    {
        coordinates: [37.535023, 55.6],
        color: '#343d44',
        title: 'Hello!',
        subtitle: 'Very long but <br>incredibly interesting text',
        draggable: true,
    },
    {
        coordinates: [37.814052, 55.790139],
        title: 'blue color',
        icon: 'url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMiIgaGVpZ2h0PSIyMiI+PHBhdGggZmlsbD0iIzgwODI4NSIgZD0iTTEyLjc0IDUuNzlWMy40N2wtNS44IDEuMTZINS4yMnYxMy45aDEuNzR2LTYuOTVsMy40Ny0uNTh2Mi4zMmw3LjUzLS41OHYtNy42bC01LjIxLjY1eiIvPjwvc3ZnPg==)',
        color: '#51aabd',
        radius: '50px',
        draggable: true,
        element: 'circle',
        onFastClick: () => alert('Fast click'),
    },
];
css
<style scoped>
select {
  border: 1px solid #000;
  padding-left: 5px;
}

.pie-marker-title {
  position: absolute;
  top: 120%;
  left: 50%;
  padding: 2px 4px;
  background-color: #fff;
  transform: translateX(-50%);
  color: var(--color);
}

.pie-marker {
  background-color: currentColor;
  background-image: var(--image);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  overflow: hidden;
}

.circle {
  width: var(--radius);
  height: var(--radius);
  border-radius: 50%;
  background-color: currentColor;
  color: var(--color);
}

.circle_element {
  position: absolute;
  top: 50%;
  left: 50%;
  display: inline-block;
  width: 50%;
  height: 50%;
  border-radius: 50%;
  background: #fff var(--image) no-repeat center center;
  transform: translate3d(-50%, -50%, 0);
}

.icon {
  position: relative;
  width: var(--size);
  height: var(--size);
  color: var(--color);
  background: var(--icon) no-repeat center center / contain;
}

.icon_title {
  position: absolute;
  top: 120%;
  left: 50%;
  padding: 2px 4px;
  background-color: #fff;
  transform: translateX(-50%);
}
</style>

Сделано с ♥ под лицензией MIT.