UNPKG

wangbd-myui

Version:

MY前端基础框架核心组件库

504 lines (491 loc) 13.5 kB
/** * 地图基础图层组件 * @module $ui/map/my-map * */ import Map from 'ol/Map' import View from 'ol/View' import DragPan from 'ol/interaction/DragPan' import MouseWheelZoom from 'ol/interaction/MouseWheelZoom' import PointerInteraction from 'ol/interaction/Pointer'; import VectorLayer from 'ol/layer/Vector' import VectorSource from 'ol/source/Vector' import {createLayer} from '../../utils/layer' import {addResizeListener, removeResizeListener} from 'element-ui/lib/utils/resize-event' export default { name: 'MyMap', provide() { return { myMap: this } }, inject: { page: {default: null} }, /** * 属性参数 * @member props * @property {number} [zoom=10] 地图初始化层级 * @property {number} [minZoom=1] 最小支持的层级 * @property {number} [maxZoom=20] 最大支持的层级 * @property {number[]} [center] 地图初始化中心经纬度 * @property {String|Object|Function} [adapter=OSM] 图层适配器,类型:OSM|XYZ|Amap|Baidu|TDT|Founder|Ez|Super|Fc 对象时 {id, type, url, layers(天地图专用)} * @property {String} [projection=EPSG:4326] 投影坐标系,支持 EPSG:4326 (WGS84) 或 EPSG:3857 (Pseudo-Mercator) * @property {String} [width=100%] 宽度,支持百分比 * @property {String} [height=400px] 高度,支持百分比 * @property {Boolean} [dragPan=true] 可拖拽移动图层 * @property {Boolean} [mouseWheelZoom=true] 鼠标滚轮可缩放图层 * @property {Object} [mapOptions] ol/Map 实例化参数选项 * @property {Object} [viewOptions] ol/View 实例化参数选项 */ props: { // 初始化缩放层级 zoom: { type: Number, default: 10 }, minZoom: { type: Number, default: 1 }, maxZoom: { type: Number, default: 20 }, // 初始化中心经纬度 center: { type: Array, default() { return [113.261999, 23.130592] } }, // 图层适配器, {id,type, url, layers(天地图专用)} adapter: { type: [String, Object, Function], default: 'Baidu' }, // 投影标准 projection: { type: String, default: 'EPSG:4326' }, width: { type: String, default: '100%' }, height: { type: String, default: '400px' }, // 可拖拽移动图层 dragPan: { type: Boolean, default: true }, // 鼠标滚轮可缩放图层 mouseWheelZoom: { type: Boolean, default: true }, // ol/Map 实例化参数选项 mapOptions: Object, // ol/View 实例化参数选项 viewOptions: Object, // 颜色反相 invert: Boolean, // 滤镜 filter: Object }, render() { return ( <div class={this.classes} style={this.styles}> {this.$slots.default} </div> ) }, data() { return { cursor: null, viewWidth: 0, viewHeight: 0 } }, computed: { classes() { return { 'my-map': true, 'my-map--invert': this.invert } }, styles() { return { width: this.width, height: this.height, cursor: this.cursor ? 'pointer' : '' } }, filterStyle() { if (!this.filter) return null const filters = Object.entries(this.filter).map(([name, value]) => { if (name === 'hue-rotate') { return `${name}(${value}deg)` } return `${name}(${value})` }) return filters.join(' ') } }, watch: { dragPan(val) { this.setActive(DragPan, val) }, mouseWheelZoom(val) { this.setActive(MouseWheelZoom, val) }, filterStyle() { this.setCanvasFilter() } }, methods: { /** * 设置是否激活交互工具 * @method setActive * @param {Object} name 工具类 * @param {Boolean} active 是否激活 */ setActive(name, active) { if (!this.map) return const interactions = this.map.getInteractions().getArray() const instance = interactions.find(n => n instanceof name) if (instance) { instance.setActive(active) } }, /** * 实例化Map */ init() { const {center, projection, zoom, minZoom, maxZoom} = this.$props const layer = createLayer(this.adapter) // 标识图层 layer.__MY_LAYER__ = true const view = new View({ ...(this.viewOptions || {}), projection, center, zoom, minZoom, maxZoom }) this.map = new Map({ ...(this.mapOptions || {}), layers: [].concat(layer), view: view, target: this.$el, // 删除默认的控件 controls: [], interactions: [ new DragPan(), new MouseWheelZoom(), new PointerInteraction({ handleEvent: this.handleEvent }) ] }) this.setActive(DragPan, this.dragPan) this.setActive(MouseWheelZoom, this.mouseWheelZoom) this.bindMapEvents() /** * 地图初始化完成时触发 * @event ready * @param {Map} map ol/Map实例 * @param {VueComponent} vm Vue实例 */ this.$emit('ready', this.map, this) addResizeListener(this.$el, this.setViewSize) this.setCanvasFilter() }, /** * 销毁Map * @method dispose */ dispose() { if (!this.map) return this.map.disposeInternal() this.unbindMapEvents() removeResizeListener(this.$el, this.setViewSize) }, /** * 根据地图上的像素位置获取图形对象 * @method getFeatureAtPixel * @param {Number[]} pixel 位置像素 [x,y] * @return {Feature} */ getFeatureAtPixel(pixel) { if (!this.map) return null return this.map.forEachFeatureAtPixel(pixel, feature => feature) }, /** * 当图形可点击时,设置图形鼠标 cursor pointer * @method setCursor * @param {ol/Feature} feature */ setCursor(feature) { const vm = this.getFeatureVM(feature) this.cursor = vm && (vm.cursor || vm.$listeners.click) }, /** * 触发图形点击事件 * @method clickTrigger * @param {ol/Feature} feature * @param {Object} e */ clickTrigger(feature, e, eventName = 'click') { if (feature) { const vm = this.getFeatureVM(feature) if (vm) { vm.$emit(eventName, e, feature) } } }, /** * 获取瓦片图层实例 * @method getLayer * @returns {Array} */ getLayer() { if (!this.map) return null return this.map.getLayers().getArray().find(n => n.__MY_LAYER__) }, /** * 获取图形挂载的 Vue实例 * @method getFeatureVM * @param feature * @returns {*} */ getFeatureVM(feature) { if (!feature) return const features = feature.get('features') || [] const matchFeature = features[0] || feature return matchFeature.__vm__ || matchFeature.get('__vm__') }, enterAndLeaveTrigger(feature, e) { // 无>有:鼠标移入 if (!this.featureAtPixel && feature) { this.$emit('mouseenter', e, feature) const vm = this.getFeatureVM(feature) vm && vm.$emit('mouseenter', e, feature) } // 有>无: 鼠标移出 if (this.featureAtPixel && !feature) { this.$emit('mouseleave', e, this.featureAtPixel) const vm = this.getFeatureVM(this.featureAtPixel) vm && vm.$emit('mouseleave', e, this.featureAtPixel) } // 同有,但不等:一出一进 if (this.featureAtPixel && feature && this.featureAtPixel !== feature) { this.$emit('mouseleave', e, this.featureAtPixel) this.$emit('mouseenter', e, feature) const vmAtPixel = this.getFeatureVM(this.featureAtPixel) vmAtPixel && vmAtPixel.$emit('mouseleave', e, this.featureAtPixel) const vm = this.getFeatureVM(feature) vm && vm.$emit('mouseenter', e, feature) } // 更新 this.featureAtPixel = feature }, handleEvent(e) { const type = e.type const page = this.page || {} const {widthScale = 1, heightScale = 1} = page const [x, y] = e.pixel const pixel = [x / widthScale, y / heightScale] const feature = this.getFeatureAtPixel(pixel) switch (type) { case 'pointermove': this.setCursor(feature) this.enterAndLeaveTrigger(feature, e) break case 'click': this.clickTrigger(feature, e) break default: this.clickTrigger(feature, e, type) break } return true }, /** * 透传Map事件 */ bindMapEvents() { if (!this.map) return Object.entries(this.$listeners) .forEach(([key, handler]) => this.map.on(key, handler)) }, /** * 销毁透传Map事件 */ unbindMapEvents() { if (!this.map) return Object.entries(this.$listeners) .forEach(([key, handler]) => this.map.un(key, handler)) }, /** * 重置地图尺寸,当容器的尺寸变化后需要执行resize * @method resize */ resize() { this.map.updateSize() }, /** * 移动到指定经纬度居中 * @method moveTo * @param {Number[]} loc 经纬度数组 */ moveTo(loc) { if (!this.map) return const view = this.map.getView() view.animate( { center: loc, duration: 200 }) }, /** * 设置地图缩放等级 * @method zoomTo * @param {Number|String} level 等级数值 */ zoomTo(level) { if (!this.map) return const view = this.map.getView() view.animate( { zoom: Number.parseInt(level), duration: 200 }) }, /** * 逐步放大 * @method zoomIn */ zoomIn() { if (!this.map) return const view = this.map.getView() this.zoomTo(view.getZoom() + 1) }, /** * 逐步缩小 * @method zoomOut */ zoomOut() { if (!this.map) return const view = this.map.getView() this.zoomTo(view.getZoom() - 1) }, /** * 地图初始化完成回调 * @method mapReady * @param callback */ mapReady(callback) { if (this.map) { callback && callback(this.map, this) return } this.$once('ready', callback) }, /** * 创建矢量图层,只会创建一个图层,已创建即复用 */ createVectorLayer() { if (!this.map || this.vectorLayer) return const vectorSource = new VectorSource() this.vectorLayer = new VectorLayer({ source: vectorSource, zIndex: 1 }) this.map.addLayer(this.vectorLayer) return this.vectorLayer }, /** * 在地图上添加图形, 图形加入到矢量图层 * @method addFeature * @param {Feature[]|feature} feature */ addFeature(feature) { if (!this.map) return if (!this.vectorLayer) { this.createVectorLayer() } const source = this.vectorLayer.getSource() const features = [].concat(feature) source.addFeatures(features) }, /** * 删除图形 * @method removeFeature * @param {Feature[]|feature} feature */ removeFeature(feature) { if (!this.vectorLayer) return const source = this.vectorLayer.getSource() const features = [].concat(feature) features.forEach(feat => source.removeFeature(feat)) }, /** * 获取每个投影单位代表的距离米 * @method getMetersPerUnit * @return {*|number|number} */ getMetersPerUnit() { if (!this.map) return 0 const view = this.map.getView() const projection = view.getProjection() return projection.getMetersPerUnit() }, /** * 更新视图可视区尺寸 */ setViewSize() { const rect = this.$el.getBoundingClientRect() this.viewWidth = rect.width || 0 this.viewHeight = rect.height || 0 if (this.map) { this.map.updateSize() } this.$emit('view-size-change', rect) }, setCanvasFilter() { if (!this.map) return const viewport = this.map.getViewport() if (viewport) { viewport.style.filter = this.filterStyle } }, /** * 清空所有覆盖物 */ clear() { if (!this.map) return const layers = this.map .getLayers() .getArray() .filter(n => !n.__MY_LAYER__) layers.forEach(n => { n.getSource().clear() }) const children = this.$children || [] children.forEach(n => { n.clear && n.clear() }) } }, mounted() { this.init() }, beforeDestroy() { this.dispose() } }