前端的这些用法你都掌握吗?

139 阅读6分钟

前端日常开发中经常会遇到一些不常见的小需求,在此处记录下对应的解决方案~

1、当单词折行时使用连字符“-”

一些单词长度过长需要折行的情况下,一般情况下我们都会添加一行 CSS 代码:word-break: break-all。它的作用是一段文字触发换行时,将处于句尾的单词强制截断,防止文字溢出

no-hyphens.png

但是有些地区的阅读习惯即使单词被截断但是也必须需要连字符“-”进行连接

CSShyphens属性告知浏览器在换行时如何使用连字符连接单词。可以完全阻止使用连字符,也可以控制浏览器什么时候使用,或者让浏览器决定什么时候使用

连字规则具有语言特定性。在HTML中,语言由lang属性决定,浏览器只会在当前属性存在且有合适的连字字典可用的情况使用连字符进行连接

<style>
    .text {
      border: 1px solid black;
      padding: 5px;
      width: 100px;
      hyphens: auto;
    }
</style>

has-hyphens.png

2、给文字加边框

1. 使用text-shadow

将 text-shadow 的 blur 值设为 0,分别向右下、左上、右上、左下方向偏移 1px。但是当我把偏移量加大时,会发现字体边缘处有锯齿状,效果差强人意

text-shadow: 1px 1px 0px black, -1px -1px 0px black, 1px -1px 0px black,
        -1px 1px 0px black;

textshadow.png

2. 使用-webkit-text-stroke

它是-webkit-text-stroke-width-webkit-text-stroke-text两个属性的缩写形式。字面意思就是为文字加上边框。边框1px或2px效果也很不错,但是当边框太大时则会挤压中间的字体宽度

text-stroke.png

3. 使用两次-webkit-text-stroke

意思就是用一个元素用两遍,绝对定位覆盖在上面

<div className="puzzle-top-canvas-title">
    <p>HELLO WORLD!</p>
    <p>HELLO WORLD!</p>
</div>
.title {
    margin: 0 auto;
    min-height: 150px;
    display: flex;
    text-align: center;
    font-weight: 900;
    font-size: 40px;
    color: #fff;
    position: relative;

    p {
      z-index: 2;
      position: absolute;

      &:last-child {
        z-index: 1;
        -webkit-text-stroke: 2px black;  // 细边框
        -webkit-text-stroke: 8px black;  // 粗边框
      }
    }
  }

double-text-stroke.png

从实验结果可以看出来当边框比较细的时候,三种方案几乎都可以符合要求,但是随着边框越来越粗时,第三种方案是比较合理的

3、如何测量文字宽度

1. 使用offsetWidth

简单粗暴,但是有用。但是这种方式引起了重绘、重排。如果有大量的文字需要测量,性能堪忧

function measureText(text, fontSize) {
    const dom = document.createElement('span');
    const body = document.querySelector("body");
    dom.style.fontSize = fontSize + 'px';
    const textDom = document.createTextNode(text);
    dom.appendChild(textDom);
    body.appendChild(dom);
    const size = dom.offsetWidth;
    body.removeChild(dom);
    return size;
}

2. 使用 Canvas 的 measureText API

Canvas 里面有一个 measureText  Api,利用这个可以测量文字宽度。而且还可以测量位置,排版信息等

function measureTextByCanvas(text, fontSize) {
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) {
      return 0;
    }
    ctx.font = fontSize + 'px PingFang SC'; // 字体需与实际字体保持一致
    return ctx.measureText(text).width;
}

4、Grid 布局应用场景

1. 如何快速实现下图中的顶部布局

日常开发中我们都习惯于使用flex一维布局,但是这种情况下,title元素是位于整个宽度的中间,而不是左侧元素的中间,所以title元素需要多一个元素配合并且使用定位才可以实现当前效果

header.png

Grid 布局则是将容器划分成"行"和"列",产生单元格,然后指定"项目所在"的单元格,可以看作是二维布局

<div class="grid-container">
    <div class="item1">left</div>
    <div class="item2">icon</div>
    <div class="item3">title</div>
</div>

<style>
    .grid-container {
      display: grid;
      grid-template-columns: 1fr 150px;
      // 将宽度划分为右侧固定150px, 左侧自适应
      grid-gap: 5px;
      background-color: #2196F3;
      padding: 10px;
    }

    .grid-container>div {
      background-color: rgba(255, 255, 255, 0.8);
      text-align: center;
      padding: 20px 0;
      font-size: 30px;
    }

    .item1 {
      grid-row-start: 1;       // 起始的行
      grid-row-end: 2;         // 结束的行
      grid-column-start: 1;    // 起始的列
      grid-column-end: 2;      // 结束的列
      border: 5px solid red;
    }

    .item2 {
      grid-row-start: 1;
      grid-row-end: 2;
      grid-column-start: 2;
      grid-column-end: 3;
      border: 5px solid blue;
    }

    .item3 {
      grid-row-start: 1;
      grid-row-end: 2;
      grid-column-start: 1;
      grid-column-end: 3;
      justify-self: center;
      align-self: center;
      width: 100%;
      border: 5px solid black;
    }
</style>

Grid 布局将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局。通过行列位置的固定便可以简单实现一个不好实现的布局,Grid 布局远比 Flex 布局强大

image.png

2. 如何实现百分比堆叠柱状图

percent.png

B端项目经常会遇到数据相关的可视化处理,从事数据可视化、统计报表的同学必定对此图感到非常熟悉,它叫做 百分比堆叠柱状图。一般情况下我们都是接入开源库去解决问题,但是当需求比较小时,起始通过 Grid 布局也是可以简单处理的~

类型数量颜色
A100gold
B200lightblue
C300gray

分别计算得出 A 占比为 16.666666666666664,B 占比为 33.33333333333333,C 占比为 50,将它们加上 fr 的单位赋值给 grid-template-columns 即可实现百分比堆叠柱状图。在 CSS Grid Layout 中,存在新单位 fr ,它是一种占比单位,1fr 代表占据可使用空间的一部分,与 flex: 1 相似

<div class="progress">
    <span></span>
    <span></span>
    <span></span>
</div>

.progress {
    margin-top: 100px;
    display: grid;
    grid-template-columns: 16.666666666666664fr 33.33333333333333fr 50fr;
    height: 50px;
}

.progress span:nth-child(1) {
    background: gold;
}

.progress span:nth-child(2) {
    background: lightblue;
}

.progress span:nth-child(3) {
    background: gray;
}

image.png

可以看出来通过 Grid 布局实现起来清晰简单,我们只需要通过 JS 动态计算并设置grid-template-columns的值即可

5、不同时区下的时间戳处理方式

国际化业务与国内业务有两点重要的不同点,一是布局语言不同,二是时区不同。第一点 Intl 为我们解决了很多问题,比如文案翻译、布局习惯等等。第二点在此推荐 Temporal ,目前还在提案中~

过去我们常用 Date 对象处理时间相关的问题,但是 Date 有两个致命的缺点:

  • 初始化的时间是当前用户所在时区的时间,不能初始化其他时区的时间
  • 不支持时间计算相关的 API

因此我们经常项目中引入 moment.js、day.js 实现复杂的时区转换和时间计算逻辑

我们先看网上的一段代码,这段代码我看了很久才理解它的深意,简单来说它的意思就是在 B 地区模拟 A 地区的时间。举个例子B地区中国是 UTC+8 时区,A地区日本是 UTC+9 时区,如果日本的本地时间是2022-3-3 09:00:00,时间戳为1646265600000,那么在中国通过以下函数格式化下则会得到该时间下中国的时间也为2022-3-3 09:00:00,但是时间戳已经变为1646269200000

/*
    timestamp: 要转换的时间戳
    utc: 要转换的时区的偏移量,例如日本为9*60*60
*/
function formatTimestampToLocal(timestamp, utc) {
  const timeOffset = new Date().getTimezoneOffset() * 60;
  const result = Math.floor(timestamp / 1000 + timeOffset + (utc || 0)) * 1000;
  return result;
}

说实话这段代码很难理解,因为当前 Temporal API 还未支持。所以为了可读性,一般引入 moment.js 或者 day.js 都可以有较为简单的实现。day.tz第一个参数为时间戳,第二个为时区标志,这段代码完全替代上面那段代码,可读性更强。而当后续 Temporal 正式发布后,也就无需引入第三方库了~

dayjs.tz(1646265600000, 'Asia/Tokyo').valueOf()

关注公众号《小前日记 》获取源码

IMG_5155.JPG