diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..106009f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}\\vue-yandex-maps.js" + } + ] +} \ No newline at end of file diff --git a/README.MD b/README.MD index 19f5452..68ea0b8 100644 --- a/README.MD +++ b/README.MD @@ -52,7 +52,9 @@ You can use the CDN: https://unpkg.com/vue-yandex-maps, `yandexMap` is exposed t Use `` tag to enable the map instance and use attributes to define map options. -`` has a class `ymap-container` and child element with class `ymap-body`, where rendering map instance. +`` has a class `ymap-container` and child element, where rendering map instance. Child class you may define through map attribute `ymap-class`. If you doesn't define this class - child element will have `style` attribute with `width: 100%; height: 100%;` + +If you have a lot of markers on your map i strongly recommend you to use map attribute `useObjectManager`. But in this case you can't set callbacks to your markers through marker attribute `callbacks`. You may define placemarks on your map by using `` tag or set an array of objects with placemark options through `` attribute `placemarks` ([interface description](https://tech.yandex.ru/maps/doc/jsapi/2.0/ref/reference/GeoObject-docpage/)). You also can use both methods together.
The Map instance rerender when changed array with markers in `placemarks` property or marker properties if marker is a component.
@@ -155,6 +157,9 @@ data() { | scroll-zoom | Boolean | Set to false to disable zoom map on scroll page | | zoom-control | Object | Configs for zoomControl of the map. [More](https://tech.yandex.ru/maps/doc/jsapi/2.1/ref/reference/control.ZoomControl-docpage/) | | placemarks | Array of Objects | Array of config objects with fields: coordinates ([lat, lon]), properties, options. [More](https://tech.yandex.ru/maps/doc/jsapi/2.1/ref/reference/Placemark-docpage/) | +| use-object-manager | Boolean | Defines using Object Mananger in map. Default: false | +| object-manager-clusterize | Boolean | Defines using clusterize in Object Mananger. Default: true | +| ymap-class | String | Defines class for element, where rendering map instance | ## Marker attributes diff --git a/package-lock.json b/package-lock.json index e6a12f8..eded864 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "vue-yandex-maps", - "version": "0.6.6", + "version": "0.6.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6dd74fc..910a99b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-yandex-maps", - "version": "0.6.10", + "version": "0.7.0", "description": "Yandex Maps component for VueJS.", "main": "vue-yandex-maps.js", "jsnext:main": "./src/index.js", diff --git a/src/Marker.js b/src/Marker.js index 3f73943..327ff60 100644 --- a/src/Marker.js +++ b/src/Marker.js @@ -2,7 +2,8 @@ import { compareValues, emitter } from './utils'; export default { data: () => ({ - ymapEventBus: emitter + ymapEventBus: emitter, + unwatchArr: [] }), props: { coords: { @@ -34,7 +35,10 @@ export default { }, mounted() { for (let prop in this.$props) { - this.$watch(prop, (newVal, oldVal) => compareValues(newVal, oldVal, this.ymapEventBus)); + this.unwatchArr.push(this.$watch(prop, (newVal, oldVal) => compareValues(newVal, oldVal, this.ymapEventBus))); } + }, + beforeDestroy() { + this.unwatchArr.forEach(f => f()); } } diff --git a/src/YMap.js b/src/YMap.js index 204dd4a..8fa2c08 100644 --- a/src/YMap.js +++ b/src/YMap.js @@ -5,7 +5,8 @@ export default { return { ymapEventBus: utils.emitter, ymapId: 'yandexMap' + Math.round(Math.random() * 100000), - myMap: {} + myMap: {}, + style: this.ymapClass ? '' : 'width: 100%; height: 100%;' } }, props: { @@ -65,7 +66,16 @@ export default { default() { return []; } - } + }, + useObjectManager: { + type: Boolean, + default: false + }, + objectManagerClusterize: { + type: Boolean, + default: true + }, + ymapClass: String }, computed: { coordinates() { @@ -75,8 +85,8 @@ export default { methods: { init() { if (!window.ymaps || !ymaps.GeoObjectCollection) return; // if ymap isn't initialized; + this.$emit('map-initialization-started'); let markers = []; - let myGeoObjects = new ymaps.GeoObjectCollection(); this.myMap = new ymaps.Map(this.ymapId, { center: this.coordinates, @@ -140,13 +150,14 @@ export default { } return marker }).filter(marker => marker && marker.markerType) || []; - + for (let i = 0; i < myMarkers.length; i++) { const m = myMarkers[i]; - const markerType = utils.setFirstLetterToUppercase(m.markerType); + const markerType = utils.createMarkerType(m.markerType, this.useObjectManager); let properties = { hintContent: m.hintContent, iconContent: m.icon && m.icon.content, + markerId: m.markerId }; const balloonProps = m.balloon ? { @@ -183,42 +194,32 @@ export default { if (markerType === 'Circle') { m.coords = [m.coords, m.circleRadius]; } - let marker = new ymaps[markerType](m.coords, properties, options); - utils.createCallbacks(m, marker); - marker.clusterName = m.clusterName; - marker.properties.set('markerId', m.markerId); + const obj = { properties, options, markerType, coords: m.coords, clusterName: m.clusterName, callbacks: m.callbacks } + const marker = utils.createMarker(obj, this.useObjectManager); markers.push(marker); - myGeoObjects.add(marker); } if (this.placemarks) { - this.placemarks.forEach(function(placemark) { - let yplacemark = new ymaps.Placemark ( - placemark.coords, - placemark.properties || {}, - placemark.options || {} - ); - - utils.createCallbacks(placemark, yplacemark); - - if (placemark.clusterName) { - yplacemark.clusterName = placemark.clusterName; - markers.push(yplacemark); - } + const markerType = this.useObjectManager ? 'Point' : 'Placemark'; + this.placemarks.forEach(placemark => { + const { properties, options, coords, clusterName, callbacks } = placemark; + const obj = { properties, options, markerType, coords, clusterName, callbacks } + let yplacemark = utils.createMarker(obj, this.useObjectManager); - myGeoObjects.add(yplacemark); + markers.push(yplacemark); }) } - this.myMap.geoObjects.add(myGeoObjects); const config = { options: this.clusterOptions, callbacks: this.clusterCallbacks, - map: this.myMap + map: this.myMap, + useObjectManager: this.useObjectManager, + objectManagerClusterize: this.objectManagerClusterize }; - utils.createClusters(markers, config); + utils.addToCart(markers, config); this.$emit('map-was-initialized', this.myMap); } }, @@ -243,7 +244,8 @@ export default { { attrs: { id: this.ymapId, - class: 'ymap-body' + class: this.ymapClass, + style: this.style } } ), @@ -298,6 +300,7 @@ export default { } }, beforeDestroy() { + this.myMap.GeoObjects && this.myMap.GeoObjects.removeAll(); this.observer.disconnect(); } } diff --git a/src/utils.js b/src/utils.js index bc05dc7..741ed2a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,15 +1,19 @@ -export function createCallbacks(marker, placemark) { - if (marker.callbacks && typeof marker.callbacks === 'object') { - for (let key in marker.callbacks) { - placemark.events.add(key, marker.callbacks[key]); +export function createCallbacks(callbacks, placemark) { + if (callbacks && typeof callbacks === 'object') { + for (let key in callbacks) { + placemark.events.add(key, callbacks[key]); } } } -export function createClusters(markers, { options, callbacks, map }) { +export function addToCart(markers, { options, callbacks, map, useObjectManager, objectManagerClusterize }) { let clusters = {}; + let unclastered = []; for (let marker of markers) { - if (!marker.clusterName) continue; + if (!marker.clusterName) { + unclastered.push(marker); + continue; + }; clusters[marker.clusterName] = clusters[marker.clusterName] ? [...clusters[marker.clusterName], marker] : [marker]; } for (let clusterName in clusters) { @@ -17,12 +21,26 @@ export function createClusters(markers, { options, callbacks, map }) { const clusterCallbacks = callbacks[clusterName] || {}; const layout = clusterOptions.layout; clusterOptions.clusterBalloonItemContentLayout = ymaps.templateLayoutFactory.createClass(layout); - const clusterer = new ymaps.Clusterer(clusterOptions); - for (let key in clusterCallbacks) { - clusterer.events.add(key, clusterCallbacks[key]); + if (useObjectManager) { + const ObjectManager = new ymaps.ObjectManager(Object.assign({ clusterize: objectManagerClusterize }, clusterOptions)); + for (let key in clusterCallbacks) { + ObjectManager.clusters.events.add(key, clusterCallbacks[key]); + } + ObjectManager.add(clusters[clusterName]); + map.geoObjects.add(ObjectManager); + } else { + const clusterer = new ymaps.Clusterer(clusterOptions); + for (let key in clusterCallbacks) { + clusterer.events.add(key, clusterCallbacks[key]); + } + clusterer.add(clusters[clusterName]); + map.geoObjects.add(clusterer); } - clusterer.add(clusters[clusterName]); - map.geoObjects.add(clusterer); + } + if (unclastered.length) { + const unclasteredMarkers = useObjectManager ? new ymaps.ObjectManager({ clusterize: false }) : new ymaps.GeoObjectCollection(); + unclasteredMarkers.add(unclastered); + map.geoObjects.add(unclasteredMarkers); } } @@ -124,5 +142,36 @@ const CONTROL_TYPES = [ export function controlsTypeValidator(val) { return val.filter(control => ![...CONTROL_TYPES, 'default'].includes(control)).length === 0 -} +} + +export function createMarkerType(val, useObjectManager) { + const type = setFirstLetterToUppercase(val); + if (!useObjectManager) return type; + switch(type) { + case 'Placemark': + return 'Point'; + case 'Polyline': + return 'LineString'; + default: + return type; + } +} + +export function createMarker(object, useObjectManager) { + let marker = useObjectManager ? { + type: 'Feature', + id: object.properties.markerId, + geometry: { + type: object.markerType, + coordinates: object.coords + }, + properties: object.properties, + options: object.options, + clusterName: object.clusterName + } : new ymaps[markerType](object.coords, object.properties, object.options); + + if (!useObjectManager) createCallbacks(object.callbacks, marker); + + return marker; +} \ No newline at end of file