多行文本超出,中间显示省略号的终极方法(适配多语言)

嘿,各位前端圈的兄弟姐妹们!今天我们来聊一个让无数英雄豪杰折腰的"史诗级"难题:文本溢出处理

你肯定见过它,也肯定被它折磨过。产品经理悠悠地走过来,指着屏幕说:"这个标题太长了,末尾显示三个点太丑了,能不能在中间显示省略号?像 前端开发...从入门到放弃 这样,前后都给我留点内容,让用户知道大概是个啥。"

你一听,心里咯噔一下。CSS 的 text-overflow: ellipsis; 就是个"偏科生",它只会末尾截断,让它中间干活,它直接"躺平"。

特别是当你的应用需要支持多语言,比如中文、日文、阿拉伯文......这些语言的字符宽度千差万别,用固定的字符数去截断,简直是"盲人摸象",效果惨不忍睹。

难道我们就束手无策了吗?别急,今天,我就掏出我的压箱底宝贝,一个能精准打击、完美适配多语言的"终极解决方案"!

传统方案的"阿喀琉斯之踵"

在亮出大招之前,我们先来"鞭尸"一下传统方案,看看它们为什么不行。

  1. 纯 CSS 方案text-overflow: ellipsis; 只能单行、末尾截断。想多行?想中间?对不起,请出门左转,找 line-clamp 商量去,但它也只能在末尾截断。
  2. JS 字符截断str.slice(0, 10) + '...' + str.slice(-10)。这种"一刀切"的方式,在等宽字体下或许能看,但在现代浏览器各种花里胡哨的字体面前,简直就是一场灾难。一个 W 的宽度可能顶三个 i,你按字符数切,切出来的宽度能一样吗?

所以,我们需要一个能精确测量文本宽度 的方案。这时候,一位沉睡的巨人被我们唤醒了------Canvas API

终极武器:Canvas 的"火眼金睛"

你没看错,就是那个平时用来画图、做游戏的 Canvas。它有一个隐藏技能:ctx.measureText(text)。这个方法能像一把精确的游标卡尺,量出任意一段文本在指定字体下的真实像素宽度!

有了这把"神兵利器",我们就可以制定一个完美的"截断策略"了。

核心思路:

  1. 先用"游标卡尺"量一下完整文本的宽度,如果比容器窄,那啥也别干,完美显示。
  2. 如果宽了,我们就启动"双指针"算法。想象一下,你的左手和右手分别从文本的开头和结尾,同时向中间"吃"字符。
  3. 每吃一口,就把左右手里的文本,加上中间的省略号 ...,组合起来,再用"游标卡尺"量一下。
  4. 如果发现组合后的宽度第一次超过了容器的宽度,就说明"吃多了"。赶紧把刚才多吃的那个字符吐出来,然后收工!
  5. 最后把"左手里的文本 + 省略号 + 右手里的文本"拼在一起,大功告成!

听着是不是有点像"两只小蜜蜂呀,飞在花丛中"?

别急,我们上代码,一看就懂!

代码实战:庖丁解牛

这是我们的核心方法

javascript 复制代码
/**
 * 获取中间省略的文本
 * @param {string} text - 原始文本
 * @param {number} maxWidth - 容器的最大宽度
 * @param {string} font - 字体样式 (e.g., '16px Arial')
 * @returns {string} 处理后的文本
 */
getMiddleEllipsisText(text, maxWidth, font) {
  const ellipsis = '...';
  
  // 1. 准备我们的"测量工具"------Canvas
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  
  // 2. 设置"游标卡尺"的量程。关键!必须使用真实的字体样式!
  ctx.font = font || '16px Arial';

  // 3. 先量一下全文,如果放得下,直接返回,别折腾了。
  if (ctx.measureText(text).width <= maxWidth) return text;

  // 4. 准备"左右开弓"的指针
  let left = 0, right = text.length - 1;
  let leftStr = '', rightStr = '';

  // 提前量好省略号的宽度,免得循环里重复计算
  const ellipsisWidth = ctx.measureText(ellipsis).width;

  // 5. 开始"向中心靠拢"的循环
  while (left < right) {
    // 左手"吃"一个字符
    leftStr += text[left];
    // 右手"吃"一个字符
    rightStr = text[right] + rightStr;
    
    // 拼接起来,看看有多宽
    const combined = leftStr + ellipsis + rightStr;
    
    // 6. 关键判断:如果超宽了!
    if (ctx.measureText(combined).width > maxWidth) {
      // 说明刚才"吃"多了,把最后那个字符"吐"出来
      leftStr = leftStr.slice(0, -1);
      rightStr = rightStr.slice(1);
      break; // 赶紧跳出循环,再吃就撑爆了!
    }
    
    // 如果没超宽,继续向中心移动
    left++;
    right--;
  }
  
  // 7. 返回最终成果
  return leftStr + ellipsis + rightStr;
},

这段代码的逻辑是不是清晰又有趣?就像两个贪吃蛇,从两头往中间吃,吃到快撑破肚皮的那一刻就停下来。

如何优雅地使用它?

光有核心方法还不够,我们得知道在真实项目中怎么调用。下面是一个典型的 Vue 使用场景:

javascript 复制代码
// 假设这是你的 Vue 组件的一个方法
updateVideoTitle() {
  // 1. 找到要操作的 DOM 元素
  const videoTitleRef = this.$refs.videoTitleRef;
  if (!videoTitleRef) return; // 元素不存在,溜了溜了

  // 2. 清空一下,防止重复调用时内容叠加
  videoTitleRef.innerHTML = '';
  
  // 3. 【多语言适配的关键!】获取元素真实的、计算后的字体样式
  //    这一步确保了我们测量的宽度和实际渲染的宽度完全一致!
  let font = window.getComputedStyle(videoTitleRef).font;
  
  // 4. 获取原始文本和容器的最大宽度
  let text = this.item.name;
  let maxWidth = videoTitleRef.offsetWidth;
  
  // 5. 调用我们的"屠龙术",获取处理后的文本
  videoTitleRef.innerText = this.getMiddleEllipsisText(text, maxWidth, font);
}

划重点window.getComputedStyle(videoTitleRef).font 是整个方案能够完美适配多语言 的灵魂!它会把元素上所有 CSS 关于字体的属性(font-size, font-family, font-weight 等)都给你抓过来,保证我们的 Canvas 测量环境和真实渲染环境分毫不差。无论是中文的"微软雅黑",还是日文的"ヒラギノ角ゴ",都能被精准拿捏!

总结一下,这个"终极方法"好在哪?

  1. 精准控制:基于像素级测量,不是字符数,结果完美。
  2. 中间省略:完美实现产品经理的"刁钻"需求。
  3. 多语言友好 :配合 getComputedStyle,无视任何语言的字符宽度差异。
  4. 兼容性好:Canvas API 的兼容性,比你想象的要好得多。

当然,它也有一个小小的"缺点":它需要 JavaScript 计算。对于海量列表(比如几千条),在 resize 事件中频繁调用可能会有性能开销。但大多数场景下,比如处理标题、卡片名称等,这点开销完全可以忽略不计。

所以,下次再遇到文本溢出的难题,别再对着 CSS 叹气了。请出我们这位 Canvas 大神,用代码的智慧,优雅地解决问题吧!

好了,今天的"屠龙秘籍"就分享到这里。觉得有用的话,别忘了点赞收藏,顺便分享给你身边那个正在为文本溢出抓狂的兄弟!我们下期再见!😎

相关推荐
王六岁5 小时前
# 🐍 前端开发 0 基础学 Python 入门指南:常用的数据类型和列表
前端·javascript·python
1_2_3_5 小时前
神级JS API,谁用谁好用
前端·javascript
冬至已至5 小时前
AI 时代的自动化信息获取与整合
前端
用户6600676685395 小时前
从送花表白实例读懂 JavaScript 对象字面量与代理模式
javascript
我是日安5 小时前
从零到一打造 Vue3 响应式系统 Day 29 - readonly:数据保护实现
前端·javascript·vue.js
时代拖油瓶5 小时前
我劝你必须知道——Intl.Segmenter
前端·javascript
韭菜炒大葱5 小时前
对象字面量与JSON:JavaScript中最强大的数据结构
javascript
海在掘金611275 小时前
从"万能函数"到"精准工具":泛型如何消除重复代码
前端
云心雨禅5 小时前
DNS工作原理:从域名到IP
运维·前端·网络协议·tcp/ip·github