wangbd-myui
Version:
MY前端基础框架核心组件库
504 lines (491 loc) • 13.5 kB
JavaScript
/**
* 地图基础图层组件
* @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()
}
}