如何使用变宽字体模仿等宽字体?

3
我已经阅读了CSS-使无衬线字体模仿等宽字体,但CSS规则letter-spacing似乎不够用:

enter image description here

如何从标准无衬线字体中模仿等宽定字体?

这并不能完美地实现:

.text {
  font-family: sans-serif;
  letter-spacing: 10px;
}
<div class="text">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789
</div>

2个回答

2

letter-spacing属性会在所有字母之间均匀地插入空格(因此得名)。但它不会使字符/字形具有相同的宽度。我们需要一种类似于letter-width的CSS属性但实际上并不存在

除了在字体编辑器中更改实际字体度量并编译新字体外,您还可以通过JavaScript将所有字母拆分为<span>元素数组。

emulateMonospace();

function emulateMonospace() {
  let monoWraps = document.querySelectorAll(".toMonospace");

  monoWraps.forEach(function(monoWrap, i) {
    //remove all "\n" linebreaks and replace br tags with "\n"
    monoWrap.innerHTML = monoWrap.innerHTML
      .replaceAll("\n", "")
      .replaceAll("<br>", "\n");
    let text = monoWrap.textContent;
    let letters = text.split("");


    //get font-size
    let style = window.getComputedStyle(monoWrap);
    let fontSize = parseFloat(style.fontSize);

    //find maximum letter width
    let widths = [];
    monoWrap.textContent = "";
    letters.forEach(function(letter) {
      let span = document.createElement("span");
      if (letter == "\n") {
        span = document.createElement("br");
      }
      if (letter == ' ') {
        span.innerHTML = '&nbsp;';
      } else {
        span.textContent = letter;
      }
      monoWrap.appendChild(span);
      let width = parseFloat(span.getBoundingClientRect().width);
      widths.push(width);
      span.classList.add("spanMono");
      span.classList.add("spanMono" + i);
    });

    monoWrap.classList.replace("variableWidth", "monoSpace");

    //get exact max width
    let maxWidth = Math.max(...widths);
    let maxEm = maxWidth / fontSize;
    let newStyle = document.createElement("style");
    document.head.appendChild(newStyle);
    newStyle.sheet.insertRule(`.spanMono${i} { width: ${maxEm}em }`, 0);
  });
}
body{
  font-family: sans-serif;
  font-size: 10vw;
  line-height: 1.2em;
  transition: 0.3s;
}

.monospaced{
    font-family: monospace;
}

.letterspacing{
  letter-spacing:0.3em;
}

.teko {
  font-family: "Teko", sans-serif;
}

.serif{
    font-family: "Georgia", serif;
}

.variableWidth {
  opacity: 0;
}

.monoSpace {
  opacity: 1;
}

.spanMono {
  display: inline-block;
  outline: 1px dotted #ccc;
  text-align: center;
  line-height:1em;
}
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300&display=swap" rel="stylesheet">

<h3 style="font-size:0.5em;">Proper monospace font</h3>
<div class="monospaced">
WiWi</br>
iWiW
</div>

<h3 style="font-size:0.5em;">Letterspacing can't emulate monospaced fonts!</h3>
<div class="letterspacing">
WiWi</br>
iWiW
</div>


<hr>

<h3 style="font-size:0.5em;">Text splitted up in spans</h3>

<div class="toMonospace variableWidth">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>

<div class="toMonospace variableWidth teko">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789<br>
</div>

<div class="toMonospace variableWidth serif">
This is<br>
not a<br>
Monospace<br>
font!!!
</div>

每个字符都将被包装在具有inline-block显示属性的中。 除此之外,所有字符都通过text-align:center居中。
上述脚本还将比较所有字符的宽度,并将最大宽度设置为span的宽度。
不可否认,这不是很方便,但这种方法可能足以满足设计/布局目的,并且不会改变实际的字体文件。
如代码片段所示:
在等宽字体中,最宽的字母(如"W")会被挤压(而不是扭曲),而较细的字母(如"i")则会被视觉上拉伸(例如通过添加底部衬线)。
因此,适当的等宽字体是完全不同的,无法真正模拟。

1

我花了太多时间寻找适用于多种字母表且外观符合我的期望的等宽字体。以下是我找到的解决方案(按推荐顺序排列):

  1. 找到你喜欢的等宽字体并使用它。
  2. 使用字体编辑器将所有字母更改为相同的宽度(可能有一个Python程序可以做到这一点,但我没有尝试过)。但是将字体更改为等宽字体不会看起来很好,因为在创建每个字母时需要进行大量的工艺处理,以使其适合等宽框中。
  3. 使用letter-spacing模拟简单的等宽字体。

谢谢。我正在寻找三个。你怎么做到的?请看我问题中的截图:仅更改字母间距无法正确对齐字母。 - Basj
1
@Basj 这就是 letter-spacing 的最佳效果,这也是为什么它是最不推荐的。 - emilsteen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接