【青训营】响应式卡片设计

1,736 阅读6分钟

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

在青训营大项目的搭建过程中,第一次尝试实践响应式的布局,为了让测试的视频卡片依据设备实现对应的缩放或者排列布局,学习到了不少的知识,特此记录。

实现目标:

  • 浏览器缩放页面大小,视频(图片)卡片随之缩放。
  • 移动端页面适配。
  • 尽可能保持视频卡片原本的宽高比。

本文涉及的CSS相关细节(文末附代码链接):

  1. 布局display:flex布局。
  2. 宽高width/height:百分比,vw,vh等。还可以配置min-widthmax-width
  3. 媒体查询@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%;    
}

下面我们通过浏览器缩放,进行最简单的测试:

动画.gif

我们这种最基础的布局存在一定的缺陷:

  • 当缩放到最大的时候,皮卡丘应该换行显示了,但是它只是保持着最大的宽度挤在一行。所以我们可以给弹性布局额外加上溢出换行的属性flex-wrap: wrap。这样当屏幕容纳不下卡片的时候,多余的卡片就会换到下一行去了。
/*去除父级元素最小高度,增加溢出换行属性*/
.main{
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    width: 100%;
    background-color: grey;
}

image.png

为了保持卡片的宽高比(尽可能不变形),我们固定设置了卡片的宽度(100px),高度会被图片撑开自由拉伸。那么就出现了两个问题:

  1. 高度不统一:如果卡片内包裹的是不同宽高比的图片或视频,那么每张卡片的高度不同,所有一行的卡片,高度会统一到拉伸后较高的卡片高度。

  2. 其他屏幕较大的设备显示的卡片较小。(图为谷歌浏览器最宽设备的显示情况,此时的皮卡丘卡片虽然仍为100px,但是显示起来已经很小了。)

image.png

我们先来解决其他屏幕设备的显示问题:问题来源于我们固定了卡片的宽度,而长度单位px是绝对单位,页面按精确像素展示。显然,解决方案为:我们需要把长度单位做出变换:绝对单位-->相对单位

相对单位

(小声BB:px对应我们显示器的分辨率,理论上显示器分辨率不一定相同,px也算相对单位。)

相对单位的个人理解:

  1. 百分比:相对于父级盒子的百分比,一般设置宽度。如果设置了高度,那么一定是父盒子高度确定的情况。
  2. 相对字体大小:
    • rem:相对于根元素的字体大小,一般来说浏览器的字体大小是16px,0.75rem == 12px;
    • em:相对于父级元素的字体大小。
  3. 相对视口大小:
    • vw:把屏幕宽度切分成100份,占据几份。
    • vh:把屏幕高度切分成100份,占据几份。

版本二:我们使用vw来控制卡片的宽度,通过设置20vw(五分之一的屏幕宽度),就能不管屏幕分辨率如何变化,确保一行只有四张卡片,因为第五张卡片也需要五分之一的宽度,但是算上margin就不能容纳第五张卡片了。

.card{
    width: 20vw;    
    border-radius: 5px;
    background-color: rgb(214, 212, 212);        
}

效果如下:

浏览器的缩放效果:

动画.gif

大型设备和小型移动设备的效果对比:

image.png

如果我们的卡片十分精美,不想在手机上并列展示四只皮卡丘,而是需要每只皮卡丘单独一列展示,这时候,就不能单靠相对的长度单位来设置了。

(比如上图下字的卡片,在移动端的字会小一点。比较适合单独一列或两列展示。)

媒体查询

@media:顾名思义,可以根据不同的媒体来确认不同的样式。

基础语法可见参考链接:

  1. 简单易懂的菜鸟教程:@media查询

  2. 全面的MDN:使用媒体查询

小细节:

  1. 媒体查询的范围:
    • 媒体类型:不止是屏幕(sreen),还有打印机(print);
    • 媒体特性:不止是宽高(min-width),还有悬浮(hover),颜色(color)等;
    • 逻辑操作符:不止是and,还有onlynot, 。 如果只是单纯针对屏幕进行响应式的适配,其实是可以写出好几种不同的格式的。
  2. 媒体查询的先后顺序:应该将限制条件严格的放在后面,条件宽松的放在前面
    • 原因: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:理论上代码应该是风格统一的,上面仅做测试。)

加上了媒体查询的迭代后,我们再次测试不同设备下的卡片展示:

动画.gif

至此,我们实现了不同设备的响应式。但是仍有一个问题没有解决,那就是不同宽高比的照片卡片如何处理?

回顾当前的样式组合: Flex布局(两端对齐+溢出换行) + 媒体查询限定相对宽度(vw)

思路:

  1. 最直接的做法:限制卡片高度。这样使得卡片整体风格比较统一,但是卡片内的照片可能会出现因为强行填充而出现变形。
    • 通过object-fit: cover解决。这个CSS属性可以达到最佳最完美的居中自动剪裁图片的功能。object-fit参考链接

    • 折中方法:通过设置相近的最低以及最高的高度,使得不同的照片卡片可以在一定范围内自由拉伸,卡片风格统一照片不变形之间trade off。比如min-height:25vh,max-height:30vh

codepen链接:codepen.io/yyforreal/p…

  1. 在不使图片变形的情况下,显然不同宽高比的照片会撑开卡片使得卡片的高度不统一,那么我们可以设置瀑布流的形式,比如四列卡片,每列卡片向下流动。
    • 如何排列不同列数的卡片是个难题,还需要去了解瀑布流算法。

image.png