以编程方式缩放 Web 应用程序的最佳方法

Avatar of Michael Romanov
Michael Romanov 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

网站可访问性一直都很重要,但如今,随着大多数国家/地区的政府都制定了明确的标准和法规,支持这些标准并使我们的项目尽可能地易于访问变得更加至关重要。

W3C 建议提供了 3 个级别的符合性:AAAAAA。为了达到 AA 级,除了其他要求之外,我们还必须提供一种方法来增加网站的字体大小。

1.4.4 调整文本大小:除了 字幕文本图像 外,文本 可以使用 辅助技术 调整大小至 200%,而不会丢失内容或功能。(AA 级)

让我们看看针对此问题的解决方案,并尝试找到最佳方案。

更新 (2017 年 9 月 25 日):事实证明,WCAG 并不需要除了用户代理提供的功能之外的文本调整大小自定义解决方案。我们只需要确保网站在调整大小后看起来正常即可。但是,如果我们仍然出于任何原因想要缩放元素,以下是对执行此操作的不同方法的全面分析。

不完整的解决方案:CSS zoom

当我们谈论尺寸更改时,第一个想到的词就是缩放。CSS 具有 zoom 属性,它可以准确地执行我们想要的操作——增加尺寸。

让我们看一下一个常见的界面设计模式(我们将在本文的其余部分使用它):一个水平导航栏,在某个断点处会转换为菜单图标。

这就是我们想要发生的事情。没有换行,整个菜单在指定的断点处转换为菜单图标。

下面的 GIF 显示了将 zoom 方法应用于菜单元素后得到的结果。我创建了一个切换器,允许选择不同的尺寸并应用相应的缩放级别。

如果您想试用它,请查看 此处的示例代码

菜单超出了可见区域,因为我们无法使用缩放以编程方式增加视口宽度,也无法因为要求而换行菜单。菜单图标也不会出现,因为屏幕尺寸实际上没有改变,与我们单击切换器之前相同。

所有这些问题,加上 zoom 本身根本不受 Firefox 支持。

错误的解决方案:缩放变换

我们可以使用 transform: scale 获得与 zoom 非常相似的效果。除了 transform 得到更广泛的 浏览器支持。但是,我们遇到了与 zoom 完全相同的问题:菜单不适合可见区域,更糟糕的是,它也超出了垂直可见区域,因为页面布局是根据初始 1 倍缩放计算的。

查看 Mikhail Romanov (@romanovma) 在 CodePen 上的示例代码 Font-switcher–wrong-scale

另一个不完整的解决方案:remhtml font-size

与其缩放或变换,我们可以使用 rem 作为页面上所有元素的尺寸单位。然后,我们可以通过更改 html 元素的 font-size 属性来更改其大小,因为根据定义,1rem 等于 htmlfont-size 值。

这是一个相当不错的解决方案,但并不完美。正如您在以下演示中看到的,它与前面的示例存在相同的问题:在某个点上,它无法水平适配,因为所需的空间增加了,但视口宽度保持不变。

查看 Mikhail Romanov (@romanovma) 在 CodePen 上的示例代码 Font-switcher–wrong-rem

问题在于,媒体查询不会根据尺寸变化进行调整。当我们放大尺寸时,媒体查询应该相应地调整,以便在相同位置的效果在尺寸变化之前发生,相对于内容而言。

有效的解决方案:使用 Sass mixin 模拟浏览器缩放

为了寻找灵感,让我们看看原生浏览器缩放功能是如何处理这个问题的。

哇!Chrome 理解缩放实际上确实会改变视口。缩放倍数越大,视口越窄。这意味着我们的媒体查询将按我们的预期生效,并且需要它们生效。

实现此目标的一种方法(不依赖于原生缩放,因为我们无法访问 AA 所需的页面内控件的原生缩放)是以某种方式在每次切换字体大小时更新媒体查询值。

例如,假设我们在 1024px 处有一个媒体查询断点,并且我们执行 200% 缩放。我们应该将该断点更新为 2048px 以补偿新的尺寸。

这难道不应该很容易吗?我们不能只使用 rem 单位设置媒体查询,以便当我们增加 font-size 时媒体查询会自动调整吗?遗憾的是,这种方法不起作用。尝试在此 示例代码 中将媒体查询从 px 更新为 rem,您会发现没有任何变化。在增加尺寸后,布局不会切换断点。这是因为,根据 标准,媒体查询中的 remem 单位都是基于 html 元素 font-size 的初始值计算的,该值通常为 16px(并且可能会有所不同)。

媒体查询中的相对单位基于初始值,这意味着单位永远不会基于声明的结果。例如,在 HTML 中,em 单位相对于“font-size”的初始值。

但是,我们可以利用 Sass mixin 的功能来解决这个问题!以下是我们的操作方法。

  • 我们将对 html 元素使用一个特殊的类来表示每个尺寸(font-size--sfont-size--mfont-size--lfont-size--xl 等)。
  • 我们将使用一个特殊的 mixin,它为断点和尺寸的每种组合创建一个媒体查询规则,并同时考虑屏幕宽度和应用于 html 元素的修饰符类。
  • 在我们要应用媒体查询的任何地方,我们都将代码包装在此 mixin 中。

以下是此 mixin 的外观。

$desktop: 640px;
$m: 1.5;
$l: 2;
$xl: 4;

// the main trick is here. We increase the min-width if we increase the font-size
@mixin media-desktop {
  html.font-size--s & {
    @media (min-width: $desktop) {
      @content;
    }
  }

  html.font-size--m & {
    @media (min-width: $desktop * $m) {
      @content;
    }
  }

  html.font-size--l & {
    @media (min-width: $desktop * $l) {
      @content;
    }
  }

  html.font-size--xl & {
    @media (min-width: $desktop * $xl) {
      @content;
    }
  }
}
.menu {
  @include media-desktop {
    &__mobile {
      display: none;
    }
  }
}

以及它生成的 CSS 示例。

@media (min-width: 640px) {
  html.font-size--s .menu__mobile {
    display: none;
  }
}
@media (min-width: 960px) {
  html.font-size--m .menu__mobile {
    display: none;
  }
}
@media (min-width: 1280px) {
  html.font-size--l .menu__mobile {
    display: none;
  }
}
@media (min-width: 2560px) {
  html.font-size--xl .menu__mobile {
    display: none;
  }
}

因此,如果我们有 n 个断点和 m 个尺寸,我们将生成 n 乘以 m 个媒体查询规则,这将涵盖所有可能的情况,并使我们能够在字体大小增加时使用增强的媒体查询。

查看下面的示例代码以了解其工作原理。

查看 Mikhail Romanov (@romanovma) 在 CodePen 上的示例代码 Font-switcher–right

缺点

但也有一些缺点。让我们看看如何处理它们。

媒体查询选择器上的特异性增加。

媒体查询内的所有代码都获得了额外的特异性级别,因为它位于 html.font-size — x 选择器内部。因此,如果我们采用移动优先方法并例如在元素上使用 .no-margin 修饰符,则桌面正常样式可能会胜过修饰符,并且会应用桌面边距。

为了避免这种情况,我们可以为移动端创建相同的 mixin,并使用我们的 mixin 不仅包装桌面 CSS 代码,还包装移动端 CSS 代码。这将平衡特异性。

其他方法是通过人为地提高特异性或创建具有所需功能(在我们的示例中为无边距)的 mixin 并将其不仅用于移动端,还用于每个断点代码来处理每个特殊情况。

生成的 CSS 代码量增加。

生成的 CSS 代码量会更高,因为我们为每个尺寸都生成了相同的 CSS 代码。

如果文件使用 gzip 压缩(通常情况下是这样),这应该不是问题,因为它可以很好地处理重复的代码。

一些前端框架(如 Zurb Foundation)在 JavaScript 工具和 CSS 媒体查询中使用内置断点。

这有点棘手。就我个人而言,我会尽量避免依赖屏幕宽度的框架功能。其中一个经常被忽略的功能是网格系统,但随着 flexbox 和 grid 的兴起,我认为这已经不再是问题了。查看这篇文章,了解有关如何构建自己的网格系统的更多详细信息。

但是,如果项目依赖于这样的框架,或者我们不想解决特异性问题但仍然想使用 AA,那么我会考虑去除固定高度元素,并结合使用 rems 和修改 html 元素的 font-size 来相应地更新布局和文本尺寸。


感谢您的阅读!如果您觉得这有帮助,或者在符合 1.4.4 调整文本大小 W3C 无障碍要求方面遇到其他问题,请告诉我。