首页 > 其他分享 >vue3实现模拟地图上,站点名称按需显示的功能

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

时间:2024-06-21 16:31:51浏览次数:9  
标签:index const 站点 item station vue3 模拟 left

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

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

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

最开始我用的比例换算法,算法复杂度是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实现了。

标签:index,const,站点,item,station,vue3,模拟,left
From: https://www.cnblogs.com/jiekzou/p/18260813

相关文章

  • C++list类的常见函数以及其模拟实现
    文章目录前言一、list内部成员有什么?二、构造函数以及析构函数1.默认构造2.传参构造3.迭代器构造4.深拷贝以及运算符=的重载1.深拷贝2.=的重载5.析构函数三、迭代器的模拟实现1.正向迭代器2.反向迭代器四、常见函数及其实现1.insert函数2.erase函数3.clear函数4.push_......
  • Vite+Electronss构建vue3桌面应用
    本文介绍使用vite构建Electron项目,使用@vitejs/plugin-vue插件辅助完成vue3桌面应用,主要使用Vite,Vue,Electron,@vitejs/plugin-vue四个模块。一创建项目1、输入命令npminitvite首先输入项目名称viteElectron,选择Vue框架和javascript语言2、运行项目输入命令:cdvite......
  • vue3中如何使用pinia -- pinia使用教程(一)
    vue3中如何使用pinia--pinia使用教程(一)安装使用创建store使用store访问修改store使用组合式api创建store--setupstorepinia和hook的完美结合如何解决上面的问题使用hook管理全局状态和pinia有何优缺点?参考小结pinia是一个Vue3的状态管理库,它......
  • vue3 详细配置 pinia,以及持久化处理
    安装piniapnpminstallpiniapnpminstallpinia-plugin-persistedstate使用pinia根目录下创建store文件夹,新家moudules文件夹和index.ts文件,如图:index.ts文件//store/index.ts//仓库大仓库import{createPinia}from"pinia";importpiniaPluginPersisteds......
  • 仿真模拟--telnet服务两种认证模式(自作)
    自己做的笔记,有问题或看不懂请见解一下~ 目录两个路由器间实现telnet服务(password认证模式)serverclient两个路由器间实现telnet服务(aaa认证模式)serverclient改名tab键补齐不会就扣问号                               ......
  • 解决Vue3项目运行控制台警告
    运行Vue3项目,控制台警告:Featureflag VUE_PROD_HYDRATION_MISMATCH_DETAILS isnotexplicitlydefined.Youarerunningtheesm-bundlerbuildofVue,whichexpectsthesecompile-timefeatureflagstobegloballyinjectedviathebundlerconfiginordertogetbet......
  • cesium中如何高性能渲染3D模型(附水淹分析模拟)
    大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第18/100篇文章;前言之前在参加城市应急数字孪生项目开发过程中,遇到一个场景,就是模拟水淹分析。也就是说,甲方需要根据你这个平台,在下暴雨的时候,精准监测到城......
  • 「C++」复杂模拟【壹】
    建议开启目录食用阅读本文之前建议您先看这里,如果您已经看完了,那么就可以放心大胆的学习本文了。我认为其实本文的难度还是比较大的,今天我们题是来自山东省省选,所以建议大家谨慎阅读,如果您是专业程序员当我没说。OK,那么事不宜迟,咱们来看第一题[SDOI2010]猪国杀题目描述游戏......
  • Vue30_指令3
    一、什么是指令?指令(Directives)是带有 v- 前缀的特殊属性。例如在入门案例中的v-model,代表双向绑定。 二、插值表达式1、花括号格式:{{表达式}}说明:该表达式支持JS语法,可以调用js内置函数(必须有返回值)表达式必须有返回结果。例如1+1,没有结果的表......
  • 利用遗传算法(GA)与模拟退火算法(SA)求目标函数最小值
    要求实现一个演化计算的算法,求测试函数的最小值。要求:群体规模NP=100;最大迭代次数不超过3000代。或者,总的计算次数小于100*3000。算法需独立运行30次,并记录进化的过程。一、遗传算法原理        遗传算法(GeneticAlgorithm,GA)是模拟达尔文生物进化论的自然选择和......