这是我参与2022首次更文挑战的第16天,活动详情查看:2022首次更文挑战
在青训营大项目的搭建过程中,第一次尝试实践响应式的布局,为了让测试的视频卡片依据设备实现对应的缩放或者排列布局,学习到了不少的知识,特此记录。
实现目标:
- 浏览器缩放页面大小,视频(图片)卡片随之缩放。
- 移动端页面适配。
- 尽可能保持视频卡片原本的宽高比。
本文涉及的CSS相关细节(文末附代码链接):
- 布局
display:flex布局。 - 宽高
width/height:百分比,vw,vh等。还可以配置min-width,max-width。 - 媒体查询
@media:设置主流设备的宽度样式。
Flex布局
通过flex弹性布局可以将块级元素并列排列,通过设置justify-content属性设置调整子元素的排列规则,实现最基础的响应式。
HTML布局如下,main盒子内包裹四个固定宽度的卡片。
<div class="main">
<div class="card"><img src="https://source.acexy.cn/view/X2KlJe2"></div>
<div class="card"><img src="https://source.acexy.cn/view/X2KlJe2"></div>
<div class="card"><img src="https://source.acexy.cn/view/X2KlJe2"></div>
<div class="card"><img src="https://source.acexy.cn/view/X2KlJe2"></div>
</div>
版本一:CSS样式设置如下:父级元素设置flex布局且将justify-content设置为space-between,使得子元素对齐两端。
.main{
display: flex;
justify-content: space-between;
width: 100%;
min-height:600px;
background-color: grey;
}
.card{
width: 100px;
}
.card img{
width: 100%;
}
下面我们通过浏览器缩放,进行最简单的测试:
我们这种最基础的布局存在一定的缺陷:
- 当缩放到最大的时候,皮卡丘应该换行显示了,但是它只是保持着最大的宽度挤在一行。所以我们可以给弹性布局额外加上溢出换行的属性
flex-wrap: wrap。这样当屏幕容纳不下卡片的时候,多余的卡片就会换到下一行去了。
/*去除父级元素最小高度,增加溢出换行属性*/
.main{
display: flex;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
background-color: grey;
}
为了保持卡片的宽高比(尽可能不变形),我们固定设置了卡片的宽度(100px),高度会被图片撑开自由拉伸。那么就出现了两个问题:
-
高度不统一:如果卡片内包裹的是不同宽高比的图片或视频,那么每张卡片的高度不同,所有一行的卡片,高度会统一到拉伸后较高的卡片高度。
-
其他屏幕较大的设备显示的卡片较小。(图为谷歌浏览器最宽设备的显示情况,此时的皮卡丘卡片虽然仍为100px,但是显示起来已经很小了。)
我们先来解决其他屏幕设备的显示问题:问题来源于我们固定了卡片的宽度,而长度单位px是绝对单位,页面按精确像素展示。显然,解决方案为:我们需要把长度单位做出变换:绝对单位-->相对单位。
相对单位
(小声BB:px对应我们显示器的分辨率,理论上显示器分辨率不一定相同,px也算相对单位。)
相对单位的个人理解:
- 百分比:相对于父级盒子的百分比,一般设置宽度。如果设置了高度,那么一定是父盒子高度确定的情况。
- 相对字体大小:
- rem:相对于根元素的字体大小,一般来说浏览器的字体大小是16px,0.75rem == 12px;
- em:相对于父级元素的字体大小。
- 相对视口大小:
- vw:把屏幕宽度切分成100份,占据几份。
- vh:把屏幕高度切分成100份,占据几份。
版本二:我们使用vw来控制卡片的宽度,通过设置20vw(五分之一的屏幕宽度),就能不管屏幕分辨率如何变化,确保一行只有四张卡片,因为第五张卡片也需要五分之一的宽度,但是算上margin就不能容纳第五张卡片了。
.card{
width: 20vw;
border-radius: 5px;
background-color: rgb(214, 212, 212);
}
效果如下:
浏览器的缩放效果:
大型设备和小型移动设备的效果对比:
如果我们的卡片十分精美,不想在手机上并列展示四只皮卡丘,而是需要每只皮卡丘单独一列展示,这时候,就不能单靠相对的长度单位来设置了。
(比如上图下字的卡片,在移动端的字会小一点。比较适合单独一列或两列展示。)
媒体查询
@media:顾名思义,可以根据不同的媒体来确认不同的样式。
基础语法可见参考链接:
小细节:
- 媒体查询的范围:
- 媒体类型:不止是屏幕(
sreen),还有打印机(print); - 媒体特性:不止是宽高(
min-width),还有悬浮(hover),颜色(color)等; - 逻辑操作符:不止是
and,还有only,not,,。 如果只是单纯针对屏幕进行响应式的适配,其实是可以写出好几种不同的格式的。
- 媒体类型:不止是屏幕(
- 媒体查询的先后顺序:应该将限制条件严格的放在后面,条件宽松的放在前面。
- 原因:CSS的优先级 及 样式覆盖原则。 一般情况下,不同媒体查询内的CSS优先级相同,所以后面声明的样式容易覆盖前面的样式。
更新后的CSS代码如下:(版本三)
.main {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
background-color: grey;
}
.card {
box-sizing:border-box;
width: 30vw;/*被覆盖*/
border-radius: 5px;
background-color: rgb(214, 212, 212);
margin: 10px auto;
}
.card img {
width: 100%;
}
@media (min-width:300px){
.card{
width:90vw;
}
}
@media (min-width:425px){
.card{
width:48vw;
}
}
@media screen and (min-width:768px) {
.card{
width:30vw;
}
}
@media screen and (min-width:1024px) and (max-width:1549px){
.card{
width:24vw;
}
}
@media (min-width:1550px){
.card{
width:18vw;
}
}
(小声BB:理论上代码应该是风格统一的,上面仅做测试。)
加上了媒体查询的迭代后,我们再次测试不同设备下的卡片展示:
至此,我们实现了不同设备的响应式。但是仍有一个问题没有解决,那就是不同宽高比的照片卡片如何处理?
回顾当前的样式组合: Flex布局(两端对齐+溢出换行) + 媒体查询限定相对宽度(vw)
思路:
- 最直接的做法:限制卡片高度。这样使得卡片整体风格比较统一,但是卡片内的照片可能会出现因为强行填充而出现变形。
-
通过
object-fit: cover解决。这个CSS属性可以达到最佳最完美的居中自动剪裁图片的功能。object-fit参考链接 -
折中方法:通过设置相近的最低以及最高的高度,使得不同的照片卡片可以在一定范围内自由拉伸,卡片风格统一与照片不变形之间trade off。比如
min-height:25vh,max-height:30vh。
-
codepen链接:codepen.io/yyforreal/p…
- 在不使图片变形的情况下,显然不同宽高比的照片会撑开卡片使得卡片的高度不统一,那么我们可以设置瀑布流的形式,比如四列卡片,每列卡片向下流动。
- 如何排列不同列数的卡片是个难题,还需要去了解瀑布流算法。