如何用可变宽度字体模仿等宽字体?

Bas*_*asj 4 html css fonts font-face letter-spacing

(编辑:我只需要这个 10 位数字 0..9,我可以在 CSS 文件中硬编码这 10 位数字中每一位的字母宽度吗?)

我读过CSS - Make sans-serif font imitate monospace font但 CSS 规则letter-spacing似乎还不够:

在此输入图像描述

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

这并不完美:

.text {
  font-family: sans-serif;
  letter-spacing: 10px;
}
Run Code Online (Sandbox Code Playgroud)
<div class="text">
ABCDEFGHIJKLMNOPQR<br>
STUVWXYZ0123456789
</div>
Run Code Online (Sandbox Code Playgroud)

her*_*zel 5

TL;DR:模仿等宽字体总是会带来问题

\n
    \n
  • 从长远来看:选择合适的等宽字体或首先提供表格数字的字体要容易/高效得多
  • \n
  • font-variant-numeric: tabular-nums如果您只需要获取表格数字,请尝试 css 属性
  • \n
\n

就可预测的跨浏览器渲染而言,所有这些黑客实际上都更加昂贵且“不稳定” 。不过,让我们总结一下这些技巧:

\n

技巧 1:将所有字符包裹在行内块元素中

\n

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

\n

\r\n
\r\n
emulateMonospace();\n\nfunction emulateMonospace() {\n  let monoWraps = document.querySelectorAll(".toMonospace");\n\n  monoWraps.forEach(function(monoWrap, i) {\n    //remove all "\\n" linebreaks and replace br tags with "\\n"\n    monoWrap.innerHTML = monoWrap.innerHTML\n      .replaceAll("\\n", "")\n      .replaceAll("<br>", "\\n");\n    let text = monoWrap.textContent;\n    let letters = text.split("");\n\n\n    //get font-size\n    let style = window.getComputedStyle(monoWrap);\n    let fontSize = parseFloat(style.fontSize);\n\n    //find maximum letter width\n    let widths = [];\n    monoWrap.textContent = "";\n    letters.forEach(function(letter) {\n      let span = document.createElement("span");\n      if (letter == "\\n") {\n        span = document.createElement("br");\n      }\n      if (letter == \' \') {\n        span.innerHTML = \'&nbsp;\';\n      } else {\n        span.textContent = letter;\n      }\n      monoWrap.appendChild(span);\n      let width = parseFloat(span.getBoundingClientRect().width);\n      widths.push(width);\n      span.classList.add("spanMono");\n      span.classList.add("spanMono" + i);\n    });\n\n    monoWrap.classList.replace("variableWidth", "monoSpace");\n\n    //get exact max width\n    let maxWidth = Math.max(...widths);\n    let maxEm = maxWidth / fontSize;\n    let newStyle = document.createElement("style");\n    document.head.appendChild(newStyle);\n    newStyle.sheet.insertRule(`.spanMono${i} { width: ${maxEm}em }`, 0);\n  });\n}
Run Code Online (Sandbox Code Playgroud)\r\n
body{\n  font-family: sans-serif;\n  font-size: 10vw;\n  line-height: 1.2em;\n  transition: 0.3s;\n}\n\n.monospaced{\n    font-family: monospace;\n}\n\n.letterspacing{\n  letter-spacing:0.3em;\n}\n\n.teko {\n  font-family: "Teko", sans-serif;\n}\n\n.serif{\n    font-family: "Georgia", serif;\n}\n\n.variableWidth {\n  opacity: 0;\n}\n\n.monoSpace {\n  opacity: 1;\n}\n\n.spanMono {\n  display: inline-block;\n  outline: 1px dotted #ccc;\n  text-align: center;\n  line-height:1em;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<link href="https://fonts.googleapis.com/css2?family=Teko:wght@300&display=swap" rel="stylesheet">\n\n<h3 style="font-size:0.5em;">Proper monospace font</h3>\n<div class="monospaced">\nWiWi</br>\niWiW\n</div>\n\n<h3 style="font-size:0.5em;">Letterspacing can\'t emulate monospaced fonts!</h3>\n<div class="letterspacing">\nWiWi</br>\niWiW\n</div>\n\n\n<hr>\n\n<h3 style="font-size:0.5em;">Text splitted up in spans</h3>\n\n<div class="toMonospace variableWidth">\nABCDEFGHIJKLMNOPQR<br>\nSTUVWXYZ0123456789<br>\n</div>\n\n<div class="toMonospace variableWidth teko">\nABCDEFGHIJKLMNOPQR<br>\nSTUVWXYZ0123456789<br>\n</div>\n\n<div class="toMonospace variableWidth serif">\nThis is<br>\nnot a<br>\nMonospace<br>\nfont!!!\n</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

每个字符都将包含在具有显示属性的范围内inline-block

\n

此外,所有字符均通过 居中text-align:center

\n

上面的脚本还将比较所有字符的宽度,以将最大宽度设置为跨度宽度。

\n

诚然,这不是很方便
,但这种方法可能足以满足设计/布局的目的,并且不会更改实际的字体文件。

\n

如片段所示:
\n在等宽字体中,最宽的字母(如“W”)以一种挤压方式设计(不扭曲)\n而较细的字母(如“i”)在视觉上被设计为拉伸(例如,通过添加底部衬线) )。

\n

因此,无法真正模拟正确的等宽字体。

\n

技巧 2:仅替换数字

\n

相当多的网络字体还包含表格数字,可以通过 CSS 属性
\n应用font-variant-numeric: tabular-nums

\n

仅将附加字体应用于数字

\n

我们可以在规则中指定 unicode-range@font-face以仅将字体应用于数字

\n

\r\n
\r\n
body {\n  font-size: 10vw;\n  font-family: Arial;\n}\n\n@font-face {\n  font-family: Arial;\n  font-style: normal;\n  font-weight: 400;\n  src: local("Arial");\n}\n/** numbers **/\n@font-face {\n  font-family: Arial;\n  font-style: normal;\n  font-weight: 400;\n  src: url(https://fonts.gstatic.com/s/firasans/v17/va9E4kDNxMZdWfMOD5Vvl4jL.woff2) format("woff2");\n  unicode-range: U+30-39;\n}\n.tabNum {\n  font-family: Arial, ArialNum;\n  font-weight: 400;\n  font-variant-numeric: tabular-nums;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<p>\nArial 01111<br>\nArial 1000\n</p>\n\n<p class="tabNum">\nArial 01111<br>\nArial 1000\n</p>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

技巧 3:(非常实验性!)text-transform: full-width

\n

此属性将渲染的字符宽度标准化为统一值。目前仅 Firefox 支持此功能。

\n

\r\n
\r\n
body {\n  font-size: 10vw;\n  font-family: Arial\n}\n\n.tabNum {\n  font-weight: 400;\n  text-transform: full-width;\n}
Run Code Online (Sandbox Code Playgroud)\r\n
<h3>Only supported in Firefox</h3>\n<p class="tabNum">\n  Arial 01111<br> Arial 1000\n</p>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

为什么letter-spacing不起作用

\n

letter-spacing只是在所有字母之间均匀地插入空格(...因此得名)。
\n它不会将字符/字形标准化为具有相同的宽度。\n我们需要一个不存在的
css 属性。\n很可能我们永远不会获得类似的属性,因为文本节点不会为每个字符添加可选择的子节点\xe2\x80\x93,这有点过大了。letter-width

\n