vue3实现模拟地图上,站点名称按需显示的功能

news/2024/9/25 20:28:29

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

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

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

最开始我用的比例换算法,算法复杂度是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);//最后一个站点leftlastLeft = 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基本上是反着的:

    //计算下行站点,控制站点是否显示在模拟地图上 getDownSiteCxfunction 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);//最后一个站点leftlastLeft = 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实现了。

我最开始的有问题代码是上下行站点共用的,最大的问题是会出现跳站点显示的情况,代码如下的:

    //计算站点,控制站点是否显示在模拟地图上function calcSite(station: any, lineWidth: number) {let availableWidth = (station.length - 1) * lineWidth; //总长度//记录显示站点的长度let totalLength = 0;station.forEach((f: any, index) => {let siteLength = getSiteNameWidth(f, true);let bigHalf =siteLength / 2;//获取比较大的半宽let bigHalfPre = 0;//计算上一项的文字半宽if (index >= 1) {let siteLengthPre = getSiteNameWidth(station[index - 1], true);bigHalfPre =siteLengthPre / 2;
            }f.left = toDecimal(lineWidth * index);f.show =index==0?true: f.left >=toDecimal(totalLength);if(index >= 1&&station[index-1].show&&bigHalf+bigHalfPre>lineWidth){f.show=false;}if (f.show) {let times = getDivisor(siteLength, lineWidth);totalLength += times * lineWidth;}
        })}
/*** 两个数相除有余数时结果加1* @param all 被除数 站点宽度* @param num 除数  线宽* @returns */
export const getDivisor=( all:number,item:number)=>{
    if(all<=item) return 1;
    let diff:number=0;
    if(item<=20){
        diff=2.5;
    }
    if(item<=30){
        diff=2;
    }
    else if(item<=40){
        diff=1.5;
    }
    else if(item<=46){
        diff=1.05;
    }
    else if(item<=50){
        diff=1;
    }
    return all%item==0?(all/item):(Math.ceil(all/item)+diff);
}

完!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/46155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

redisson WRONGPASS invalid username-password pair or user is disable

1、技术架构:若依微服务框架<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>2021.1</version></dependency> <dependency><groupId>…

dc-2

开放了80、7744端口访问80端口发现不能正常显示,URL里有dc-2的地址,本地无法解析域名添加域名后可以正常访问第一个页面可以找到flag1找到登录页面用wpscan扫一下,爆出3个用户将用户放到de-2users.list里面使用flag1中提示的工具cewl生成密码字典dc-2pass.dic使用wpscan工具…

2024新版本dbeaver改字体大小

窗口-》首选项-》外观/颜色字体-》dbeaver fonts

#26. 2024.6.21

929. qoj1961 Postman 930. loj3085 「GXOI / GZOI2019」特技飞行 931. loj3086 「GXOI / GZOI2019」逼死强迫症 932. loj3087 「GXOI / GZOI2019」旅行者 933. loj3088 「GXOI / GZOI2019」旧词 934. The 3rd Universal Cup. Stage 0: Trial ContestL 又名:hos_lyric 代…

win 7 下载vscode

vscode最新版本目前只支持win10和11,对于win7及以下系统需要下载旧版本vscode,当然官网也有地址可以下载旧版本。 https://code.visualstudio.com/updates/v1_70作者:Outsrkem原文链接:https://www.cnblogs.com/outsrkem/p/18260740本文版权归作者所有,欢迎转载,但未经作者…

2024-06-21 如何在React中使用ECharts

要安装两个插件echarts和echarts-for-react,前者是一个js图标库,后者是对前者在react的封装,想要在react用echarts,就得装echarts-for-react这类的转换库。yarn add echarts echarts-for-react例子:import React, { Component } from "react"; import ReactECha…

Anthropic 发布 Claude 3.5 Sonnet;欧洲杯首次引入虚拟实时广告丨 RTE 开发者日报 Vol.229

Pixel 9 、Vision Pro 、微信输入法 开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点…

Syslog日志外发

Syslog是一种广泛应用于网络设备、操作系统和应用程序的日志通信协议,通过收集、监控和分析Syslog日志,企业可以有效维护网络安全、故障排除和运营管理。 除了内部监控,有时企业也需要将Syslog日志外发以实现更多的管理和合规需求。在实现Syslog日志外发的过程中,合适的工具…