认识视口单位vm、vh在网页中的排版应用

在之前的两篇关于构建模块化的文章中,谈到了 rem & em响应式排版,而在文章的评论中,对基于视口单位的网页排版抱有很大的想象空间。

由于视口单位涉及到计算,有一段时间我是抵制在工作使用视口单位。但就在上周,我克服了心中的抵制情绪,开始去了解视口单位在网页排版中的使用。在深入介绍视口单位以及其在网页排版中的工作原理时,先了解下有哪些常见的视口单位。

视口单位是什么?

在 CSS 规范中,有4种类型的可用视口单位:

  • vw — 1vw 等于视口宽度的 1%
  • vh — 1vh 等于视口高度的 1%
  • vmin — vw 和 vh 中的较小值
  • vmax — vw 和 vh 中的较大值

视口,即浏览器屏幕大小,1vw 等于浏览器宽度的 1%,100vw 即整个浏览器的宽度。

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f76696577706f72742d756e6974732e706e6

视口的单位大小会根据视口大小的改变自动计算,视口大小的改变常发生于页面加载、页面缩放或者屏幕方向的改变(横纵切换)。正因为如此,创建一个大小总为视口四分之一大小的容器是非常容易滴:

.component {
  width: 50vw;
  height: 50vh;
  background: rgba(255, 0, 0, 0.25)
}

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f717561727465722d76696577706f72742d636f6d706f6e656e742e67696

将视口单位用于网页排版

将视口单位用于网页排版的唯一理由就是视口的单位大小会根据客户端浏览器的视口大小自动计算。也就是说,我们不必明确地通过媒体查询来声明字体大小。举个demo来简要说明一下。

代码如下,将断点设置为 800px,字体大小从 16px 变为 20px:

// Note: CSS are all written in SCSS

html {
  font-size: 16px;

  @media (min-width: 800px) {
    font-size: 20px;
  }
}

对于上述代码,当视口大小是 800px 时,字体会从 16px “突变” 到 20px。在响应式排版中,这是经常采用的方式。有时,你会碰到在两个断点之间添加额外的媒体查询来确保页面排版适应所有设备:

html {
  font-size: 16px;

  @media (min-width: 600px) {
    font-size: 18px;
  }

  @media (min-width: 800px) {
    font-size: 20px;
  }
}

尽管这样做能达到效果,但需要更多特定的媒体查询规则和字体大小。通常,会选择 3~4 中字体大小。

但是,如何不同媒体查询或字体大小的设置来达到同样的效果呢?

当然是有滴,这就是视口单位的用处了。你可以用视口单位来表示字体大小:

html { font-size: 3vw; }

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f666f6e742d73697a652d696e2d76772e67696

是不是很棒?一段简短的代码就实现了适配。但也有明显的缺点,就是视口的单位大小是根据设备屏幕的视口大小计算的,对于小屏幕设备(如宽度 320px 的手机),字体太小,难以阅读;对于大屏幕设备(如宽度 1440px 的笔记本),字体会变的非常大,同样也会难以阅读。

所以,现在面临的一个有意思的挑战是—怎么解决不同设备的视口宽度对视口单位计算的影响?一种简单地方式是设置 font-size 的最小值,然后通过 calc() 属性来动态计算小屏幕设备上的字体大小值:

html { font-size: calc(18px + 0.25vw) }

参考 Mike Riethmuller 的 Precise control over responsive typography

但是,不是所有的浏览器都支持 calc() 的这种计算方式(px+vw)。解决方式也很简单,结合使用百分比和vw 用于 calc 计算能获得更好地浏览器支持:

html { font-size: calc(112.5% + 0.5vw) }

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f666f6e742d73697a652d76772d74616d65642e67696

下一个需要克服的挑战就是用视口单位来设置排版元素(h1-h6)的字体大小。

用视口单位设置其它排版元素的字体大小

首先,我创建一个 <h1> 元素,将其字体大小设置为body 的两倍:

html { font-size: calc(112.5% + 0.25vw) }
h1 { font-size: calc((112.5% + 0.25vw) * 2); }

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f76696577706f72742d73697a65642d6865616465722e706e6

我试图将 html 元素的字体大小乘以2,但并不可行,对于 <h1>,字体大小是基于百分比计算的。在字体大小继承了 <html> 的大小之后,又重新计算了 <h1> 的字体大小。

现在假设视口宽度是 800px,默认的 font-size 是 16px:

  • 对于 html 元素,112.5% 意味着 font-size 是 18px(112.5/100 * 16px)
  • 0.25vw == 2px(800px * 0.25/100)
  • 所以,htmlfont-size 的最终值是 20px(18px + 2px)

按照同样的方法来计算 h1 元素的 font-size,但需要特别注意的是此时百分比(112.5%)的相对计算量值:

  • 对于 h1 元素,112.5% 意味着 font-size 是 22.5px(112.5/100 * 20px)
  • 0.25vw == 2px(800px * 0.25/100)
  • 所以,h1font-size 的最终值是 49px((22.5px + 2px) * 2)

这与最初想把 h1 元素的 font-size 设置成 Body 的两倍大小的想法相违背。但我们知道了造成差异的原因是由于 h1 继承了 html 的 font-size,有两种方式来解决这个问题。

第一种方式就简单滴将 112.5% 改为 100%:

h1 { font-size: calc((100% + 0.25vw) * 2) }

第二种方式是确保 font-size 不被跨元素继承:

h1 { font-size: calc((100% + 0.25vw) * 2) }
p { font-size: calc((100% + 0.25vw)) }

这两种方式看起来有点 hack,看起来不爽,于是又继续尝试其它方法。最终,最干净的方式是使用 Rem & Em:

html { font-size: calc(112.5% + 0.25vw) }
h1 { font-size: 2em; }

687474703a2f2f7a656c6c776b2e636f6d2f696d616765732f323031362f76696577706f72742d62617365642d7479706f6772617068792f636f72726563746c792d73697a65642d6865616465722e706e6

既然讲到了字体大小的计算,那接下来的问题是:”视口单位的垂直和标准化计算是怎么样的?”

视口单位的垂直和标准化计算

这个相对比较容易回答。不知是否注意到,视口单位常仅被用于 html 元素?其它元素仍用 rem 和 em 作为计算的单位。

这就意味着,你仍然能使用 rem 和 em 用于视口单位的垂直和标准化计算,这和我之前在 Everything I Know about Responsive Typography 一文中讨论的一样。

结束这篇文章之前,最后一个需要谈到的问题是:要怎么样去计算 vw 的值,才能在视口宽度是 800px 时,排版的字体大小为 20px?很多人问到了这个问题,因而,将这个问题简化成一个词就是—精确。换句话说,如何才能字体大小更加精确?

精确

结果是,Mike 已经替我解决了这个问题,我只需要再简单解释下计算方式。

假设你要处理下面两种情况:

  • 视口宽度是 600px 时,font-size 是 18px
  • 视口宽度是 1000px 时,font-size 是 20px
  1. 首先,我们必须将较小的 font-size 值转为百分比。
  2. 第一部计算是:calc(18/16 * 100%) (或者 calc(112.5%))。
  3. 接下来,计算出 vw 值。**这部分的计算略繁琐:
  4. 计算 font-size 的最大差值 v1(22-18=4)
  5. 用 v1 除以视口宽度的最大差值 v2(1000-600)
  6. 将上述结果再乘以 100vw – smaller-viewport-width(100vw – 600)

最终,结果如下:

html {
  font-size: calc(112.5% + 4 / 400 * (100vw - 600px) )
}

开始接触可能会比较复杂,但是熟悉之后,你可以把它简化成 Sass 混入(simple sass mixin)。

原文参照

Viewport Unit Based Typography
rem和em的使用

原文:基于视口单位的网页排版


关注我

我的微信公众号:前端开发博客,在后台回复以下关键字可以获取资源。

  • 回复「小抄」,领取Vue、JavaScript 和 WebComponent 小抄 PDF
  • 回复「Vue脑图」获取 Vue 相关脑图
  • 回复「思维图」获取 JavaScript 相关思维图
  • 回复「简历」获取简历制作建议
  • 回复「简历模板」获取精选的简历模板
  • 回复「加群」进入500人前端精英群
  • 回复「电子书」下载我整理的大量前端资源,含面试、Vue实战项目、CSS和JavaScript电子书等。
  • 回复「知识点」下载高清JavaScript知识点图谱

每日分享有用的前端开发知识,加我微信:caibaojian89 交流