搜索
首页web前端css教程基于视口的CSS夹具线性缩放字体大小

Linearly Scale font-size with CSS clamp() Based on the Viewport

响应式排版过去尝试过多种方法,例如媒体查询和 CSS calc()。

本文将探讨一种不同的方法,即随着视口宽度增加,在最小和最大尺寸之间线性缩放文本,目的是使其在不同屏幕尺寸下的行为更可预测——所有这些都只需一行 CSS 代码,这要归功于 clamp()

CSS 函数 clamp() 功能强大。它对各种用途都有效,但对于排版尤其有用。其工作原理如下:它接受三个值:

<code>clamp(minimum, preferred, maximum);</code>

它返回的值将是首选值,直到该首选值低于最小值(此时将返回最小值)或高于最大值(此时将返回最大值)。

那么,假设您没有设置奇怪的值并在最小值和最大值之间设置它,它难道总是首选值吗?嗯,您应该使用首选值的公式,例如:

<code>.banner {
  width: clamp(200px, 50%   20px, 800px); /* 是的,您可以在 clamp() 中进行数学运算!*/
}</code>

假设您希望在视口宽度为 360px 或以下时将元素的最小字体大小设置为 1rem,并在视口宽度为 840px 或以上时将最大字体大小设置为 3.5rem。

换句话说:

<code>1rem   = 360px 及以下
缩放 = 361px - 839px
3.5rem = 840px 及以上</code>

任何介于 361 像素和 839 像素之间的视口宽度都需要一个在 1rem 和 3.5rem 之间线性缩放的字体大小。使用 clamp() 实际上非常容易!例如,在视口宽度为 600 像素(介于 360 像素和 840 像素之间)的情况下,我们将获得 1rem 和 3.5rem 之间的中间值,即 2.25rem。

我们尝试使用 clamp() 实现的目标称为线性插值:获取两个数据点之间的中间信息。

以下是执行此操作的四个步骤:

步骤 1

选择最小和最大字体大小,以及最小和最大视口宽度。在我们的示例中,字体大小为 1rem 和 3.5rem,宽度为 360px 和 840px。

步骤 2

将宽度转换为 rem。由于大多数浏览器上的 1rem 默认情况下为 16px(稍后会详细介绍),因此我们将使用它。因此,最小和最大视口宽度将分别为 22.5rem 和 52.5rem。

步骤 3

在这里,我们将稍微偏向数学方面。当组合在一起时,视口宽度和字体大小在 X 和 Y 坐标系上形成两点,而这些点构成一条线。

我们需要这条线——更具体地说,我们需要它的斜率及其与 Y 轴的交点。以下是计算方法:

<code>slope = (maxFontSize - minFontSize) / (maxWidth - minWidth)
yAxisIntersection = -minWidth * slope   minFontSize</code>

这使我们得到斜率值为 0.0833,Y 轴交点值为 -0.875。

步骤 4

现在我们构建 clamp() 函数。首选值的公式为:

<code>preferredValue = yAxisIntersection[rem]   (slope * 100)[vw]</code>

因此,该函数最终如下所示:

<code>.header {
  font-size: clamp(1rem, -0.875rem   8.333vw, 3.5rem);
}</code>

您可以在以下演示中可视化结果:

继续操作并试用它。如您所见,当视口宽度为 840px 时,字体大小停止增长,当视口宽度为 360px 时,字体大小停止减小。介于两者之间的所有内容都以线性方式变化。

如果用户更改根字体大小怎么办?

您可能已经注意到这种方法的一个小缺陷:它只有在根字体大小是您认为的大小(在前面的示例中为 16px)并且从不更改时才有效。

我们通过将其除以 16 来将宽度 360px 和 840px 转换为 rem 单位,因为我们假设这是根字体大小。如果用户将其首选项设置为另一个根字体大小,例如 18px 而不是默认的 16px,则该计算将出错,并且文本不会按我们预期的那样调整大小。

我们这里只有一种方法可以使用,那就是 (1) 在页面加载时在代码中进行必要的计算,(2) 侦听根字体大小的变化,以及 (3) 如果发生任何变化,则重新计算所有内容。

这是一个有用的 JavaScript 函数来进行计算:

// 获取以像素为单位的视口宽度和以 rem 为单位的字体大小
function clampBuilder(minWidthPx, maxWidthPx, minFontSize, maxFontSize) {
  const root = document.querySelector("html");
  const pixelsPerRem = Number(getComputedStyle(root).fontSize.slice(0, -2));

  const minWidth = minWidthPx / pixelsPerRem;
  const maxWidth = maxWidthPx / pixelsPerRem;

  const slope = (maxFontSize - minFontSize) / (maxWidth - minWidth);
  const yAxisIntersection = -minWidth * slope   minFontSize;

  return `clamp(${minFontSize}rem, ${yAxisIntersection}rem   ${slope * 100}vw, ${maxFontSize}rem)`;
}

// clampBuilder(360, 840, 1, 3.5) -> "clamp(1rem, -0.875rem   8.333vw, 3.5rem)"

我故意省略了如何将返回的字符串注入 CSS 中,因为根据您的需求以及您是否使用的是原生 CSS、CSS-in-JS 库或其他内容,有很多方法可以做到这一点。此外,没有针对字体大小更改的原生事件,因此我们必须手动检查。我们可以使用 setInterval 每秒检查一次,但这可能会影响性能。

这更像是一个极端情况。很少有人更改其浏览器的字体大小,甚至更少的人会在访问您的网站时更改它。但是,如果您希望您的网站尽可能具有响应能力,那么这就是最佳方法。

对于那些不介意极端情况的人

更新:自从本文首次发表以来,此处共享的资源已停止工作。如果您正在寻找计算器来帮助确定各种视口下的字体大小,请考虑使用 Fluid Type Generator,它利用现代流体排版技术。

如何避免文本重新调整

对排版尺寸进行如此细致的控制使我们能够做其他很酷的事情——例如阻止文本在不同的视口宽度下重新调整。

这就是文本的正常行为。

但是现在,有了我们的控制,我们可以使文本保持相同的行数,始终在同一个单词处换行,无论我们使用什么视口宽度。

那么我们该如何做到这一点呢?首先,字体大小和视口宽度之间的比率必须保持不变。在此示例中,我们从 320px 处的 1rem 变为 960px 处的 3rem。

<code>320 / 1 = 320
960 / 3 = 320</code>

如果我们使用前面编写的 clampBuilder() 函数,则会变成:

const text = document.querySelector("p");
text.style.fontSize = clampBuilder(320, 960, 1, 3);

它保持相同的宽度与字体比率。我们这样做的原因是,我们需要确保文本在每个宽度下都具有正确的大小,以便能够保持相同的行数。它仍然会在不同的宽度下重新调整,但这对于我们接下来要做的工作是必要的。

现在我们必须从 CSS 字符 (ch) 单位获得一些帮助,因为仅具有正确的字体大小是不够的。一个 ch 单位相当于元素字体中字形“0”的宽度。我们希望使文本主体与视口一样宽,不是通过设置 width: 100%,而是使用 width: Xch,其中 X 是水平填充视口所需的 ch 单位(或 0)的数量。

要查找 X,我们必须将最小视口宽度 320px 除以元素在视口宽度为 320px 时的字体大小处的 ch 大小。在这种情况下为 1rem。

别担心,这里有一个代码片段来计算元素的 ch 大小:

// 返回元素在所需字体大小下的“0”字形的宽度(以像素为单位)
function calculateCh(element, fontSize) {
  const zero = document.createElement("span");
  zero.innerText = "0";
  zero.style.position = "absolute";
  zero.style.fontSize = fontSize;

  element.appendChild(zero);
  const chPixels = zero.getBoundingClientRect().width;
  element.removeChild(zero);

  return chPixels;
}

现在我们可以继续设置文本的宽度:

function calculateCh(element, fontSize) { ... }

const text = document.querySelector("p");
text.style.fontSize = clampBuilder(320, 960, 1, 3);
text.style.width = `${320 / calculateCh(text, "1rem")}ch`;

哇,等等。发生了一些不好的事情。有一个水平滚动条弄乱了事情!

当我们谈论 320px 时,我们谈论的是视口的宽度,包括垂直滚动条。因此,文本的宽度设置为可见区域的宽度加上滚动条的宽度,这使其水平溢出。

那么为什么不使用不包含垂直滚动条宽度的度量呢?我们不能,这是因为 CSS vw 单位。请记住,我们正在使用 clamp() 中的 vw 来控制字体大小。您会看到,vw 包括垂直滚动条的宽度,这使得字体会随着包括滚动条在内的视口宽度一起缩放。如果我们想要避免任何重新调整,则宽度必须与视口宽度成比例,包括滚动条。

那么我们该怎么做呢?当我们这样做时:

text.style.width = `${320 / calculateCh(text, "1rem")}ch`;

……我们可以通过将其乘以小于 1 的数字来缩小结果。0.9 可以解决问题。这意味着文本的宽度将是视口宽度的 90%,这将足以弥补滚动条占用的小空间。我们可以通过使用更小的数字(例如 0.6)来使其更窄。

function calculateCh(element, fontSize) { ... }

const text = document.querySelector("p");
text.style.fontSize = clampBuilder(20, 960, 1, 3);
text.style.width = `${320 / calculateCh(text, "1rem") * 0.9}ch`;

您可能会倾向于简单地从 320 中减去几个像素以忽略滚动条,如下所示:

text.style.width = `${(320 - 30) / calculateCh(text, "1rem")}ch`;

这样做的问题是它会使重新调整问题恢复!这是因为从 320 中减去会破坏视口与字体的比率。

文本的宽度必须始终是视口宽度的百分比。另一件需要注意的事情是,我们需要确保我们在使用该网站的每台设备上都加载相同的字体。这听起来很明显,不是吗?嗯,这里有一个小细节可能会使您的文本偏离。执行诸如 font-family: sans-serif 之类的事情并不能保证在每个浏览器中都使用相同的字体。sans-serif 将在 Windows 版 Chrome 上设置 Arial,但在 Android 版 Chrome 上设置 Roboto。此外,某些字体的几何形状可能会导致重新调整,即使您做对了所有事情也是如此。等宽字体往往会产生最佳结果。因此,请务必确保您的字体准确无误。

在以下演示中查看此非重新调整示例:

容器内的非重新调整文本

我们现在要做的就是将字体大小和宽度应用于容器,而不是直接应用于文本元素。其中的文本只需要设置为 width: 100%。对于段落和标题,这并不是必需的,因为它们本身就是块级元素,并且会自动填充容器的宽度。

在父容器中应用此方法的一个优点是,其子元素将自动响应和调整大小,而无需逐个设置其字体大小和宽度。此外,如果我们需要更改单个元素的字体大小而不影响其他元素,我们只需将其字体大小更改为任何 em 值,它就会自然地相对于容器的字体大小。

非重新调整文本很挑剔,但它是一种微妙的效果,可以为设计带来不错的效果!

总结

为了总结,我整理了一个关于所有这些如何在现实场景中看起来的小演示。

在此最终示例中,您还可以更改根字体大小,并且 clamp() 函数将自动重新计算,以便文本在任何情况下都能具有正确的大小。

尽管本文的目标是将 clamp() 与字体大小一起使用,但此相同的技术可用于接收长度单位的任何 CSS 属性。现在,我并不是说您应该在任何地方都使用它。很多时候,一个好的 font-size: 1rem 就足够了。我只是想向您展示需要时您可以拥有多少控制权。

就我个人而言,我相信 clamp() 是 CSS 中最好的东西之一,我迫不及待地想看看随着它的越来越广泛使用,人们会想出哪些其他用法!

以上是基于视口的CSS夹具线性缩放字体大小的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
什么是CSS网格?什么是CSS网格?Apr 30, 2025 pm 03:21 PM

CSS网格是创建复杂,响应式Web布局的强大工具。它简化了设计,提高可访问性并提供了比旧方法更多的控制权。

什么是CSS Flexbox?什么是CSS Flexbox?Apr 30, 2025 pm 03:20 PM

文章讨论了CSS FlexBox,这是一种布局方法,用于有效地对齐和分布响应设计中的空间。它说明了FlexBox用法,将其与CSS网格进行了比较,并详细浏览了浏览器支持。

我们如何使用CSS使网站迅速响应?我们如何使用CSS使网站迅速响应?Apr 30, 2025 pm 03:19 PM

本文讨论了使用CSS创建响应网站的技术,包括视口元标签,灵活的网格,流体媒体,媒体查询和相对单元。它还涵盖了使用CSS网格和Flexbox一起使用,并推荐CSS框架

CSS盒装属性有什么作用?CSS盒装属性有什么作用?Apr 30, 2025 pm 03:18 PM

本文讨论了CSS盒装属性,该属性控制了元素维度的计算方式。它解释了诸如Content-Box,Border-Box和Padding-Box之类的值,以及它们对布局设计和形式对齐的影响。

我们如何使用CSS动画?我们如何使用CSS动画?Apr 30, 2025 pm 03:17 PM

文章讨论使用CSS,关键属性并与JavaScript结合创建动画。主要问题是浏览器兼容性。

我们可以使用CSS向我们的项目添加3D转换吗?我们可以使用CSS向我们的项目添加3D转换吗?Apr 30, 2025 pm 03:16 PM

文章讨论了Web项目的3D转换,关键属性,浏览器兼容性和性能注意事项的讨论。(角色计数:159)

我们如何在CSS中添加梯度?我们如何在CSS中添加梯度?Apr 30, 2025 pm 03:15 PM

文章讨论了使用CSS梯度(线性,径向,重复)来增强网站视觉效果,添加深度,焦点和现代美学。

CSS中的伪元素是什么?CSS中的伪元素是什么?Apr 30, 2025 pm 03:14 PM

文章讨论了CSS中的伪元素,它们在增强HTML样式方面的使用以及与伪级的差异。提供实用的例子。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

ZendStudio 13.5.1 Mac

ZendStudio 13.5.1 Mac

功能强大的PHP集成开发环境

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。