Vue-Leaflet地图组件开发(三)地图控件与高级样式设计
第三篇:Vue-Leaflet地图控件与高级样式设计
1. 专业级比例尺组件实现
1.1 比例尺控件集成
import { LControl } from "@vue-leaflet/vue-leaflet";// 在模板中添加比例尺控件
<l-control-scaleposition="bottomleft":imperial="false":metric="true":maxWidth="200":updateWhenIdle="true"
/>// 自定义比例尺样式
:deep(.leaflet-control-scale) {background-color: rgba(255, 255, 255, 0.8);padding: 5px 10px;border-radius: 4px;box-shadow: 0 1px 5px rgba(0,0,0,0.2);border: 1px solid #ddd;.leaflet-control-scale-line {border: 2px solid #333;border-top: none;color: #333;font-size: 12px;text-align: center;margin: 2px 0;}
}
1.2 动态比例尺组件
<template><l-control position="bottomleft" class="custom-scale-control"><div class="scale-container"><div class="scale-line" :style="scaleStyle"></div><div class="scale-text">{{ scaleText }}</div></div></l-control>
</template><script setup>
import { ref, onMounted, watch } from 'vue';
import { useMap } from '@vue-leaflet/vue-leaflet';const map = useMap();
const scaleText = ref('0 m');
const scaleStyle = ref({ width: '100px' });const updateScale = () => {const zoom = map.value?.getZoom();if (!zoom) return;// 根据缩放级别计算比例尺const metersPerPixel = 156543.03392 * Math.cos(0) / Math.pow(2, zoom);const scaleWidthMeters = 100 * metersPerPixel;// 自动选择合适单位if (scaleWidthMeters >= 1000) {scaleText.value = `${(scaleWidthMeters / 1000).toFixed(1)} km`;} else {scaleText.value = `${Math.round(scaleWidthMeters)} m`;}scaleStyle.value = { width: '100px' };
};// 监听地图缩放事件
onMounted(() => {map.value?.on('zoomend', updateScale);updateScale();
});
</script><style scoped>
.custom-scale-control {background: rgba(255, 255, 255, 0.8);padding: 6px;border-radius: 4px;box-shadow: 0 1px 5px rgba(0,0,0,0.2);border: 1px solid #ddd;
}.scale-container {display: flex;flex-direction: column;align-items: center;
}.scale-line {height: 4px;background: #333;margin-bottom: 2px;
}.scale-text {font-size: 12px;color: #333;font-weight: bold;
}
</style>
2. 增强版图例系统
2.1 动态图例组件
<template><div class="legend-control" :class="{ collapsed: isCollapsed }"><div class="legend-header" @click="toggleCollapse"><span>图层图例</span><el-icon :class="collapseIcon"></el-icon></div><div class="legend-content" v-show="!isCollapsed"><div v-for="layer in activeLayers" :key="layer.id" class="legend-item"><div class="layer-title">{{ layer.name }}</div><div v-if="layer.type === 'categorical'"><div v-for="item in layer.legend" :key="item.label" class="legend-category"><div class="legend-symbol" :style="getSymbolStyle(item)"></div><div class="legend-label">{{ item.label }}</div></div></div><div v-else-if="layer.type === 'gradient'"><div class="gradient-bar" :style="getGradientStyle(layer)"></div><div class="gradient-labels"><span>{{ layer.minValue }}</span><span>{{ layer.maxValue }}</span></div></div></div></div></div>
</template><script setup>
import { ref, computed } from 'vue';
import { useLayerStore } from '@/stores/layerStore';const layerStore = useLayerStore();
const isCollapsed = ref(false);const activeLayers = computed(() => {return layerStore.layers.filter(layer => layer.visible).map(layer => ({id: layer.id,name: layer.name,type: layer.legendType,legend: layer.legend,minValue: layer.minValue,maxValue: layer.maxValue}));
});const collapseIcon = computed(() => isCollapsed.value ? 'el-icon-arrow-down' : 'el-icon-arrow-up'
);const toggleCollapse = () => {isCollapsed.value = !isCollapsed.value;
};const getSymbolStyle = (item) => {return {backgroundColor: item.color,border: item.border ? `1px solid ${item.borderColor || '#333'}` : 'none',borderRadius: item.shape === 'circle' ? '50%' : '0'};
};const getGradientStyle = (layer) => {return {background: `linear-gradient(to right, ${layer.colors.join(',')})`,height: '20px'};
};
</script><style scoped>
.legend-control {position: absolute;bottom: 20px;right: 20px;background: white;border-radius: 4px;box-shadow: 0 2px 10px rgba(0,0,0,0.2);z-index: 1000;transition: all 0.3s ease;max-width: 250px;
}.legend-control.collapsed {width: 120px;
}.legend-header {padding: 10px 15px;background: #f5f5f5;cursor: pointer;display: flex;justify-content: space-between;align-items: center;border-radius: 4px 4px 0 0;font-weight: bold;
}.legend-content {padding: 10px;max-height: 60vh;overflow-y: auto;
}.legend-item {margin-bottom: 15px;
}.layer-title {font-weight: bold;margin-bottom: 8px;padding-bottom: 4px;border-bottom: 1px solid #eee;
}.legend-category {display: flex;align-items: center;margin: 5px 0;
}.legend-symbol {width: 20px;height: 20px;margin-right: 8px;
}.gradient-bar {width: 100%;margin: 10px 0;
}.gradient-labels {display: flex;justify-content: space-between;font-size: 12px;color: #666;
}
</style>
3. 专业地图控件设计
3.1 增强版缩放控件
<template><l-control position="topleft" class="custom-zoom-control"><div class="zoom-btn" @click="zoomIn"><el-icon :size="18"><Plus /></el-icon></div><div class="zoom-display">{{ currentZoom }}</div><div class="zoom-btn" @click="zoomOut"><el-icon :size="18"><Minus /></el-icon></div><div class="zoom-home" @click="resetView"><el-icon :size="18"><House /></el-icon></div></l-control>
</template><script setup>
import { ref, watch } from 'vue';
import { useMap } from '@vue-leaflet/vue-leaflet';
import { House, Plus, Minus } from '@element-plus/icons-vue';const map = useMap();
const currentZoom = ref(0);watch(() => map.value?.getZoom(), (zoom) => {currentZoom.value = zoom;
});const zoomIn = () => {map.value?.zoomIn();
};const zoomOut = () => {map.value?.zoomOut();
};const resetView = () => {map.value?.flyTo(center.value, defaultZoom.value);
};
</script><style scoped>
.custom-zoom-control {background: white;border-radius: 4px;box-shadow: 0 1px 5px rgba(0,0,0,0.2);padding: 5px;
}.zoom-btn, .zoom-home {width: 30px;height: 30px;display: flex;align-items: center;justify-content: center;cursor: pointer;background: #f8f8f8;margin: 2px 0;border-radius: 3px;transition: all 0.2s;
}.zoom-btn:hover, .zoom-home:hover {background: #e6f7ff;
}.zoom-display {text-align: center;font-size: 12px;font-weight: bold;padding: 5px 0;
}.zoom-home {margin-top: 5px;border-top: 1px solid #eee;
}
</style>
3.2 地图图层切换控件
<template><l-control position="topright" class="layer-switcher"><el-dropdown trigger="click" @command="handleLayerChange"><div class="layer-switcher-btn"><el-icon :size="20"><Map /></el-icon><span class="current-layer">{{ currentLayerName }}</span></div><template #dropdown><el-dropdown-menu><el-dropdown-item v-for="layer in baseLayers" :key="layer.name":command="layer.name":class="{ active: currentLayerName === layer.name }">{{ layer.name }}</el-dropdown-item></el-dropdown-menu></template></el-dropdown></l-control>
</template><script setup>
import { ref, onMounted } from 'vue';
import { useMap } from '@vue-leaflet/vue-leaflet';
import { Map } from '@element-plus/icons-vue';const map = useMap();
const currentLayerName = ref('电子地图');const baseLayers = [{ name: '电子地图', layer: 'vector' },{ name: '卫星影像', layer: 'satellite' },{ name: '地形图', layer: 'terrain' }
];const handleLayerChange = (layerName) => {currentLayerName.value = layerName;// 这里实现实际图层切换逻辑emit('change-base-layer', layerName);
};
</script><style scoped>
.layer-switcher {background: white;border-radius: 4px;box-shadow: 0 1px 5px rgba(0,0,0,0.2);padding: 5px 10px;
}.layer-switcher-btn {display: flex;align-items: center;cursor: pointer;
}.current-layer {margin-left: 5px;font-size: 14px;
}.active {color: var(--el-color-primary);font-weight: bold;
}
</style>
4. 高级地图样式技巧
4.1 响应式地图容器
/* 响应式地图容器 */
.map-container {position: relative;height: 100%;width: 100%;/* 移动端优化 */@media (max-width: 768px) {:deep(.leaflet-control) {transform: scale(0.8);transform-origin: 0 0;}.legend-control {max-width: 200px;bottom: 10px;right: 10px;}}
}/* 修复Leaflet图标路径 */
:deep(.leaflet-default-icon-path) {background-image: url(https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png);
}/* 自定义弹出窗口样式 */
:deep(.leaflet-popup-content-wrapper) {border-radius: 6px;box-shadow: 0 3px 14px rgba(0,0,0,0.2);
}:deep(.leaflet-popup-content) {margin: 12px;min-width: 200px;
}/* 自定义工具提示样式 */
:deep(.leaflet-tooltip) {background: rgba(255, 255, 255, 0.9);border: 1px solid #ddd;border-radius: 3px;box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
4.2 夜间模式支持
// 在组件中添加
const isDarkMode = ref(false);const toggleDarkMode = () => {isDarkMode.value = !isDarkMode.value;if (isDarkMode.value) {// 切换到暗色底图baseLayer.value = darkBaseLayer;// 添加暗色样式类document.querySelector('.map-container').classList.add('dark-mode');} else {// 切换回普通底图baseLayer.value = normalBaseLayer;// 移除暗色样式类document.querySelector('.map-container').classList.remove('dark-mode');}
};
/* 夜间模式样式 */
.dark-mode {:deep(.leaflet-tile) {filter: brightness(0.6) invert(1) contrast(3) hue-rotate(200deg) saturate(0.3) brightness(0.7);}:deep(.leaflet-control) {background-color: #2d3748;color: #e2e8f0;}:deep(.leaflet-bar) {background-color: #2d3748;a {background-color: #2d3748;color: #e2e8f0;border-bottom-color: #4a5568;&:hover {background-color: #4a5568;}}}.legend-control {background-color: #2d3748;color: #e2e8f0;.legend-header {background-color: #1a202c;}.layer-title {border-bottom-color: #4a5568;}}
}
5. 完整控件集成示例
<template><div class="styled-map-container"><!-- 主地图 --><l-map ref="map" :options="mapOptions" @ready="initMap"><!-- 底图 --><l-tile-layer :url="baseLayerUrl" /><!-- 自定义控件 --><custom-zoom-control /><layer-switcher /><advanced-scale-control /><dynamic-legend /><!-- 比例尺 --><l-control-scale position="bottomleft" /><!-- 其他图层 --></l-map><!-- 地图控制面板 --><div class="map-toolbar"><el-button-group><el-button @click="toggleDarkMode"><el-icon><Moon /></el-icon>{{ isDarkMode ? '日间模式' : '夜间模式' }}</el-button><el-button @click="toggleLegend"><el-icon><Picture /></el-icon>图例</el-button></el-button-group></div></div>
</template><script setup>
// 这里集成前面介绍的所有控件和样式
</script><style scoped>
.styled-map-container {position: relative;height: 100%;width: 100%;
}.map-toolbar {position: absolute;top: 80px;right: 20px;z-index: 1000;background: white;padding: 8px;border-radius: 4px;box-shadow: 0 2px 12px rgba(0,0,0,0.1);.dark-mode & {background: #2d3748;color: white;}
}
</style>
相关文章:

Vue-Leaflet地图组件开发(三)地图控件与高级样式设计
第三篇:Vue-Leaflet地图控件与高级样式设计 1. 专业级比例尺组件实现 1.1 比例尺控件集成 import { LControl } from "vue-leaflet/vue-leaflet";// 在模板中添加比例尺控件 <l-control-scaleposition"bottomleft":imperial"false&qu…...

174页PPT家居制造业集团战略规划和运营管控规划方案
甲方集团需要制定一个清晰的集团价值定位,从“指引多元”、“塑造 能力”以及“强化协同”等方面引领甲方做大做强 集团需要通过管控模式、组织架构及职能、授权界面、关键流程、战略 实施和组织演进路径,平衡风险控制和迅速发展,保证战略落地…...

wsl开启即闪退
[ 问题 ]: 在一次电脑卡住,强制关机重启后,遇到打开WSL就闪退的问题在CMD中打开WSL,出现如上图的描述: C:\Users\admin>wsl wsl: 检测到 localhost 代理配置,但未镜像到 WSL。NAT 模式下的 WSL 不支持…...
Spark 写文件
Repartition Spark 输出文件数量 假设每个 Task 的输出数据都包含了全部 8 个分区值,那么最终的文件生成情况如下: 总文件数 = Task 数量 分区组合数 假设: Task 数量:200 分区组合数:8 个 (from_cluster 和 ds 的组合) 则: 总文件数:200 8 = 1600 …...
PostgreSQL 的扩展pg_prewarm
PostgreSQL 的扩展pg_prewarm pg_prewarm 是 PostgreSQL 提供的一个实用扩展,用于将数据预先加载到共享缓冲区或操作系统缓存中,从而提升查询性能。 一、扩展概述 核心功能 手动预热:将指定的表或索引数据加载到内存自动预热:…...
F5 – TCP 连接管理:会话、池级和节点级操作
在 F5 BIG-IP 中,您可以在池成员级别或节点级别管理流向服务器的流量。节点级别状态会影响与该节点关联的所有池,而池成员状态则仅限于单个池。了解每种方法以及何时使用它们对于顺利进行维护窗口和流量管理至关重要。 池级状态:启用、禁用、强制离线、移除 在 BIG-IP 配置…...
金融预测模型开发:数据预处理、机器学习预测与交易策略优化
金融预测模型开发:数据预处理、机器学习预测与交易策略优化 概述 本文将详细介绍一个完整的金融预测模型开发流程,包含数据预处理、机器学习预测和交易策略优化三个核心模块。我们使用Python实现一个端到端的解决方案,适用于股票价格预测和量化交易策略开发。 # 导入必要…...

【P2P】直播网络拓扑及编码模式
以下从 P2P 直播的常见拓扑模式出发,分析各种方案的特点与适用场景,并给出推荐。 一、P2P 直播的核心挑战 实时性要求高 直播场景下,延迟必须控制在可接受范围(通常 <2 秒),同时要保证画面连贯、不卡顿。带宽分布不均 每个节点(观众)上传带宽与下载带宽差异较大,且…...

Python数据可视化科技图表绘制系列教程(二)
目录 表格风格图 使用Seaborn函数绘图 设置图表风格 设置颜色主题 图表分面 绘图过程 使用绘图函数绘图 定义主题 分面1 分面2 【声明】:未经版权人书面许可,任何单位或个人不得以任何形式复制、发行、出租、改编、汇编、传播、展示或利用本博…...

低空城市场景下的多无人机任务规划与动态协调!CoordField:无人机任务分配的智能协调场
作者:Tengchao Zhang 1 ^{1} 1 , Yonglin Tian 2 ^{2} 2 , Fei Lin 1 ^{1} 1, Jun Huang 1 ^{1} 1, Patrik P. Sli 3 ^{3} 3, Rui Qin 2 , 4 ^{2,4} 2,4, and Fei-Yue Wang 5 , 1 ^{5,1} 5,1单位: 1 ^{1} 1澳门科技大学创新工程学院工程科学系࿰…...

算法-构造题
#include<iostream> #include<bits/stdc.h> using namespace std; typedef long long ll; const ll N 5e5 10; int main() {ll n, k;cin >> n >> k; ll a[N] {0}; // 初始化一个大小为N的数组a,用于存储排列// 构造满足条件的排列for (l…...
Go 并发编程深度指南
Go 并发编程深度指南 Go 语言以其内置的并发原语而闻名,通过 goroutine 和 channel 提供了一种高效、安全的并发编程模型。本文将全面解析 Go 的并发机制及其实际应用。 核心概念:Goroutines 和 Channels 1. Goroutines (协程) Go 的轻量级线程实现&…...
PostgreSQL 的扩展pg_freespacemap
PostgreSQL 的扩展pg_freespacemap pg_freespacemap 是 PostgreSQL 提供的一个内置扩展,用于查看表的空闲空间映射(Free Space Map, FSM)信息。这个扩展对于数据库性能调优和空间管理非常有用。 一 扩展概述 功能:提供对表的空…...

【Linux】进程的基本概念
目录 概念描述进程-PCB如何查看进程通过系统目录进行查看通过ps指令进行查看 通过系统调用获取进程的PID和PPID(进程标⽰符)通过系统调用创建子进程通过一段代码来介绍fork为什么要有子进程?fork为什么给子进程返回0,给父进程返回子进程的PIDfork函数到底…...

设备驱动与文件系统:05 文件使用磁盘的实现
从文件使用磁盘的实现逻辑分享 我们现在讲第30讲,内容是文件使用磁盘的具体实现,也就是相关代码是如何编写的。上一节我们探讨了如何从字符流位置算出盘块号,这是文件操作磁盘的核心。而这节课,我们将深入研究实现这一核心功能的…...

AI数据分析在体育中的应用:技术与实践
在现代体育竞技领域,"数据驱动"已不再是一个遥远的概念。尤其随着人工智能(AI)和大数据分析的不断成熟,从职业俱乐部到赛事直播平台,从运动员训练到球迷观赛体验,AI正以前所未有的方式渗透并改变…...

zabbix 6 监控 docker 容器
zabbix 6 监控 docker 容器 1.安装zabbix_agent2 curl -s http://10.26.211.56:8080/centos7-agent2-install.sh | bash2.在zabbix server 端测试 zabbix_get -s 10.26.219.180 -k docker.infoZBX_NOTSUPPORTED: Cannot fetch data: Get "http://1.28/info": dial…...

正则持续学习呀
源匹配为 (.*): (.*)$ 替换匹配为 "$1": "$2", 可将headers改为字典 参考 【爬虫军火库】如何优雅地复制请求头 - 知乎...
【Go语言基础【19】】接口:灵活实现多态的核心机制
文章目录 零、概述一、接口基础1、接口的基本概念a. 接口定义b. 类型实现接口(无需显式声明)c. 接口变量(体现了多态) 2、实现接口的方式3、接口组合4、接口的底层结构 二、空接口与类型断言1. 空接口(interface{}&…...
MySql读写分离部署(一主一从,双主双从,Mycat)
参考资料: 参考视频 参考博客 视频参考资料及安装包: https://pan.baidu.com/s/1xT_WokN_xlRv0h06b6F3yg 提取码: aag3 Mysql主从复制部署指南(一主一从) NotePad++编辑Linux服务器文档 Mysql高版本(8.0及以后)Linux安装 Mysql分库分表(基于Mycat)的基本部署 …...

Go基本语法——go语言中的四种变量定义方法
前言 在go语言中,定义一个变量有四种方式,本文单从语法的层面来介绍这几种方式 单变量定义方法 1.var 变量名 类型,不进行初始化 例如,定义一个变量a后为其赋值,并且打印其值,运行结果如下 //1.不进行…...

27.【新型数据架构】-数据共享架构
27.【新型数据架构】-数据共享架构:降低数据获取成本,实时数据访问,保持数据新鲜度,促进数据经济发展,打破数据孤岛,标准化数据交换,增强数据安全性,完整审计追踪,合规性保障 一、数据共享架构的本质:打破壁垒的“数字立交桥” 传统企业或组织间的数据往往呈现“烟囱…...

virtualbox 如何虚拟机ip固定
1、在网络管理里新建 2、配置网络 3、 进入linux系统,查看 查看 网卡是enp0s8, ifconfig 4、进入网卡配置文件 cd /etc/sysconfig/network-scripts如果没有enp0s8 ,则使用mv ifcfg-enp0s3 ifcfg-enp0s8命令 配置项如下 TYPEEthernet PROXY_METHODn…...

RKNN3588上部署 RTDETRV2
RT-DETR V2 是由百度研究团队在 2024年 提出的,是其广受好评的实时目标检测模型 RT-DETR 的重大升级版本。它继承了第一代 RT-DETR 利用 Transformer 架构实现端到端目标检测 和 卓越实时性能 的核心优势,并针对模型精度、训练效率和部署灵活性进行了全方…...

Python----循环神经网络(BiLSTM:双向长短时记忆网络)
一、LSTM 与 BiLSTM对比 1.1、LSTM LSTM(长短期记忆网络) 是一种改进的循环神经网络(RNN),专门解决传统RNN难以学习长期依赖的问题。它通过遗忘门、输入门和输出门来控制信息的流动,保留重要信息并丢弃无关…...
Elasticsearch 常用操作命令整合 (cURL 版本)
Elasticsearch 常用操作命令整合 (cURL 版本) 集群管理 查看集群健康状态 curl -X GET "localhost:9200/_cluster/health?pretty"查看节点信息 curl -X GET "localhost:9200/_cat/nodes?v"查看集群统计信息 curl -X GET "localhost:9200/_clus…...
Redis持久化策略:RDB与AOF详解
目录 1. RDB持久化工作原理触发机制优点缺点配置示例 2. AOF持久化工作原理同步策略重写机制优点缺点配置示例 3. RDB与AOF比较4. 混合持久化(Redis 4.0)5. 选择建议 Redis提供了两种主要的持久化机制来保证数据安全:RDB(Redis Database)和AOF(Append Only File)。本…...

Linux系统编程-DAY10(TCP操作)
一、网络模型 1、服务器/客户端模型 (1)C/S:client server (2)B/S:browser server (3)P2P:peer to peer 2、C/S与B/S区别 (1)客户端不同&#…...

基于eclipse进行Birt报表开发
Birt报表开发最终实现效果: 简洁版的Birt报表开发实现效果,仅供参考! 可动态获取采购单ID,来打印出报表! 下面开始Birt报表开发教程: 首先:汉化的eclipse及Birt值得拥有:至少感觉上…...

GPU虚拟化
引言 现有如下环境(注意相关配置:只有一个k8s节点,且该节点上只有一张GPU卡): // k8s版本 $ kubectl version Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.7&…...