如何精确控制响应式排版

发布于 大漠

2014年@Jason Pamental写了一篇博客详细介绍了Web排版的缩放。@Richard Rutter另一篇文章中介绍如何更好的表达你的排版。比如在一个Banner区,如何让文本显示的各为大气,又适合你的Web排版,如下图所示:

提出的思考

自从2010年响应式设计概念提出一直以来,在我的印象中:**图片的响应式文本排版的响应式**都是制约响应式应用的两大阻碍。虽然有很多热衷于响应式设计的设计师和开发人员都在致力于解决这方面的问题。但至目前为止,还未看到一套有关于图片响应式的文本排版响应式的最佳解决方案。

当然,你可能会说我说的比较片面,为什么呢?你肯定会说,对于图片的响应式不是有了<picture>标签和srcset属性了?这不是很好的解决方案吗?对于文本排版,可能会想到在不同的断点之下改变root元素htmlfont-size来做相应的处理。那么问题来了?这些真的是很好的解决方案?值得我们去思考一下。

那么今天我们来思考一下响应式的文本排版,也就是标题所说的:如何精确控制响应式排版

响应式排版的使用场景

简单的回忆一下响应式排版的使用场景,其实他的使用场景对于现代Web排版还是很常见的。比如@Richard Rutter在他的博文中所说的,怎么让Banner区域标题在不同的终端屏幕下显示得更为大器(文章开头的示意图)。特别是在移动端时代,都希望在面对不同的移动终端的时候,能让自己的排版更为合适,如下图所示:

为了能更好的做出响应式排版,@MikeRiethmuller提出了精准响应式排版的概念。对于这个概念我也是第一次听说。至于什么是精准响应式排版,我们先把这个概念放一放,先来看一段代码的截图:

上图中蓝色框框标出来的两段代码:

@media screen and (min-width: 20rem) {
    h3,.h3 {
        font-size: calc (1.266rem + .511 * (100vw - 20rem) / 100);
    }
}

这里面有几个很熟悉的东东,@mediaremvwcalc()。那么结合在一起,就有点让人摸不着头脑了。这些计算是怎么来的,它的原理是什么?难道这就是所谓精准响应式排版的出处吗?欲知其中之原委,看官请继续。

实现原理

既然实现“精确控制响应式排版”跟下面这段代码

@media screen and (min-width: 20rem) {
    h3,.h3 {
        font-size: calc (1.266rem + .511 * (100vw - 20rem) / 100);
    }
}

有着千丝万缕的关系,那么我们就有必要对代码做一个分析。在分析这段代码之前,有几个相关的概念需要先和大家一起聊聊。

@meida

@media在CSS Media Queries模块,也是CSS条件属性(Conditional CSS)之一。它也是响应式设计中必不可缺的属性,可以给@media传递相关参数,告诉浏览器选择对应的CSS规则。简单的理解就是在不同的断点下,渲染出不同的效果。这里就不对其做详细的阐述,如果感兴趣的话,可以点击这里进行了解。

calc()

calc()CSS Values and Units Module Level 3属性之一。它是CSS自带的函数功能,其最大功能就是可以帮助你做一些数学计算,比如:

width: calc( 100% - 20px);

可以很方便的帮助我们计算出width的值。特别是在一些流体布局中,比如上面的代码,我们需要在%值中减去一定的px值时,它让我们变得轻松。不过在使用calc()时有一个细节需要特别的注意,那就是在计算符的前后必须要有一个空格。当然,通过calc()在CSS中做一些数值运算,很多同这担心会影响页面的性能,对于这方面,我也没做过相关测试,在网上找了一圈,并没有找到相关的文章介绍,如果你比较擅长这方面,欢迎做一些相关的测试,并且把你的测试结果与大家分享。那么有关于其详细的介绍,可以阅读早前整理的另外一篇博文《CSS3的calc()使用》。

CSS中的单位

remvw都是CSS中的单位,简单点讲他们都是CSS单位中的相对单位,可以根据一定的参考值做相应的变化。那么这也是我们这篇文章的重点之一。接下简单的阐述一下几个比较重要的单位:remvwvhvminvmax

rem

rem是相对于根元素htmlfont-size来计算。基于这个原理,在响应式排版中,通过@media在不同的断点之下修改html元素的font-size,然后对应使用rem单位的元素就会做相应的变化。例如下面的代码:

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

假设h1h2h3font-size分别是50px37px28px,那么其在默认情况下以及800px1200px对应的font-size将如下表所示:

@media 断点 HTML (font-size) h1(50px) h2(37px) h3(28px)
默认 16px 3.125rem 2.3125rem 1.75rem
800px 18px 2.777rem 2.055rem 1.555rem
1200px 20px 2.5rem 1.85rem 1.4rem

根据上表的数据,不难表现,不管在什么样的断点下,元素h1h2h3最终的font-size都是一样的。并没有达到@Richard Rutter所描述的,在不同的屏幕(断点)具有不同的font-size,让你的文本在Banner区显得大气。言外之意,也没有达到精确响应式布局效果。

上面简单的回忆了rem在CSS中的简单原理,如果你想更进一步的了解rem的话可以阅读《CSS3的REM设置字体大小》一文或者点击这里做进一步深入的了解。

vw、vh、vmin和vmax

vwvhvminvmax也是CSS的相对单位,但它们又称之为视窗单位。它们的大小都是由视窗大小来决定的,不管哪个值,1就相当于视窗widthheight1%。具体描述如下:

  • vw:视窗宽度的百分比
  • vh:视窗高度的百分比
  • vmin:当前较小的vwvh
  • vmax:当前较大的vwvh

在这种情况下,视窗,指的是浏览器屏幕。1vw就意味着1%的浏览器的宽度。100vw将意味着整个浏览器宽度。

视窗单位的好处在于当视窗大小改变时,他们会自动的重新计算其大小。当重新加载页面,调整页面大小或改变页面方向时就会发生此现象。

那么在响应式排版本中,如果font-size使用vw作为元素的单位,那么元素的font-size大小直接跟视窗大小有关系,言外之意就跟断点有直接关系。比如:

h1 {
    font-size: 13vw;
}

其在不同的断点下的font-size就如下表所示:

@media (断点) h1(13vw)
320px 320 ÷ 100 × 13 = 41.6px (42px)
768px 768 ÷ 100 × 13 = 99.84px (100px)
1024px 1024 ÷ 100 × 13 = 133.12px (133px)
1280px 1280 ÷ 100 × 13 = 166.4px (166px)
1920px 1920 ÷ 100 × 13 = 249.6px (250px)

使用vw做为单位值主要是跟视窗的width有关系,比如在移动设备,横屏和竖屏下,他们的值就不一样,因为横屏和竖屏的视窗大小是不一样的。如下图所示:

标题在横屏下明显的要比在竖屏下大得多。

vhvw非常的类似,只不过是根据视窗的高度来进行计算。这里就不再做累述。

虽然使用vw单位之后,元素的font-size会跟随视窗大小做相应的变化,相比而言,比前面的rem更具响应式,但还是并不完美。仔细观察一下,文本不管在什么终端下(比如iPhone),那么他的横屏或竖屏状态下,它的可视空间是一样的,如果我们让文本在可视空间具有一定的font-size,也就是说视觉效果更加一致,是不是更完美一些呢?那么这个时候vmin相对而言就要比vw完美一些。

再继续下面的内容之前,先简单的了解一下vmin或者说vmax

vhvm总是与视口的高度和宽度有关,与之不同的,vminvmax是与这次宽度和高度的最大值或最小值有关,取决于哪个更大和更小。例如,如果浏览器设置为1100px宽、700px高,1vmin会是7px,1vmax11px。然而,如果宽度设置为800px,高度设置为1080px1vmin将会等于8px1vmax将会是10.8px

设想你需要一个总是在屏幕上可见的元素。使用高度和宽度设置为低于100vmin值将可以实现这个效果。例如,一个正方形的元素总是至少接触屏幕的两条边可能是这样定义的:

.box {
    height: 100vmin;
    width: 100vmin;
}

.box {
    height: 100vmax;
    width: 100vmax;
}

如此一来,在一个可控空间,相比vw单位,在排版中采用vmin会将更适合。简单来看看不同Viewport尺寸下vwvmin对应的值(13vmin):

Viewport 13 vw 13 vmin
320 × 480 42px 42px
414 × 736 54px 54px
768 × 1024 100px 100px
1024 × 768 133px 100px
1280 × 720 166px 94px
1366 × 768 178px 100px
1440 × 900 187px 117px
1680 × 1050 218px 137px
1920 × 1080 250px 140px
2560 × 1440 333px 187px

混合字号

来假设一下,在没有断点的情况之下,如果我们想从400pxfont-size: 16px过渡到800pxfont-size: 24px;。我们可以通过断点最小和最大字号来进行计算。这个时候就需要使用到前面介绍的calc()函数。将calc()和视窗单位结合在一起,这样一来可以让我们的排版更具灵活,这样也实现了在一个范围内的视窗下有具体的像素值。

@media screen and (min-width: 20rem) {
    h3,.h3 {
        font-size: calc (1.266rem + .511 * (100vw - 20rem) / 100);
    }
}

将代码中的数学运算部分抽取出来,就类似下图所示:

上图就不做详细的阐述了,我想大家看图就应该知道其中的意思。

精确控制响应式排版

经过前面的大篇篇幅,对其中的一些属性的使用性质以及相关的用法有了一定的了解,接下来回到本文的正题,**如何精确控制响应式排版?**其实要实现这一个特性,说实话,也就是如何更好的控制Web页面中各元素的font-size大小,让其值在适合的空间有一个更适合的字号。接下来简单的分几个步骤。

通过calc()限制字体缩放

如果你想设置一个确切的最小字号,那么可以通过calc()px以及vw结合在一起。比如:

:root {
    font-size: calc( 16px + 3vw);
}

上面的代码就是设置了根元素的默认font-size值为16px + 3vw

**注意:**在一些浏览器使用视窗单位和calc()结合在一起还是会有一点的问题,所以为了安全,最好是在媒体查询中使用。

通过媒体查询限制字体缩放

为了防止文本缩放的比例低于特定的阈值,我们只需要借助媒体查询。在特定的的断点之下采用。

:root {
    font-size: 18px; 
}
@media (min-width: 600px){
  :root {
    font-size: 3vw; /*3 × 600 ÷ 100 = 18px*/
  }
}

通过calc()精确控制字号大小

根据上述的表达式,我们就可以借助calc()控制一个精确的font-size

font-size: calc( 12px + (24 - 12) * ( (100vw - 400px) / ( 800 - 400) ));

这行代码实现了从400pxfont-size: 16px过渡到800pxfont-size: 24px;。为了能在响应式下更好的精确控制文本的font-size。我们只需要在媒体查询下控制:rootfont-size,这就回到了文章前面看到的代码:

:root,html {
  font-size: .875rem
}

@media screen and (min-width: 20rem) {
  :root,html {
    font-size:calc(.875rem + .25 * (100vw - 20rem) / 100)
  }
}

@media screen and (min-width: 120rem) {
  :root,html {
    font-size:1.125rem
  }
}

使用的时候只需这样:

body {
  font-size: 1.125rem;
}

除了可以运用于文本之外,这种思路也可以运用于其他地方,比如说line-hieghtwidth之类。有关于这方面的示例可以点击这里查阅

使用CSS处理器进行封装

如果你坚持将文章看到这里,你也应该知道其中的原委。为了方便日后使用,我们可以借助于Sass这样的CSS预处理对其进行封装。比如封装一个fluid-type的混合宏。在这个混合宏,我们将传递几个参数:

  • $properties: CSS的属性,比如widthline-heightfont-size
  • $min-vw:视窗最小宽度
  • $max-vw:视窗最大宽度
  • $min-value:最小值
  • $max-value:最大小值

我们可以这样写@mixin fluid-type:

@mixin fluid-type($properties, $min-vw, $max-vw, $min-value, $max-value) {
    & {
        @each $property in $properties {
            #{$property}: $min-value;
        }

        @media screen and (min-width: $min-vw) {
            @each $property in $properties {
                #{$property}: calc(#{$min-value} + #{strip-units($max-value - $min-value)} * ((100vw - #{$min-vw}) / #{strip-units($max-vw - $min-vw)}));
            }
        }

        @media screen and (min-width: $max-vw) {
            @each $property in $properties {
                #{$property}: $max-value;
            }
        }
    }
}

假设$min-vw($minScreen)的值为20rem$max-vw($maxScreen)的值为50rem$min-vaule($minFont)为.8rem$max-value($maxFont)为2rem。在:root中调用的时候:

// SCSS变量
$minScreen: 20rem; // $min-vw
$maxScreen: 50rem; // $max-vw
$minFont: .8rem; // $min-value
$maxFont: 2rem; // $max-value
:root {
    @include fluid-type(font-size, $minScreen, $maxScreen, $minFont, $maxFont);
}

编译出来的CSS:

:root {
  font-size: 0.8rem;
}
@media screen and (min-width: 20rem) {
  :root {
    font-size: calc(0.8rem + 1.2 * ((100vw - 20rem) / 30));
  }
}
@media screen and (min-width: 50rem) {
  :root {
    font-size: 2rem;
  }
}

除了Sass之外,还可以使用PostCSS插件rucksack-css

总结

这篇文章简单介绍了如何通过CSS的calc()函数以及CSS的一些相对单位,比如rem,特别是视窗单位vw或者vmin来实现精确的响应式排版。并且介绍了其中一些实现原理以据依据。如果你感兴趣的话,可以在自己的测试项目中实践一下。如果你想看一些别人写好的DEMO,那么你可以在Codepen上浏览@MadeByMike整理的一些案例

如果你有类似或更具创意的Demo,也可以在Codepen上书写,并且将你的Demo地址在下面的评论中分享给我们。或者说你有更好的思路,也欢迎与我们一起分享。

扩展阅读

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.fedev.cn/responsive/precise-control-responsive-typography.htmlAdidas Tubular Boost