文本超出(溢出)元素后截断文本,在中间显示省略号...

感谢原文章提供了思路,本文在此基础上做了完善

由于本项目有多语言环境,所以要考虑切换语言后文本宽度的变化

思路:1. 在视图初始化后拿到dom元素的宽度

            2. 用canvas计算文本大概的宽度进行比对看是否超出设定宽度

           2. 超出的话再双指针遍历文本,用canvas计算出需要截断的下标后再组合显示

注意点:1.需要注意语言切换时的监听,且关注执行顺序

               2.需要注意翻译后,视图更新后再去获取元素宽度

               3.需要注意canvas计算与真实情况的误差(目前粗略计算了,可以再优化)

               4.需要注意初始化时会执行translateService.onLangChange

import { Component, AfterViewInit, Input, ViewChild,OnDestroy,ChangeDetectorRef } from '@angular/core';
import {TranslateService} from '@ngx-translate/core';
import {PermissionToolkitService, StorageService, TranslatePoolComponent} from "pi-front-common-toolkits";


@Component({
  selector: 'app-text-ellipsis',
  templateUrl: './text-ellipsis.component.html',
  styleUrls: ['./text-ellipsis.component.less']
})
export class TextEllipsisComponent extends TranslatePoolComponent implements AfterViewInit, OnDestroy {

  constructor(
    public translateService: TranslateService,
    private cdr: ChangeDetectorRef  // 注入 ChangeDetectorRef
  ) { 
    super(translateService);
  }

 //在视图初始化之后才能拿到dom元素,onOninit的时候ChildView还没执行完成,拿不到dom元素
  ngAfterViewInit(): void {
    //由于translateService.instant方法会在ngAfterViewInit之后执行,所以要用translateService.get(this.newText)的回调
    //拿到翻译后的标题,然后拿到dom元素,在dom元素大小变化时更新内容
    this.updateTitle();
    // 监听语言切换事件
    this.langChangeSub = this.translateService.onLangChange.subscribe(() => {
      console.log('langChangeSub');
      this.updateTitle();  // 语言切换时,重新获取翻译并更新内容
    });
  }
  updateTitle(): void {
    this.translateService.get(this.title).subscribe(translatedTitle => {
      this.dom = this.titleRef?.nativeElement || null;
      if (this.dom) {
        const observer = new ResizeObserver(() => { // 在大小变化时更新内容
            this.newText = translatedTitle;
            console.log(this.newText)
            this.truncateText(this.newText,this.dom);  
        });
        observer.observe(this.dom);
      }
    });
  }
  ngOnDestroy(): void {
    // 取消订阅语言切换事件,防止内存泄漏
    if (this.langChangeSub) {
      this.langChangeSub.unsubscribe();
    }
  }

  

  @Input() title: string = '' ;
  @ViewChild('titleRef') titleRef: any;
  @Input() maxWidth: string = '100%';
  langChangeSub = null;
   // 标题盒子dom
  dom = null;
  newText = this.title;

// 获取dom元素的padding值
getPadding(el) {
    const domCss = window.getComputedStyle(el, null);
    const pl = Number.parseInt(domCss.paddingLeft, 10) || 0;
    const pr = Number.parseInt(domCss.paddingRight, 10) || 0;
    return {
        left: pl,
        right: pr
    }
}


getTextLength(dom: HTMLElement): { status: boolean, width: number } {
  const fullTextWidth = this.calculateTextWidth(this.newText, dom);

  // 获取容器宽度并减去 padding
  const { left, right } = this.getPadding(dom);
  const paddingWidth = left + right;
  const availableWidth = dom.clientWidth - paddingWidth;

  return {
    status: fullTextWidth > availableWidth,  // 文本是否超出容器
    width: availableWidth                    // 容器的可用宽度
  };
}
truncateText(text: string, dom: HTMLElement): void {
  const { status, width } = this.getTextLength(dom);
  let result = text;

  if (status) {
    // 省略号的宽度
    const ellipsis = '...';
    const ellipsisWidth = this.calculateTextWidth(ellipsis, dom);

    let leftText = '';
    let rightText = '';
    let leftIndex = 0;
    let rightIndex = text.length - 1;
    

    // 逐步截取左右两边的字符,直到符合容器的宽度
    while (leftIndex < rightIndex) {
      const tempLeft = leftText + text[leftIndex];
      const tempRight = text[rightIndex] + rightText;

      const combinedText = `${tempLeft}${ellipsis}${tempRight}`;
      const combinedWidth = this.calculateTextWidth(combinedText, dom) + width / 14; //误差值为宽度的1/14左右

      if (combinedWidth <= width) {
        leftText = tempLeft;
        rightText = tempRight;
        leftIndex++;
        rightIndex--;
      } else {
        break;
      }
    }

    result = `${leftText}${ellipsis}${rightText}`;
  }
  dom.innerText = result;
}

calculateTextWidth(text: string, dom: HTMLElement): number {
  // 获取 DOM 的字体样式
  const computedStyle = window.getComputedStyle(dom);
  const fontSize = computedStyle.fontSize;
  const fontFamily = computedStyle.fontFamily;

  // 创建一个 canvas 进行测量
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');

  // 设置与目标 DOM 相同的字体样式
  context.font = `${fontSize} ${fontFamily}`;

  // 使用 canvas 的 measureText 方法测量文本宽度
  const textWidth = context.measureText(text).width;
  return textWidth;
}
}
<div class="title" #titleRef [ngStyle]="{width: maxWidth}">{{newText | translate}}</div>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值