很久很久没有更新博客了,因为实在是太忙了,每天都有公司的事情忙不完.......

最近在做车辆模拟地图,在实现控制站点名称按需显示时,折腾了好一段时间,特此记录一下。最终界面如下图所示:

站点显示需求:首末站必须显示,从第一个站开始,如果站点名称能显示下,则显示,如果站点名称会重叠则隐藏,以此类推。当界面宽度变化时,车辆模拟地图自动变化,保证站点名称能够最大限度的显示。

最开始我用的比例换算法,算法复杂度是O,结果总是不准。尽管一开始我就觉得算法的复杂度应该是O2。我之前一直想着只遍历一次就算出来,

需要注意的地方:由于站点的名称内容是千奇百怪的,可以有空格,各种特殊图标,所以站点文字的长度计算是一个问题,这里是通过canvas来计算的。还有,这里我添加了一个限制,站点文字内容我最大显示120px,超出隐藏并显示省略号,站点名称上添加了title显示全称。

/**
* 获取站点名称
* @param item
* @param showFullName 是否总是显示站点全名
*/
/** */
export const getSiteName = (item: any,showFullName?:boolean=false) => {
const { siteSign } = simulatedMapConf.value;
let name = '';
if (siteSign == 'firstWord') {
name = getSubStrByPreNum(item.stationName);
} else if (siteSign == 'order') {
name = item.stationSeq + '';
} else {
if(showFullName){
name=item.stationName;
}else{
name =item.show? item.stationName:''; //show控制站点名称是否显示
}
}
return name || '';
}
/**
* 获取站点名称宽度
* @param item 站点对象
* @param showFullName 是否总是显示站点全名
* @returns
*/
export const getSiteNameWidth = (item: any,showFullName?:boolean=false) => {
const name =showFullName?item.stationName: getSiteName(item,showFullName);
const width= calculateStringWidth(name);
return width>siteMaxWidth?siteMaxWidth:width;
}
/**
* 根据字符串计算出界面渲染的宽度
* @param str
* @returns
*/
function calculateStringWidth(str:string) {
// 创建一个虚拟的 <canvas> 元素
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 设置字体样式
ctx.font = '12px sans-serif';
// 使用 canvas 的 measureText 方法测量字符串的宽度
const width = ctx.measureText(str).width;
// 返回计算出的宽度
return width;
}

最核心的算法:

    //计算上行站点,控制站点是否显示在模拟地图上
function calcSite(station: any, lineWidth: number) {
if (station.length < 1) return [];
station.forEach((f: any, index) => {
f.show=false;
});
let lastSiteLength = getSiteNameWidth(station[station.length - 1], true) / 2;//站点文字宽度
let lastLeft = getSiteCx(station[station.length - 1], station.length - 1);//最后一个站点left
lastLeft = toDecimal(lastLeft - lastSiteLength);
station.forEach((f: any, index) => {
let siteLength = getSiteNameWidth(f, true);//站点文字宽度
let bigHalf = siteLength / 2;//获取当前的半宽
f.left = getSiteCx(f, index);
if (index == 0 || index == station.length - 1) { //第一项和最后一项必须显示
f.show = true;
} else {
const preShowIndex = getLastTrueIndex(station); //获取前面最近一个显示站点的索引
const preEndLeft = toDecimal(station[preShowIndex].left + getSiteNameWidth(station[preShowIndex], true) / 2);//上一项显示的站点名称结束left位置
f.show = toDecimal(f.left - bigHalf) >=preEndLeft && preEndLeft < lastLeft; //如果上一个显示站点文字的结尾位置 小于等于 当前站点文字的开始位置  并且小于最后一个站点文字的开始位置
if (f.show && toDecimal(f.left + bigHalf) > lastLeft) {
f.show = false;
}
}
})
}

下行站点的计算有些差别,因为left基本上是反着的:

    //计算下行站点,控制站点是否显示在模拟地图上 getDownSiteCx
function calcDownSite(station: any, lineWidth: number) {
if (station.length < 1) return [];
station.forEach((f: any, index) => {
f.show=false;
});
let lastSiteLength = getSiteNameWidth(station[station.length - 1], true) / 2;//站点文字宽度
let lastLeft = getDownSiteCx(station[station.length - 1], station.length - 1);//最后一个站点left
lastLeft = toDecimal(lastLeft + lastSiteLength);
station.forEach((f: any, index) => {
let siteLength = getSiteNameWidth(f, true);//站点文字宽度
let bigHalf = siteLength / 2;//获取当前的半宽
f.left = getDownSiteCx(f, index);
if (index == 0 || index == station.length - 1) { //第一项和最后一项必须显示
f.show = true;
} else {
const preShowIndex = getLastTrueIndex(station); //获取前面最近一个显示站点的索引
const preEndLeft = toDecimal(station[preShowIndex].left - getSiteNameWidth(station[preShowIndex], true) / 2);//上一项显示站的的结束left位置
f.show = toDecimal(f.left + bigHalf) <=preEndLeft && preEndLeft > lastLeft;
if (f.show && toDecimal(f.left - bigHalf) < lastLeft) {
f.show = false;
}
}
})
}

另外获取站点中心点位置的方法

    //获取上行站点水平x位置
const getSiteCx = (item: any, index: number) => {
return startleft.value + dLayout.lineWidth * index;
}
//获取下行站点水平x位置
const getDownSiteCx = (item: any, index: number) => {
return downStartleft.value - layout.endLine - dLayout.downLineWidth * index;
}

说明:站点的布局采用css绝对定位。第一个版本这块我是采用的svg画的,后来发现扩展起来越来越麻烦,周末就在家花了半天时间全部改造为html实现了。

vue3实现模拟地图上,站点名称按需显示的功能的更多相关文章

  1. MeteoInfo-Java解析与绘图教程(七)_图层添加站点名称或区域名称

    MeteoInfo-Java解析与绘图教程(七)_图层添加站点名称或区域名称 在上文说了用自动站的数据经过插值绘制色斑图,这种一般是在geoserver上叠加图片呈现,但遇到后端导出图片,我们又想添加 ...

  2. WordPress获取首页网站链接和站点名称

    利用bloginfo 获取WordPress网站名称和主页链接 用法一: $blog_title = get_bloginfo('name'); //获取站点名称 $linkzmki = get_bl ...

  3. 罗列Linux发行版的基础目录名称,命令法则和功能

    罗列Linux发行版的基础目录名称命名法则及功用规定 目录描述 /主层次 的根,也是整个文件系统层次结构的根目录 /bin存放在单用户模式可用的必要命令二进制文件,所有用户都可用,如 cat.ls.c ...

  4. SNF开发平台WinForm之十三-单独从服务器上获取PDF文件进行显示-SNF快速开发平台3.3-Spring.Net.Framework

    1运行效果: 2开发实现: 如果需要单独显示PDF文件时用下面代码去实现,指定url地址. 地址: . 获取附件管理的实体对象: List<KeyValuePair<string, obj ...

  5. python测试开发django-21.admin后台表名称和字段显示中文

    前言 admin后台页面表名称(默认会多加一个s)和字段名称是直接显示在后台的,如果我们想设置成中文显示需加verbose_name和verbose_name_plural属性 verbose_nam ...

  6. 为axure生成的html站点添加关闭所有节点的功能

    上一篇随笔:将Axure用于需求分析工具中,我分享了我做了一个axure部件,方便用axure中制作各种uml图. 用axure的朋友可能会发现一个问题,如下图,axure生成的html站点中所有的文 ...

  7. NuGetTools:批量上传、删除和显示NuGet包

    快照 前言 NuGet是.NET开发必不可少的包管理工具,在日常更新版本过程中,可能需要批量发布 NuGet 包,也不可避免需要发布一些测试的包,后期想将这些测试或者过期的包删除掉.nuget.org ...

  8. 上传媒体文件--添加显示进度条 layui的upload控件

    上传媒体文件--添加显示进度条   layui的upload控件 详细上传功能请参考博客:上传文件--媒体文件+获取上传文件的属性信息 layui的upload控件 - じ逐梦 - 博客园 (cnbl ...

  9. [django]Django站点admin支持中文显示和输入设置

    正文: Django站点admin支持中文输入设置,操作如下: 1 需要确定的你的数据库的client客户端和服务端的编码设置为utf-8,如果不是,请将其设置成utf-8编码,我采用mysql,详情 ...

  10. iOS:实现MKAnnotation协议,在地图上设置大头针,点击显示具体的位置信息

    如何添加大头针(地标): 通过MapView的addAnnotation方法可以添加一个大头针到地图上 通过MapView的addAnnotations方法可以添加多个大头针到地图上 –(void)a ...

随机推荐

  1. [FE] G2Plot 在 Vue 中使用 CDN 方式避免构建时增大 js 体积

    使用 npm.yarn 方式安装的包,虽方便使用,但是会极大增加 vendor.xx.js 体积,拖慢网站运行速度. 以 G2Plot 为例,实际在 build 构建时,会下载一些额外字体到 vend ...

  2. SemanticFunction 自然语言函数

    本文将和大家介绍 LLM 的魔法,通过自然语言编程的方式开发 SemanticFunction 函数 大家都知道,编程里面的函数可以是一个完成某个功能的逻辑片段,绝大部分的函数都需要使用人类不友好的编 ...

  3. WPF 开源二维绘画小工具 GeometryToolDemo 项目

    这是一个演示 WPF 进行二维绘画的小工具 Demo 项目,基于 MIT 协议在 GitHub 上完全开源 源作者是 YuWeiCong 我只是帮助开源的工具人 软件运行界面效果: 开源地址: htt ...

  4. dotnet Roslyn 通过读取 suo 文件获取解决方案的启动项目

    本文来告诉大家一个黑科技,通过 .suo 文件读取 VisualStudio 的启动项目.在 sln 项目里面,都会生成对应的 suo 文件,这个文件是 OLE 格式的文件,文件的格式没有公开,本文的 ...

  5. PageOffice6最简集成代码(Asp.Net)

    本文描述了PageOffice产品在普通的Asp.Net项目中如何集成调用. 新建Asp.Net项目:PageOffice6-Net-Simple 在您的web项目的"依赖项-包-管理NuG ...

  6. npm创建项目

    创建项目 创建项目目录 首先新建一个文件夹,这里存放着我们的项目. 创建项目文件 这里不使用任何项目模板,相当于使用空模板. 进入这个文件夹,再cmd中运行npm init. 然后按照提示输入pack ...

  7. 文件系统(四):FAT32文件系统实现原理

    FAT32是从FAT12.FAT16发展而来,目前主要应用在移动存储设备中,比如SD卡.TF卡.隐藏的FAT文件系统现在也有被大量使用在UEFI启动分区中. 为使文章简单易读,下面内容特意隐藏了很多实 ...

  8. containerd 源码分析:kubelet 和 containerd 交互

    0. 前言 Kubernetes:kubelet 源码分析之创建 pod 流程 介绍了 kubelet 创建 pod 的流程,其中介绍了 kubelet 调用 runtime cri 接口创建 pod ...

  9. Istio(十二):Istio问题排查

    目录 一.模块概览 二.Envoy基础 2.1 Envoy 基础 三.Envoy示例 四.调试备忘录 4.1 配置 4.2 运行时 4.3 检查 Envoy 日志 4.4 配置 istiod 日志 一 ...

  10. GeminiDB PITR,让游戏回档“进退自如”!

    本文分享自华为云社区<GeminiDB PITR,让游戏回档"进退自如"!>,作者:GaussDB 数据库. 在实际业务场景中,客户数据库难免会出现数据损毁.数据丢失. ...