整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

如何进行自适应网页设计?

.背景介绍

如今人们经过手机阅读网页占了大多数,随着阅读方式的改动,网页完成多终端自适应,无论关于防止工程师无谓的反复劳动或者是项目管理的便利性上来说重要性都是非常巨大的。

2.知识剖析

由于挪动设备越来越多的被人们运用,手机成为访问互联网的最常见终端,而我们设计的网页确是为了呈如今PC端。

手机的屏幕比拟小,宽度通常在600像素以下,而PC的屏幕宽度,普通都在1000像素以上(目前主流宽度是1366×768),有的还到达了2000像素。同样的内容,要在大小悬殊的屏幕上,都呈现出称心的效果,并不是一件容易的事。

很多网站的处理办法,是为不同的设备提供不同的网页,比方特地提供一个mobile版本,或者iPhone / iPad版本。这样做固然保证了效果,但是比拟费事,同时要维护好几个版本,而且假如一个网站有多个portal(入口),会大大增加架构设计的复杂度。

自适应是为了解决如何在不同大小的设备上呈现同样的网页。

3.常见问题

如何进行自适应网页设计

4.解决方案

  1. 在HTML头部增加viewport标签。

通俗的讲,移动设备上的viewport就是设备的屏幕上能用来显示我们的网页的那一块区域,在具体一点,就是浏览器上(也可能是一个app中的webview)用来显示网页的那部分区域,但viewport又不局限于浏览器可视区域的大小,它可能比浏览器的可视区域要大,也可能比浏览器的可视区域要小。在默认情况下,一般来讲,移动设备上的viewport都是要大于浏览器可视区域的,这是因为考虑到移动设备的分辨率相对于桌面电脑来说都比较小,所以为了能在移动设备上正常显示那些传统的为桌面浏览器设计的网站移动设备上的浏览器都会把自己默认的viewport设为980px或1024px(也可能是其它值,这个是由设备自己决定的),但带来的后果就是浏览器会出现横向滚动条,因为浏览器可视区域的宽度是比这个默认的viewport的宽度要小的。

该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,那就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条。

meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"

把移动设备上的viewport分为layout viewport 、 visual viewport 和 ideal viewport 三类,其中的ideal viewport是最适合移动设备的viewport,ideal viewport的宽度等于移动设备的屏幕宽度,只要在css中把某一元素的宽度设为ideal viewport的宽度(单位用px),那么这个元素的宽度就是设备屏幕的宽度了,也就是宽度为100%的效果。ideal viewport 的意义在于,无论在何种分辨率的屏幕下,那些针对ideal viewport 而设计的网站,不需要用户手动缩放,也不需要出现横向滚动条,都可以完美的呈现给用户。

要得到ideal viewport就必须把默认的layout viewport的宽度设为移动设备的屏幕宽度。因为meta viewport中的width能控制layout viewport的宽度,所以我们只需要把width设为width-device这个特殊的值就行了。

  • 2、不使用绝对宽度

在css中我们一般使用px作为单位,在桌面浏览器中css的1个像素往往都是对应着电脑屏幕的1个物理像素,这可能会造成我们的一个错觉,那就是css中的像素就是设备的物理像素。但实际情况却并非如此,css中的像素只是一个抽象的单位,在不同的设备或不同的环境中,css中的1px所代表的设备物理像素是不同的。在为桌面浏览器设计的网页中,我们无需对这个津津计较,但在移动设备上,必须弄明白这点。在早先的移动设备中,屏幕像素密度都比较低,如iphone3,它的分辨率为320x480,在iphone3上,一个css像素确实是等于一个屏幕物理像素的。后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。

  • 3、流动布局

流动布局(fluid grid) "流动布局"的含义是,各个区块的位置都是浮动的,不是固定不变的。

.main {float: right;width: 70%; }

.leftBar {float: left;width: 25%;}

float的好处是,如果宽度太小,放不下两个元素,后面的元素会自动滚动到前面元素的下方,不会在水平方向overflow(溢出),避免了水平滚动条的出现。

  • 4、MediaQuery模块

"自适应网页设计"的核心,就是CSS3引入的MediaQuery模块。 它的意思就是,自动探测屏幕宽度,然后加载相应的CSS文件。

link rel="stylesheet" type="text/css"media="screen and (max-device-width:400px)"href="a.css"

上面的代码意思是,如果屏幕宽度小于400像素(max-device-width: 400px),就加载a.css文件。

link rel="stylesheet" type="text/css"media="screen and (min-width: 400px)and (max-device-width: 600px)"href="b.css"

如果屏幕宽度在400像素到600像素之间,则加载b.css文件。

5.扩展思考

自适应与响应式的区别

自适应是为了解决如何才能在不同大小的设备上呈现同样的网页,让同一张网页自动适应不同大小的屏幕,根据屏幕宽度,自动调整网页内容大小。但是无论怎样,他们主体的内容和布局是没有变的。

自适应还是暴露出一个问题,如果屏幕太小,即使网页能够根据屏幕大小进行适配,但是会感觉在小屏幕上查看,内容过于拥挤,响应式正是为了解决这个问题而衍生出来的概念。它可以自动识别屏幕宽度、并做出相应调整的网页设计,布局和展示的内容可能会有所变动。

响应式布局被大家熟知的一个重要原因就是 twitter 开源了 bootstrap。

相比自适应网站,响应式网站省去了很多的控件,同时也省去了不少建立和维护的功夫。响应式布局就是一种流体,在按百分比缩放时也能相当的流畅。

使用响应式设计,你要记住所以的布局。这当然可能会使过程混乱,并且使设计更加复杂。这就意味着你应该专注于中等分辨率的视图,然后再用media querie调整为更低或更高的分辨率。 所以通常的做法是,在一个新的项目中使用响应式设计,在后期的改造中使用自适应设计。

自适应可用于改造现有的网站使其更好地适应移动端。这使你的设计可控制和开发多个特定的视图。你开发视图的数量完全取决于你,你的公司和全面的预算。然而,它也提供了一定量的控件(例如在内容和布局上),如此你便无须使用响应式设计。但当你设计多种分辨率时你会发现,在改变窗口大小的时候将会“跳出”布局。

自适应网站可以用于设计和开发一个拥有多个自适应视图的网站。所以这种设计通常用于改造网站。

代 CSS 颜色规范

新的 CSS Color Module 规范引入了多种新的颜色表示法. 可以支持多种色彩空间和颜色模型. 这解锁了很多的玩法. 比如: 基于一个颜色生成更深或更浅的颜色; 根据背景自适应文本色; 使用广色域颜色等等.

CSS Color Module Level 4 规范

引入了新的颜色表示法, 同时引入了多种色彩空间, 不再仅限于sRGB, 这些方法现代浏览器均已支持:

  • 基于 sRGB 色彩空间的方法: hsl(), hwb(), rgb();
  • 基于 CIELAB 色彩空间的方法: lab(), lch();
  • 基于 Oklab 色彩空间的方法: oklab(), oklch();
  • 其它色彩空间的方法: color()

再补充一点, 这些新方法中:

  • 带 h 的是使用极坐标的颜色空间: hsl, hwb, lch, oklch
  • color()使用任意 color-gamut CSS 媒介功能支持的颜色空间
  • 其它使用的是直角坐标系的颜色空间: rgb, lab, oklab

CSS Color Module Level 5 规范

  • 引入工具方法: color-mix(), 现代浏览器均已支持.
  • 扩展CSS Color Module Level 4 引入的新方法, 是它们支持相对颜色表示法. 相对颜色表示法绝大多数现代浏览器已支持.

CSS Color Module Level 6 规范

  • 引入工具方法: color-contrast(), 目前还没有浏览器支持, 但使用Level 4和Level 5中的工具和方法就可以实现.

在详细介绍这些现代新方法之前有必要对一些术语进行解释:

名词解释:

  • 颜色模型(color model)

颜色模型是指颜色与坐标系之间的映射和编码方式, 它定义维度分量与色彩空间的关系. 一个颜色模型就会有一个对应的色彩空间.

  • 色彩空间(color space)

色彩空间是某一颜色模型所涵盖的颜色的定义和命名. 每个颜色空间都由数学模型和关联的规则集定义. 色彩空间是表示颜色的三维网格, 色彩空间中的每个颜色都由三个通道分量(维度)来表示. 每个颜色空间都有一个定义的色域。

  • 色域(color gamut)

色域指的是它可以表示的特定颜色的范围, 通常指设备可以显示的颜色范围. 如 sRGB, P3, Rec2020 等

可以看出三者有一些共性的东西, 通常来说, 当上下文中使用颜色空间时强调的是它的颜色模型和算法. 当使用色域时强调的是能不能显示某些颜色

所有色彩空间

CSS Color Module Level 4 新引入的颜色方法统一了带和不带alpha通道的表示法

比如, 以前用的最多的rgb方法, 带和不带alpha通道是不同的方法: 不带alpha通道的是: rgb(r, g, b), 而带alpha通道的是: rgba(r, g, b, a)

现在可以统一使用: rgb(R G B [/ A]), alpha通道值是可选的. 注意为了区分旧方法, 新方法不使用逗号分隔分量, 而是用空格替代.

上面只是拿rgb方法举了个例子, 其实Level 4 中的所有新方法都支持这种表示法. 如: oklch(L C H [/A])

CSS Color Module Level 5 相对颜色表示法

语法: rgb(from <color> R G B[ / A]), hsl(from <color> H S L [/A]), oklch(from <color> L C H [/A]), ...

相对颜色是指从一个指定颜色的色彩空间转换到目标色彩空间, 通过对目标色彩空间中的维度变量进行微调后的结果作为输出.

这听起来比较绕, 简单点说就是可以根据原色, 对维度变量进行微调后输出. 主要特性:

  1. 可以从任意色彩空间转换到目标色彩空间, 甚至是CSS自定义属性.
  2. 转换后可以使用目标色彩模型中的维度变量
  3. 维度分量可以使用calc()等CSS函数计算

这3个特性解锁了一些原本只能通过js才能实现的一些功能.

例子1: 鼠标覆盖按钮时加深背景色:

方法1: 使用Level 5 规范中的相对颜色表示法:

.btn {
  --btn-bg: blue;
  background-color: var(--btn-bg);
}
.btn:hover {
  background-color: oklch(from var(--btn-bg) calc(l - 0.1) c h);
}

这个例子中--btn-bg自定义属性可以更改为任意颜色, 本例中使用了oklch作为目标色彩空间, 因为oklch可以做到调整亮度而不会影响色相.

从这个例子中可以看出, CSS自定义属性与相对颜色的结合使用, 可以创造出很多的新玩法.

方法2: 使用Level 5 规范中的color-mix()方法

.btn {
  --btn-bg: blue;
  background-color: var(--btn-bg);
}
.btn:hover {
  background-color: color-mix(in oklch, var(--btn-bg), black 10%);
}

color-mix()方法的意思是将颜色1和颜色2先转换到in关键字指定的目标色彩空间, 然后按百分比混合它们后输出.由于black只有L分量, 因此混合只影响了L分量, 因此就得到了不改变色相的情况下加深了颜色.

例子2: 根据不同背景色自适应高对比度的文本色

这个场景需要一种方式确定高对比度的算法模型. 通用的是WCAG 2.1, 但它不太准确, 还有一种是APCA, 它相对准确性更高, 参考性更大. 在APCA算法下, 采用oklch颜色模型下L分量在72%左右是一个比较好的对比度分界线. 72%以上采用黑色文本, 72%以下采用白色文本.

好了, 有了这个基础, 现在可以使用纯CSS实现自适应高对比度的文本色:

.btn {
  --btn-bg: blue;
  background-color: var(--btn-bg);
  color: oklch(from var(--btn-bg) clamp(0, calc((0.72 - l) * 10000), 1) 0 0);
}

这个例子中--bg自定义属性可以更改为任意颜色, 按钮文本都可以自适应的高对比度颜色. 本例中使用了oklch作为目标色彩空间, 因为oklch可以做到亮度是可预测性.

这里稍微解释这句:clamp(0, calc((0.72 - l)* 10000), 1), 意思是背景色的l维度分量 > 0.72说明背景是浅色的, 那么文本色的L分量就取0即黑色, 否则就说明背景是深色的,L分量就取1即白色. 如果不想纯白或纯黑, 适当调整各分量以及L的上下界即可.

这个例子还可以使用CSS Color Module Level 6中的color-contrast()实现相同的效果, 但目前还没有浏览器支持, 留着将来备用:

.btn {
  --btn-bg: blue;
  background-color: var(--btn-bg);
  color: color-contrast(var(--btn-bg) vs white,black);
}

color-contrast()的意思是选择vs关键字之后与第一个参数指定的颜色对比度最高的颜色作为输出.

这么多CSS颜色新方法, 我应该使用哪个?

现代网页中推荐使用oklch颜色模型, 使用 OKLCH 的好处:

  • OKLCH 各分量是独立的,互不影响的, 对一个分量的修改不会影响其它分量. 而 hsl()不是.
  • OKLCH 使设计师无需手动选择每种颜色。他们可以定义一个公式,选择几种颜色,并自动生成整个设计系统调色板。
  • OKLCH 可用于任意色域, 标准色域 sRGB, 广色域 P3, Rec2020 等。例如,新设备(如 Apple 的设备)可以显示比旧 sRGB 显示器更多的颜色,我们可以使用 OKLCH 来指定这些新颜色。而且超出色域浏览器会呈现最接近的颜色
  • 与 hsl() 不同,OKLCH 更适合颜色修改和调色板生成。因为它使用的是感知亮度, 因此不会再出现意想不到的结果,比如在 Sass 中使用 darken() 产生的意外结果。
  • 凭借其可预测的亮度 L, OKLCH 提供了更好的 a11y。
  • 随着 CSS 相对颜色函数的浏览器普及, 可以很方便的微调颜色(比如根据基色生成强调色, 只需修改 L 分量), 也将不再需要js侦测背景色的高对比度文本色. 根据 APAC 对比度算法,在感知亮度 L >=72% 时所有色相下黑色文本都不会产生对比度的可访问性问题.
  • 与 rgb() 或十六进制(#ca0000)不同,OKLCH 是人类可读和可预测的。您只需查看数字即可快速轻松地知道 OKLCH 值代表哪种颜色。OKLCH 的工作方式类似于 HSL,但它对亮度的编码比 HSL 更好。

OKLCH 颜色由亮度(明度)、色度(饱和度或纯度)、色相三个维度组成, 这也是人类认知里的颜色的三个基本属性.

  • 亮度(lightness) 是感知亮度,取值: 0-1 或 0-100%,0为黑色,1或100%为白色,"感知"意味着它对我们的眼睛具有一致的亮度,并且是可预测的, 这与 hsl()中的 L 是不同的。
  • 色度(chroma) 从灰色到最饱和的颜色, 对于 P3 和 sRGB 颜色空间应低于 0.37, Rec2020 颜色空间应低于 0.4
  • 色相(hue) 在色环中的角度, 色环: 红、橙、黄、绿、青、蓝、紫。红为20度左右, 每个颜色间隔50度左右.

详细分析请看这篇文章: OKLCH in CSS: why we moved from RGB and HSL—Martian Chronicles, Evil Martians’ team blog

为了兼容性考虑或想在js中使用CSS相同的颜色功能怎么办?

使用这个 colorjs.io npm包即可. 它完全支持CSS Color Module Level 4 和 Level 5 的规范

新的CSS Color Module规范产生的影响:

  1. 浏览器原生的颜色选取器元素需要更新以支持新的颜色模型, 目前还没有浏览器实现.
  2. 现有第三方的颜色选取器组件都是基于sRGB的, 不能选取新的色彩空间中的颜色. 这些组件需要重构.

我们的正式开源轻量级的基于Tailwindcss的React 组件库中的颜色选取组件正在重构中

家好,很高兴又见面了,我是"高级前端‬进阶‬",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。

1.为什么开始谈论关键路径 CSS

关键路径 CSS 很重要

构建页面的渲染树需要 CSS,而 JavaScript 在页面的初始构建过程中通常会阻塞 CSS。 因此,开发者应该确保任何非必要的 CSS 都被标记为非关键(例如:打印和其他媒体查询),并且关键 CSS 体积应该足够小,同时加载时间要尽可能短。

借助于关键 CSS,用户可以尽快获得基本样式;因为它就在文档的<head>中的<style>元素中,所以没有额外的请求到服务器获取样式表&等待请求的样式加载和渲染。


然后在后台加载下面的折叠或非关键样式,以避免渲染阻塞。

关键路径 CSS 应该内联

为了获得最佳性能,开发者可能需要考虑将关键 CSS 直接内联到 HTML 文档中。

内联 CSS 消除了关键路径中的额外往返,如果正确设置,可用于提供“单次往返”关键路径长度,其中只有 HTML 是阻塞资源。

2.critical.js 基础用法

critical.js 用于从 HTML 中提取关键内容并内联关键路径 CSS,比如首屏。critical.js 在 Github上开源,有超过10k的star,同时使用也非常简单:

import { generate } from 'critical';

同时支持以下配置选项:

generate({
  // Inline the generated critical-path CSS
  // - true generates HTML
  // - false generates CSS
  inline: true,
  // Your base directory
  base: 'dist/',
  // HTML source
  html: '<html>...</html>',
  // HTML source file
  src: 'index.html',
  // Your CSS Files (optional)
  css: ['dist/styles/main.css'],
  // Viewport width
  width: 1300,
  // Viewport height
  height: 900,
  // Output results to file
  target: {
    css: 'critical.css',
    html: 'index-critical.html',
    uncritical: 'uncritical.css',
  },
  // Extract inlined styles from referenced stylesheets
  extract: true,
  // ignore CSS rules
  ignore: {
    atrule: ['@font-face'],
    rule: [/some-regexp/],
    decl: (node, value) => /big-image\.png/.test(value),
  },
});

下面的示例用于生成关键路径 CSS,是最基本的用法示例:

generate({
  base: 'test/',
  src: 'index.html',
  target: 'styles/main.css',
  width: 1300,
  height: 900,
});

下面示例生成并压缩关键路径 CSS:

generate({
  base: 'test/',
  src: 'index.html',
  target: 'styles/styles.min.css',
  width: 1300,
  height: 900,
});

而下面的示例用于生成、压缩和内联关键路径 CSS:

generate({
  inline: true,
  base: 'test/',
  src: 'index.html',
  target: {
    html: 'index-critical.html',
    css: 'critical.css',
  },
  width: 1300,
  height: 900,
});

值得一提的是,generate 方法支持 callback 和 promise 两种方式返回处理后的结果,比如下面是 promise 的方式:

generate({
    base: 'test/',
    src: 'index.html',
    width: 1300,
    height: 900
}).then((({css, html, uncritical})) => {
    // You now have critical-path CSS as well as the modified HTML.
    // Works with and without target specified.
    ...
}).error(err => {
    ...
});

3.critical.js 高级用法

生成具有多种分辨率的关键路径 CSS

当网站是自适应,并且开发者想要为多种屏幕分辨率提供关键 CSS 时,这是一个有用的选项。 但是值得注意的是,最终输出将被压缩,以消除重复的规则包含。

generate({
  base: 'test/',
  src: 'index.html',
  target: {
    css: 'styles/main.css',
  },
  dimensions: [
    {
      height: 200,
      width: 500,
    },
    {
      height: 900,
      width: 1200,
    },
  ],
});

生成关键路径 CSS 并忽略特定选择器

当开发者想要推迟加载网络字体或背景图像,可以通过下面的方式:

generate({
  base: 'test/',
  src: 'index.html',
  target: {
    css: 'styles/main.css',
  },
  ignore: {
    atrule: ['@font-face'],
    decl: (node, value) => /url\(/.test(value),
  },
});

更多高级用法可以参考文末资料,本文不再过多展开。

4.critical.js 的可行替代方案

值得一提的是 Penthouse,同时 FilamentGroup 还维护一个关键 CSS 节点模块,类似于 Penthouse ,其会查找并输出页面的关键路径 CSS。

当开发者启用 Priority_ritic_css 过滤器时,适用于 nginx、apache、IIS、ATS 和 Open Lightspeed 的 PageSpeed Optimization 模块可以自动完成所有繁重的工作。

本文重点关注下 Penthouse,其他类似模块可以在文末参考资料中获取。Critical 和 Penthouse 之间的主要区别包括以下几个点:

  • Critical 会自动从 HTML 中提取样式表,并生成关键路径 CSS,而其他模块通常要求开发者预先指定这一点。
  • Critical 提供了内联关键路径 CSS 的方法(生成 CSS 后的常见下一步逻辑)
  • 由于同时处理生成和内联,因此能够抽出一些丑陋的样板文件,否则需要分别解决这些问题

总之,如果网站或应用程序有大量样式需要动态注入到 DOM 中(有时在 Angular 应用程序中很常见),建议直接使用 Penthouse。 但是 Penthouse 要求开发者预先提供样式,某些情况下可能比 Critical 提供更高的准确性。

参考资料

https://github.com/addyosmani/critical

https://github.com/addyosmani/critical-path-css-demo

https://github.com/filamentgroup/criticalCSS

https://github.com/pocketjoso/penthouse

https://www.tezify.com/how-to/defer_css_loading_with_loadcss/

https://web.dev/articles/extract-critical-css?hl=zh-cn