当我们在讨论Web开发的时候,就不得不聊到响应式设计
。这是个老生常谈的设计理念了,并且已经存在很多年了。而媒体查询
是响应式设计
的一部分,但却不会随处可见。自从引入媒体查询
(实际上是几十年前)以来,CSS 已经发展到有很多技巧可以帮助我们大幅减少我们使用的媒体查询
的使用。这篇文章里面,将向你展示如何仅用一个 CSS 声明替换多个媒体查询。这些方法可以减少代码,更容易维护,并与日常项目开发紧密地联系在一起。
让我们先来看看一些广泛使用的方法来构建没有媒体查询的响应式布局。这并不奇怪——这些方法与flexbox和grid 相关。
使用flex和flex-wrap
.container {
display:flex;
flex-wrap:wrap; /* this */
gap:10px;
}
.container > div {
height:100px;
flex:400px; /* and this */
background:red;
}
复制代码
在上面的演示中,flex: 400px
为网格中的每个元素设置一个等于400px
的基本宽度。如果当前行上没有足够的空间来容纳它,每个元素将换行到一个新行。同时,每行上的元素会增长/拉伸
以填充容器中的任何剩余空间,如果该行无法容纳另一个400px
元素,则它们会向后收缩。
flex: 400px
是相当于flex: 1 1 400px
( flex-grow: 1, flex-shrink: 1, flex-basis: 400px)的简写。
到目前为止我们所拥有的:
✔️ 只有两行代码
❌ 一致的页脚元素宽度
❌ 控制每行的项目数
❌ 控制项目何时换行
使用auto-fit和minmax
.container {
display:grid;
grid-template-columns:repeat(auto-fit,minmax(400px,1fr)); /*this */
gap:10px;
}
.container > div {
height:100px;
background:red;
}
复制代码
与前面的方法类似,我们设置了一个基本宽度400px
,使用repeat(auto-fit, minmax(400px, 1fr))
——如果有足够的空间,我们的项目就会换行。这一次,我们使用的是CSS Grid
。这意味着每行上的元素也会增长以填充任何剩余空间,但与 flexbox 配置
不同,最后一行保持与其余元素相同的宽度。
因此,我们解决了不一致的页脚元素宽度
问题,但却也引入了一个新问题,因为我们的项目不能缩小到以下400px
,会导致一些溢出。
✔️ 只有一行代码
✔️页脚中的元素宽度一致
❌ 控制每行的项目数
❌ 项目会增长,但不会缩小
❌ 控制项目何时换行
控制每行的项目数
让我们以第一个示例为例,更改flex: 400px
为flex: max(400px, 100%/3 - 20px)
,
演示地址: codepen.io/xiaobinwu/p…
调整屏幕大小后,即使在超宽屏幕上,每一行的项目也不会超过三个。我们将每行限制为最多三个元素,这意味着在任何时候,每行仅包含一到三个项目。
让我们剖析一下代码:
-
当屏幕宽度增加时,我们容器的宽度也会增加,这意味着它
100%/3
会比400px
某个时候更大。 -
由于我们使用的
max()
功能,那我们会取100%/3
的长度,也就是始终会被分为3等分
。 -
当屏幕宽度较小时,会固定为
400px
。
你可能会问:公式中的那个20px
值到底是什么?
它是网格模板gap值的两倍,也就是10px
两倍。当我们在一行中有三个项目时,元素之间有两个间隙(中间元素的左右两侧各有一个),因此对于N项目我们应该使用max(400px, 100%/N - (N - 1) * gap)。 是的,gap定义宽度的时候需要考虑,不过不用担心,我们还是可以优化公式去掉!
我们可以使用max(400px, 100%/(N + 1) + 0.1%)。 逻辑是:我们告诉浏览器每个项目的宽度等于 100%/(N + 1)
,所以每行 N + 1 个项目,但我们添加了一个很小的百分比 ( 0.1%)——因此其中一个项目会被换行,我们实现每行只有N个项目。聪明吧?再也不用担心差距了!
.container {
display:flex;
flex-wrap:wrap; /* this */
gap:10px;
}
.container > div {
height:100px;
flex:max(400px, 100%/4 + 0.1%); /* and this */
background:red;
}
复制代码
演示地址: codepen.io/xiaobinwu/p…
现在我们可以控制每行的最大项目数,这使我们可以部分控制每行的项目数。
同样的方法也适用于 CSS Grid
方法:
.container {
--w:400px;
--n:3;
display:grid;
grid-template-columns:repeat(auto-fit,minmax(max(var(--w), 100%/(var(--n) + 1) + 0.1%),1fr)); /*this */
gap:10px;
}
.container > div {
height:100px;
background:red;
}
复制代码
演示地址: codepen.io/xiaobinwu/p…
minmax()定义了一个长宽范围的闭区间,传参最小值、最大值,则 最小值 <= 显示值 <= 最大值
离我们的目标越来越近了!
✔️ 只有一行代码
✔️页脚中的元素宽度一致
⚠️ 部分控制每行项目数量
❌ 项目会增长,但不会缩小
❌ 控制项目何时换行
项目增长,但不缩小
我们之前注意到,如果基础宽度大于容器宽度,使用网格方法会导致溢出。
我们来做一些修改:
max(400px, 100%/(N + 1) + 0.1%)
|
| 修改成以下
|
clamp(100%/(N + 1) + 0.1%, 400px, 100%)
复制代码
- 当屏幕宽度很大时,400px会被限制变成为100%/(N + 1) + 0.1%,保持我们对每行最大项目数的控制。
- 当屏幕宽度很小时,会400px被限制变成100%,因此我们的项目永远不会超过容器宽度,就不会溢出了。
clamp(MIN, VAL, MAX),MIN最小值、VAL首选值、MAX最大值,等价于max(MIN, min(VAL, MAX))
.container {
--w:400px;
--n:3;
display:grid;
grid-template-columns:repeat(auto-fit,minmax(clamp(100%/(var(--n) + 1) + 0.1%,var(--w),100%),1fr)); /*this */
gap:10px;
}
.container > div {
height:100px;
background:red;
}
复制代码
演示地址: codepen.io/xiaobinwu/p…
✔️ 只有一行代码
✔️页脚中的元素宽度一致
⚠️ 部分控制每行项目数量
✔️ 项目会变大变小
❌ 控制项目何时换行
控制项目何时换行
到目前为止,我们无法控制元素何时从一行换到另一行。我们真的不知道它什么时候发生,因为它取决于很多事情,比如基本宽度、间隙、容器宽度等。为了控制这个,我们将改变我们的最后一个clamp()
公式,从这个:
clamp(100%/(N + 1) + 0.1%, 400px, 100%)
|
| 修改成以下
|
clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%)
复制代码
演示地址: codepen.io/xiaobinwu/p…
看到这样的一个公式,你可能这个又是什么鬼数学公式,请耐心等待。这比你想象的要容易。这是发生了什么:
当屏幕宽度 (100vw
) 大于 时400px
,(400px - 100vw
)结果为负值,并被限制为100%/(N + 1) + 0.1%
,这是一个正值。这给了我们每行N
个项目。 当屏幕宽度 ( 100vw
) 小于 时400px
,(400px - 100vw
)是一个正值并乘以一个大值,该值被限制在100%. 这导致每行单个项目占满。
我们在没有真正的媒体查询
@media
的情况下完成了第一个媒体查询(借助flexbox/grid
)!由于我们的公式,由于我们的clamp()
公式,每行的项目数从 N 到 1。应该注意的是,400px
在这种情况下充当临界点。
那从每行 N 项到每行 M 项,该怎么实现?
clamp(100%/(N + 1) + 0.1%, (400px - 100vw)*1000, 100%/(M + 1) + 0.1%)
复制代码
我想你现在可能已经明白了。当屏幕宽度大于400x
我们属于第一条规则(每行N个项目)时。当屏幕宽度小于 时400px
,我们属于第二条规则(每行M项目)。
演示地址: codepen.io/xiaobinwu/p…
讲解到这里,我们现在可以控制每行的项目数量以及该数量何时应该更改-----》仅使用 CSS 自定义属性和一个 CSS 声明。
我们也实现了我们的小目标,
✔️ 只有一行代码
✔️页脚中的元素宽度一致
✔️ 完全控制每行的项目数
✔️ 项目会变大变小
✔️ 控制项目何时换行
更多
更多clamp、max、min等css公式
的应用,可以点击演示地址去查看效果和代码,不要错过,很多场景我们日常开发都会用到,可以收藏起来。
每行 N 项到每行 M 项
clamp(clamp(100%/(N + 1) + 0.1%, (W1 - 100vw)*1000,100%/(M + 1) + 0.1%), (W2 - 100vw)*1000, 100%)
复制代码
- 当屏幕宽度小于 时W2,我们限制到100%,或每行一个项目。
- 当屏幕宽度大于 时W2,我们限制第一个clamp()。
- 在第一个限制中,当屏幕宽度小于 时W1,我们夹到100%/(M + 1) + 0.1%)或每行 M 个项目。
- 在第一个限制中,当屏幕宽度大于 时W1,我们夹到100%/(N + 1) + 0.1%)或每行 N 个项目。
演示地址: codepen.io/xiaobinwu/p…
模拟容器查询
演示地址: codepen.io/xiaobinwu/p…
切换元素的可见性
演示地址: codepen.io/xiaobinwu/p…
改变元素的位置
演示地址: codepen.io/xiaobinwu/p…
进度条
演示地址: codepen.io/xiaobinwu/p…
时间线布局
演示地址: codepen.io/xiaobinwu/p…
响应卡
演示地址: codepen.io/xiaobinwu/p…
对话气泡
演示地址: codepen.io/xiaobinwu/p…
固定按钮
演示地址: codepen.io/xiaobinwu/p…
固定警报
演示地址: codepen.io/xiaobinwu/p…