阅读 220

Element 2 组件源码剖析之响应式(媒体查询)

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

0x00 简介

前文 📚 Layout拾遗 提到了栅格布局系统中响应式布局的实现方式媒体查询,本文将对其深入分析, 耐心读完,相信会对您有所帮助。


0x01 媒体查询

媒体查询Media queries)是实现响应式设计的最常见方法,它可以让我们根据设备的大致类型(如打印、手机、平板或电脑等)或者特定的特征和设备参数(例如屏幕分辨率和浏览器视口宽度)来调整网站或应用。 例如,缩小小型设备上的字体大小,判断手机朝向(横屏或竖屏)等。

CSS 语法

媒体查询语法通常使用 @media(@规则),  可用于基于一个或多个媒体查询的结果来应用样式表。
通过指定的媒体类型与正在显示文档的设备类型匹配,并且媒体查询中的所有表达式均为 true,将应用样式规则。

// 媒体类型   媒体特性表达式
@media media-type and (media-feature-rule) {
  /* CSS rules go here */
}
复制代码

媒体类型

媒体类型Media types)描述设备的一般类别。可以指定的媒体类型为:

  • all 适用于所有设备。
  • print 适用于在打印预览模式下。
  • screen 主要用于屏幕(计算机屏幕、平板电脑、智能手机)。
  • speech 主要应用于屏幕阅读器等发声设备。

在不使用 notonly 操作符的情况下,媒体类型是可选的,默认为 all

媒体特性

媒体特性Media features)描述了user agent、输出设备,或是浏览环境的具体特征。媒体特性表达式是完全可选的,它负责测试这些特性或特征是否存在、值为多少。每条媒体特性表达式都必须用括号括起来。

最常用的特性是视口宽度,可以使用min-width最常用)、max-widthwidth媒体特性,在视口宽度大于或者小于某个大小——或者是恰好等于某个大小——的时候,应用CSS样式规则。

逻辑运算符

逻辑操作符logical operators) 使用 notandonly, (逗号) 构建复杂的媒体查询。

  • and 运算符用来把多个媒体属性组合起来,合并到同一条媒体查询中。只有当每个属性都为真时,这条查询的结果才为真。
  • not运算符用于否定媒体查询,如果不满足这个条件则返回true,否则返回false。 如果出现在以逗号分隔的查询列表中,它将仅否定应用了该查询的特定查询。 not关键字不能用于否定单个媒体功能表达式,而只能用于否定整个媒体查询。
  • only 运算符仅在整个查询匹配时才用于应用样式,并且对于防止老式浏览应用所选样式很有用。
  • , 逗号将多个媒体查询合并为一个规则。 逗号分隔列表中的每个查询都与其他查询分开处理。 因此,如果列表中的任何查询为true,则整个 @media 语句均返回true。 换句话说,列表的行为类似于逻辑或or运算符。

使用 not, only 运算符,必须指定媒体类型

在老式浏览器中将@media only screen and (min-width: 992px) {...} 简单地解释为only,因为没有叫 only 的设备,所以老式浏览器不会应用样式。 当不使用only时,将@media screen and (min-width: 992px) {...}简单地解释为screen,忽略查询的其余部分,应用样式。

0x02 断点

断点(Breakpoints) 是响应式设计的基石。 它是可自定义的宽度,使用它可以控制何时可以在特定视口或设备大小下调整布局。

断点设置

参照了 Bootstrap 的 响应式设计,预设了五个响应尺寸:xssmmdlg 和 xl

断点类中缀分辨率设备
X-Smallxs<767px超小屏幕 手机
Smallsm≥768px小屏幕 平板
Mediummd≥992px中等屏幕 桌面显示器
Largelg≥1200px大屏幕 大桌面显示器
Extra largexl≥1920px超大屏幕 大桌面显示器

在文件 theme-chalk\src\common\var.scss 定义了这些断点。

/* Break-point
--------------------------*/
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;

// 断点Map
$--breakpoints: (
  "xs": (
    max-width: $--sm - 1,
  ),
  "sm": (
    min-width: $--sm,
  ),
  "md": (
    min-width: $--md,
  ),
  "lg": (
    min-width: $--lg,
  ),
  "xl": (
    min-width: $--xl,
  ),
);
复制代码

0x03 组件响应式实现

@mixin res()

在文件 theme-chalk\src\mixins\mixins.scss 定义了指令 res构建css,使用媒体查询来创建关键的分界点阈值。

/* Break-points
 -------------------------- */
// 断点Map
$--breakpoints: (
  "xs": ( max-width: 767px ),
  "sm": ( min-width: 768px ),
  "md": ( min-width: 992px ),
  "lg": ( min-width: 1200px ),
  "xl": ( min-width: 1920px ),
);

@mixin res($key, $map: $--breakpoints) {
  // 循环断点Map,如果存在则返回
  @if map-has-key($map, $key) {
    @media only screen and #{inspect(map-get($map, $key))} {
      @content;
    }
  } @else {
    @warn "Undefeined points: `#{$map}`";
  }
}
复制代码

指令功能逻辑如下:

  1. 指令 res 接受两个参数 $key(响应尺寸) 和 $map (断点Map)。
  2. 指令内部使用函数 map-has-key($map,$key) 作用是根据 $key 参数,返回 $key $map 中对应的 value 值。如果 $key 不存在 $map中,将返回 null 值(此时生成警告断点未定义 Undefeined points )。
  3. 若返回 true,使用函数 map-get($map,$key) 返回 $key$map 中对应的 value 值。
  4. 使用函数 inspect($value) 将其转成字符串,使用插值 #{}@content 生成 媒体查询。
// 参数 $key  值为  xs
// 获取Map
map-get($map, $key) =>  ( max-width: 767px )
// 返回string
inspect(map-get($map, $key)) =>  '( max-width: 767px )'

@media only screen and #{inspect(map-get($map, $key))} { @content; }
// 编译结果
@media only screen and (max-width: 767px) { ... }  
复制代码

在栅格系统中,媒体查询通过断点构建CSS。响应式布局五个响应尺寸样式如下:

/* 超小屏幕(手机,小于 768px) */
@media only screen and (max-width: 767px) { ... } 
/* 小屏幕(平板,大于等于 768px) */
@media only screen and (min-width: 768px) { ... }
/* 中等屏幕(桌面显示器,大于等于 992px) */
@media only screen and (min-width: 992px) { ... }
/* 大屏幕(大桌面显示器,大于等于 1200px) */
@media only screen and (min-width: 1200px) { ... }
/* 超大屏幕(大桌面显示器,大于等于 1920px) */
@media only screen and (min-width: 1920px) { ... }
复制代码

0x04 基于断点的隐藏实现

Element 额外提供了一系列类名,用于在某些条件下隐藏元素。

使用方式

首先,项目引入样式文件:

import 'element-ui/lib/theme-chalk/display.css';
复制代码

然后将这些类名可以添加在任何 DOM 元素或自定义组件上。

  • hidden-xs-only - 当视口在 xs 尺寸时隐藏
  • hidden-sm-only - 当视口在 sm 尺寸时隐藏
  • hidden-sm-and-down - 当视口在 sm 及以下尺寸时隐藏
  • hidden-sm-and-up - 当视口在 sm 及以上尺寸时隐藏
  • hidden-md-only - 当视口在 md 尺寸时隐藏
  • hidden-md-and-down - 当视口在 md 及以下尺寸时隐藏
  • hidden-md-and-up - 当视口在 md 及以上尺寸时隐藏
  • hidden-lg-only - 当视口在 lg 尺寸时隐藏
  • hidden-lg-and-down - 当视口在 lg 及以下尺寸时隐藏
  • hidden-lg-and-up - 当视口在 lg 及以上尺寸时隐藏
  • hidden-xl-only - 当视口在 xl 尺寸时隐藏

实现原理

文件 theme-chalk\src\common\var.scss 定义了隐藏类断点Map$--breakpoints-spec ,使用逻辑运算符 and,让媒体查询能跨越多个断点宽度。

$--breakpoints-spec: (
  "xs-only": (
    max-width: $--sm - 1,
  ),
  "sm-and-up": (
    min-width: $--sm,
  ),
  "sm-only": "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
  "sm-and-down": (
    max-width: $--md - 1,
  ),
  "md-and-up": (
    min-width: $--md,
  ),
  "md-only": "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
  "md-and-down": (
    max-width: $--lg - 1,
  ),
  "lg-and-up": (
    min-width: $--lg,
  ),
  "lg-only": "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
  "lg-and-down": (
    max-width: $--xl - 1,
  ),
  "xl-only": (
    min-width: $--xl,
  ),
);

复制代码

转译后内容如下,定了关于视口宽度的各种媒体查询规则。

$--breakpoints-spec: (
  "xs-only":( max-width: 767px ),
  "sm-and-up": ( min-width: 768px ),
  "sm-only": "(min-width: 768px) and (max-width: 991px)",
  "sm-and-down": (max-width: 991px),
  "md-and-up": ( min-width: 992px ),
  "md-only": "(min-width: 992px) and (max-width: 1199px)",
  "md-and-down": (max-width: 1199px),
  "lg-and-up": (min-width: 1200px),
  "lg-only": "(min-width: 1200px) and (max-width: 1919px)",
  "lg-and-down": (max-width: 1919px),
  "xl-only": (min-width: 1920px),
);
复制代码

使用指令 res 生成带前缀 hidden的隐藏类。隐藏类CSS规则 display: none !important; 用于隐藏元素。

// 类前缀  
.hidden {
  // 遍历 Map $--breakpoints-spec 
  @each $break-point-name, $value in $--breakpoints-spec {
    // 生成.hidden-#{$break-point-name} 如 hidden-xs-only  hidden-sm-only
    &-#{$break-point-name} {
      // 生成媒体查询
      @include res($break-point-name, $--breakpoints-spec) {
        display: none !important;
      }
    }
  }
}
复制代码

最终生成 display.css,内容如下:

// 当视口在 `xs` 尺寸时
@media only screen and (max-width: 767px) { /* .hidden-xs-only  */ } 
// 当视口在 `sm` 及以上尺寸时
@media only screen and (min-width: 768px) { /* .hidden-sm-and-up  */ }
// 当视口在 `sm` 尺寸时
@media only screen and (min-width: 768px) and (max-width: 991px) { /* .hidden-sm-only  */ }
// 当视口在 `sm` 及以下尺寸时
@media only screen and (max-width: 991px) { /* .hidden-sm-and-down  */ }
//  当视口在 `md` 及以上尺寸时
@media only screen and (min-width: 992px) { /* .hidden-md-and-up  */ }
// 当视口在 `md` 尺寸时
@media only screen and (min-width: 992px) and (max-width: 1199px) { /* .hidden-md-only  */ }
// 当视口在 `md` 及以下尺寸时
@media only screen and (max-width: 1199px) { /* .hidden-md-and-down  */ }
// 当视口在 `lg` 及以上尺寸时
@media only screen and (min-width: 1200px) { /* .hidden-lg-and-up  */ }
// 当视口在 `lg` 尺寸时
@media only screen and (min-width: 1200px) and (max-width: 1919px) { /* .hidden-lg-only  */ }
// 当视口在 `lg` 及以下尺寸时
@media only screen and (max-width: 1919px) { /* .hidden-lg-and-down  */ }
// 当视口在 `xl` 尺寸时
@media only screen and (min-width: 1920px) { /* .hidden-xl-only  */ }
复制代码

0x05 📚参考

“使用媒体查询”,MDN
“Media_queries”,MDN
"@规则",MDN
screen sizes
“sass 在线编辑器”,sassmeister.com

0x06 关注专栏

此文章已收录到专栏中 👇,可以直接关注。

文章分类
前端
文章标签