在前端面试中我们可能经常会被问到:
- 你常用的宽高等样式单位有哪些?
- 如何实现响应式布局?
- 如何进行移动端的样式适配?
- 1px 问题
- ...
响应式布局和自适应布局的区别:
一、CSS 样式单位
px
px 表示像素
- 在 MDN 中,px 被认为是绝对长度单位,因为像素就是显示器上的一个个小点,每个小点的大小是相同的。
- 有些人则认为 px 属于相对长度单位,因为在移动端存在设备像素比,所以 px 的实际大小是不确定的。
px 的特点:
- 在 PC 端经常使用;
- 直接使用,方便快捷;
- 不能根据屏幕进行自适应;
rpx
rpx(responsive pixel) 是微信小程序中的css尺寸单位,属于相对长度单位。
rpx 的特点:
-
微信小程序使用;
-
可根据屏幕自适应;
微信规定:屏幕宽为 750px,如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备 rpx 换 px(屏幕宽度 / 750) px 换 rpx(750 / 屏幕宽度) iphone 5 1rpx = 0.42px 1px = 2.34rpx iphone 6 1rpx = 0.5px 1px = 2rpx iphone 6 plus 1rpx = 0.552px 1px = 1.81rpx
%
%,相对长度单位,相对于父元素的比例
<div class="box">
<div class="box1">box1</div>
</div>
<style>
.box {
width: 100px;
font-size: 30px;
height: 110px;
border: solid 1px green;
}
.box1 {
width: 40%;
height: 100px;
border: solid 1px red;
font-size: 50%;
}
</style>
em
em 是相对长度单位。在 font-size 中使用是相对于父元素的字体大小(如果没有设置父元素的字体大小,则相对于浏览器的默认字体大小(16px)),在其他属性中使用是相对于自身的字体大小。
其实本质上 em 还是相对于自身的 font-size(1em = 自身的 font-size),因为 font-size 具有继承性。
em 的使用
- 子元素设置了 font-size
.box1 {
font-size: 20px;
width: 1em;
}
- 子元素未设置 font-size,父元素设置了 font-size
.box {
font-size: 30px;
}
.box1 {
width: 1em;
}
- 子元素未设置 font-size,父元素也未设置 font-size
rem
rem(font size of the root element),相对长度单位,相对于根元素(<html>) 的 font-size
1rem = html 根元素设定的 font-size 的 px 值(如果没有设置就是 1rem = 16px)
vw / vh
网页视口尺寸:
window.screen.height // 屏幕高度
window.innerHeight // 网页视口高度
document.body.clientHeight // body 高度
vw(viewport's width)和 vh(viewport's height)是相对长度单位,相对于网页视口的宽高
- vh 网页视口高度的 1%;
- vw 网页视口宽度的 1%;
- vmax 取vh 和 vw 的最大值;
- vmin 取vh 和 vw 的最小值。
⚠️ 注意区分 vw/vh 和 %。
二、 响应式布局的常见方案
media-query + rem
使用媒体查询(media-query),根据不同的屏幕宽度设置根元素 font-size
html { font-size: 20px; }
@media only screen and (min-width:320px) { html { font-size: 17.067px } }
@media only screen and (min-width:360px) { html { font-size: 19.2px } }
@media only screen and (min-width:375px) { html { font-size: 20px } }
@media only screen and (min-width:414px) { html { font-size: 22.08px } }
@media only screen and (min-width:480px) { html { font-size: 25.6px } }
@media only screen and (min-width:640px) { html { font-size: 34.13px } }
@media only screen and (min-width:720px) { html { font-size: 38.918px } }
@media only screen and (min-width:750px) { html { font-size: 40px } }
@media (min-width:800px) { html { font-size: 20px } }
/* ----------- Non-Retina Screens ----------- */
@media screen
and (min-device-width:1200px)
and (max-device-width:1600px)
and (min-device-pixel-ratio:1) {
html {
font-size: 20px;
}
}
/* ----------- Retina Screens ----------- */
@media screen
and (min-device-width:1200px)
and (max-device-width:1600px)
and (min-device-pixel-ratio:2)
and (min-resolution:192dpi) {
html {
font-size: 20px;
}
}
缺点:
- 成本大,不同尺寸需要写多个
@media。
viewport 适配方案
css 单位使用 vw/vh,vw/vh 作为布局单位,从底层根本解决了不同尺寸屏幕的适配问题,因为每个屏幕的百分比是固定的、可预测、可控制的。
如果我们拿到的视觉稿宽度为750px,视觉稿中某个字体的大小为75px,我们的 css 只需这样写:
.box {
font-size: 10vw; // 1vw = 750px * 1% = 7.5px
}
设置 meta 标签
在 html 头部设置 meta 标签如下,让当前 viewport 的宽度等于设备宽度,同时不允许用户手动缩放:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
px 自动转 vw
使用 postcss-px-to-viewport 插件完成 px 单位向视口单位的转换。
三、1px 问题
1px 问题出现的原因
设计稿上的 1px 是指设备像素 1px,但是我们设置 CSS 大小为 1px 的时候,在 dpr(设备像素比)为 2 时,则等于 2px 设备像素,即 1px CSS 像素需要 2 个物理像素进行渲染;在 dpr(设备像素比)为 3 时,则等于 3px 设备像素,即 1px CSS 像素需要 3 个物理像素进行渲染,所以看到的会比实际的粗一些。
dpr(设备像素比),是设备上物理像素和设备独立像素的比例,即物理像素 / dip。可以使用
window.devicePixelRatio查看设备像素比。
dip / dp(设备独立像素)与屏幕密度有关。
解决方案:transform + :before/:after + 媒体查询
// util.scss
@mixin _hairline($x, $y, $type) {
position: absolute;
content: '';
#{$x}: 0;
#{$y}: 0;
#{$type}: 100%;
transform-origin: #{$x} #{$y};
@media (-webkit-min-device-pixel-ratio: 2) {
#{$type}: 200%;
transform: scale(0.5);
}
@media (-webkit-min-device-pixel-ratio: 3) {
#{$type}: 300%;
transform: scale(0.333);
}
}
@mixin hairline-top($color) {
position: relative;
&:before {
height: 0;
border-top: 1px solid $color;
@include _hairline(left, top, width);
}
}
@mixin hairline-bottom($color) {
position: relative;
&:after {
height: 0;
border-bottom: 1px solid $color;
@include _hairline(left, bottom, width);
}
}
@mixin hairline-left($color) {
position: relative;
&:before {
width: 0;
border-left: 1px solid $color;
@include _hairline(left, top, height);
}
}
@mixin hairline-right($color) {
position: relative;
&:after {
width: 0;
border-right: 1px solid $color;
@include _hairline(right, top, height);
}
}
@mixin hairline-around($color: currentColor, $border-radius: 1px) {
position: relative;
&:after {
width: 100%;
border: 1px solid $color;
border-radius: $border-radius;
@media (-webkit-min-device-pixel-ratio: 2) {
width: 200%;
border-radius: $border-radius * 2;
}
@media (-webkit-min-device-pixel-ratio: 3) {
width: 300%;
border-radius: $border-radius * 3;
}
@include _hairline(left, top, height);
bottom: -100%;
}
}
使用:
四、iPhoneX 适配方案
iPhoneX 取消了物理按键,改用小黑条,这就导致出现了一些屏幕适配问题:
解决方案:env() 和 constant()
iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域和边界的距离,有四个预定义的变量:
safe-area-inset-left:安全区域距离左边边界距离;safe-area-inset-right:安全区域距离右边边界距离;safe-area-inset-top:安全区域距离顶部边界距离;safe-area-inset-bottom:安全区域距离底部边界距离;
padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */
欢迎大家参与讨论,有问题随时指出~~