diff --git a/src/components/MapBox.vue b/src/components/MapBox.vue index b6673e8..a1a2879 100644 --- a/src/components/MapBox.vue +++ b/src/components/MapBox.vue @@ -19,58 +19,59 @@ export default { wayLngLats: [], // 航线 不包括起点航点 takeoffLngLats: [], // 航线 第一个航点 起点 最后一个航点 isflow: false, // 飞机经纬度变化时是否跟随飞机 + currentStyleIndex: 0, // 当前选中的地图样式索引 // 地图样式 - GoogleRasterStyle: { + mapStyles: [ // mapbox官方样式 卫星地图 - name: 'Mapbox Streets', - sprite: this.$store.state.settings.host + '/Public/map/sprite', - glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', - version: 8, - sources: { - google: { - type: 'raster', - tileSize: 256, - tiles: ['https://sb.im/google-maps/vt?lyrs=s&x={x}&y={y}&z={z}'] - } + { + name: '谷歌卫星', + sprite: this.$store.state.settings.host + '/Public/map/sprite', + glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', + version: 8, + sources: { + google: { + type: 'raster', + tileSize: 256, + tiles: ['https://sb.im/google-maps/vt?lyrs=s&x={x}&y={y}&z={z}'] + } + }, + layers: [{ id: 'GoogleRasterLayer', type: 'raster', source: 'google' }] }, - layers: [{ id: 'GoogleRasterLayer', type: 'raster', source: 'google' }] - }, - MapBoxglRasterStyle: 'mapbox://styles/mapbox/outdoors-v12', // mapbox官方样式 矢量街道 - MapBoxglSatellite: 'mapbox://styles/mapbox/satellite-streets-v12', // mapbox官方样式 卫星街道 - GaodeVectorStyle: { // 第三方 高德矢量 - name: 'Gaode Vector', - sprite: this.$store.state.settings.host + '/Public/map/sprite', - glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', - version: 8, - sources: { - gaode: { - type: 'raster', - tileSize: 256, // 瓦片大小 256 512像素 - tiles: [ - 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}' - ] - } + { + name: '高德矢量', + sprite: this.$store.state.settings.host + '/Public/map/sprite', + glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', + version: 8, + sources: { + gaode: { + type: 'raster', + tileSize: 256, // 瓦片大小 256 512像素 + tiles: [ + 'http://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}' + ] + } + }, + layers: [{ id: 'GaodeVectorLayer', type: 'raster', source: 'gaode' }] }, - layers: [{ id: 'GaodeVectorLayer', type: 'raster', source: 'gaode' }] - }, - GaodeRasterStyle: { // 第三方 高德卫星 - name: 'Gaode Raster', - sprite: this.$store.state.settings.host + '/Public/map/sprite', - glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', - version: 8, - sources: { - gaode: { - type: 'raster', - tileSize: 256, // 瓦片大小 256 512像素 - tiles: [ - 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}' - ] - } - }, - layers: [{ id: 'GaodeRasterLayer', type: 'raster', source: 'gaode' }] - } + { + name: '高德卫星', + sprite: this.$store.state.settings.host + '/Public/map/sprite', + glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf', + version: 8, + sources: { + gaode: { + type: 'raster', + tileSize: 256, // 瓦片大小 256 512像素 + tiles: [ + 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}' + ] + } + }, + layers: [{ id: 'GaodeRasterLayer', type: 'raster', source: 'gaode' }] + } + ] } }, computed: { @@ -159,7 +160,7 @@ export default { // 实例化map this.map = new mapboxgl.Map({ container: 'map', - style: this.GoogleRasterStyle, + style: this.mapStyles[0], center: this.defaultLnglat, zoom: this.defaultZoom, pitch: 0, @@ -174,15 +175,9 @@ export default { // 全屏 并再之后 刷新地图 this.map.addControl(new CustomFullscreenControl(this.handleResize), 'top-right') - // 地图样式选择 - // 默认样式(可以根据需要修改或添加更多样式) - MapboxStyleSwitcherControl.DEFAULT_STYLES = [ - { title: '谷歌卫星', uri: 'mapbox://styles/mapbox/streets-v11' }, - { title: '高德卫星', uri: 'mapbox://styles/mapbox/satellite-v9' } - // 你可以添加更多样式 - ] // 地图样式选择控件 - const styleSwitcherControl = new MapboxStyleSwitcherControl([], 'iconfont icon-duozhang f-s-20') + // 自定义地图样式列表 + const styleSwitcherControl = new MapboxStyleSwitcherControl(this.mapStyles, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex) this.map.addControl(styleSwitcherControl, 'top-right') // 飞机跟随 @@ -597,5 +592,4 @@ export default { background-size: cover; cursor: pointer; } - diff --git a/src/utils/mapboxgl_plugs/index.js b/src/utils/mapboxgl_plugs/index.js index 228e441..e10947a 100644 --- a/src/utils/mapboxgl_plugs/index.js +++ b/src/utils/mapboxgl_plugs/index.js @@ -1,58 +1,142 @@ import mapboxgl from 'mapbox-gl' +/** + * 自定义地图样式切换控件 + * 用于在 Mapbox 地图上添加一个控件,允许用户选择不同的地图样式 + */ export class MapboxStyleSwitcherControl { - constructor (styles = [], defaultIconClass = '') { - this.styles = styles.length ? styles : MapboxStyleSwitcherControl.DEFAULT_STYLES - this.defaultIconClass = defaultIconClass // 用于设置自定义图标的类名 - this.controlContainer = null - this.map = null - this.styleButton = null - this.styleDropdown = null + /** + * 构造函数 + * @param {Array} styles - 可选的样式列表,每个样式包含 name 和 URI。默认为空数组。 + * @param {String} defaultIconClass - 自定义图标的 CSS 类名。默认为空字符串。 + * @param {Number} currentStyleIndex - 当前选中的样式索引。默认为 0。 + */ + constructor (styles = [], defaultIconClass = '', currentStyleIndex = 0) { + this.styles = styles + this.defaultIconClass = defaultIconClass + this.currentStyleIndex = currentStyleIndex // 当前样式索引 + this.controlContainer = null // 控件容器 + this.map = null // 地图实例 + this.styleButton = null // 切换样式的按钮 + this.styleDropdown = null // 样式选择的下拉菜单 } + /** + * 添加控件到地图上 + * @param {MapboxMap} map - Mapbox 地图实例 + * @returns {HTMLElement} 控件的容器元素 + */ onAdd (map) { this.map = map + // 创建控件的容器元素 this.controlContainer = document.createElement('div') this.controlContainer.className = 'mapboxgl-ctrl mapboxgl-ctrl-group' + this.controlContainer.style.display = 'flex' + this.controlContainer.style.flexDirection = 'column' + this.controlContainer.style.alignItems = 'center' + this.controlContainer.style.justifyContent = 'center' + this.controlContainer.style.overflow = 'hidden' + // 创建切换样式的按钮 this.styleButton = document.createElement('button') this.styleButton.className = this.defaultIconClass || 'style-switcher-button' // 如果提供了自定义类名,则使用它 - this.styleButton.onclick = this._toggleDropdown.bind(this) + this.styleButton.onclick = this._toggleDropdown.bind(this) // 绑定按钮点击事件 + // 让按钮内容(图标)居中 + this.styleButton.style.display = 'flex' + this.styleButton.style.alignItems = 'center' + this.styleButton.style.justifyContent = 'center' + this.styleButton.style.width = '29px' // 可以根据需要调整 + this.styleButton.style.height = '29px' // 可以根据需要调整 + + // 创建样式选择的下拉菜单 this.styleDropdown = document.createElement('div') this.styleDropdown.className = 'style-dropdown' - this.styleDropdown.style.display = 'none' - this.styles.forEach(style => { + this.styleDropdown.style.display = 'none' // 初始状态为隐藏 + this.styleDropdown.style.cursor = 'pointer' // 鼠标样式 + + // 为每个样式创建选择项并添加到下拉菜单中 + this.styles.forEach((style, index) => { const styleOption = document.createElement('div') styleOption.className = 'style-option' - styleOption.innerHTML = style.title - styleOption.onclick = () => this._changeStyle(style.uri) + styleOption.innerHTML = style.name // 使用 name 作为显示内容 + styleOption.style.padding = '5px 5px' + styleOption.style.borderTop = '1px solid #ddd' + styleOption.style.overflow = 'hidden' + // 如果当前样式索引与选中样式索引匹配,添加高亮样式 + if (this.currentStyleIndex === index) { + styleOption.style.color = '#409EFF' // 设置选中项字体颜色 + } else { + styleOption.style.color = 'black' // 设置默认字体颜色 + } + styleOption.onclick = () => this._changeStyle(style, index) // 绑定样式选择事件 + + // 添加鼠标经过效果 + styleOption.addEventListener('mouseover', () => { + styleOption.style.backgroundColor = '#f0f0f0' // 鼠标经过时的背景颜色 + }) + + styleOption.addEventListener('mouseout', () => { + styleOption.style.backgroundColor = 'transparent' // 鼠标移开时的背景颜色 + }) + this.styleDropdown.appendChild(styleOption) }) + // 将按钮和下拉菜单添加到控件容器中 this.controlContainer.appendChild(this.styleButton) this.controlContainer.appendChild(this.styleDropdown) return this.controlContainer } + /** + * 从地图上移除控件 + */ onRemove () { this.controlContainer.parentNode.removeChild(this.controlContainer) this.map = null } + /** + * 切换下拉菜单的显示状态 + */ _toggleDropdown () { if (this.styleDropdown.style.display === 'none') { - this.styleDropdown.style.display = 'block' + this.styleDropdown.style.display = 'block' // 显示下拉菜单 + this.styleButton.style.width = '100%' // 可以根据需要调整 } else { - this.styleDropdown.style.display = 'none' + this.styleDropdown.style.display = 'none' // 隐藏下拉菜单 + this.styleButton.style.width = '29px' // 可以根据需要调整 } } - _changeStyle (styleURI) { - this.map.setStyle(styleURI) - this.styleDropdown.style.display = 'none' + /** + * 切换地图样式 + * @param {Object} style - 要切换到的样式对象 + * @param {Number} index - 要切换到的样式索引 + */ + _changeStyle (style, index) { + this.map.setStyle(style) // 设置地图样式 + this.styleDropdown.style.display = 'none' // 隐藏下拉菜单 + this.styleButton.style.width = '29px' // 可以根据需要调整 + this.currentStyleIndex = index // 更新当前选中的样式索引 + this._updateStyleOptions() // 更新下拉菜单中的样式选项 + } + + /** + * 更新下拉菜单中的样式选项 + */ + _updateStyleOptions () { + const styleOptions = this.styleDropdown.querySelectorAll('.style-option') + styleOptions.forEach((option, index) => { + if (this.currentStyleIndex === index) { + option.style.color = '#409EFF' // 高亮选中项 + } else { + option.style.color = 'black' // 默认字体颜色 + } + }) } }