在 CSS 中有许多距离单位,比如 px | em | rem | %,还有 CSS3 中的 vh | vw 等单位。
那么我们在项目中应该如何使用呢?我们在 pc 端不需要考虑的这么复杂,所以这里我们主要讲讲这些单位在移动端中的使用。
基础单位 px
px 是我们最早接触到的单位了,不过我们在移动端自适应的要求下,使用的频率不是很高;我总结了以下使用的情况:
比较小的图案
比如需要我们画一个 r 为 5px 的圆,如果我们使用 rem 作为单位,我们很快会发现在一些机型上的图案不圆,会呈现椭圆形。这是由于 rem 转 px 会存在精度丢失问题。
所以这个时候我们就需要使用 px 配合 dpr 来实现:
// less
/*@size 建议取双数*/
.circle(@size, @backgroundColor) {
width: @size;
height: @size;
background-color: @backgroundColor;
[data-dpr="1"] & {
width: @size * 0.5;
height: @size * 0.5;
}
[data-dpr="3"] & {
width: @size * 1.5;
height: @size * 1.5;
}
}
1px 细线问题
这个问题下面我会单独做一小节讲,在这里就不累述。
字体大小(基本都是用 rem 作为单位)
一般情况字体的大小我也会使用 rem 作为单位,因为精度丢失我认为在可以接受的范围之内。
相对单位 rem
rem 是 CSS3 新增的一个相对单位(root em),即相对 HTML 根元素的字体大小的值。
rem 应该是自适应使用的最广泛的单位了。
相对单位 em
em 也是一个相对单位,却是相对于当前对象内文本的字体大小。
line-height
一般建议在 line-height 使用 em。因为在需要调整字体大小的时候,只需修改 font-size 的值,而 line-height 已经设置成了相对行高了。
首行缩进两个字符
在存在首行缩进的需求,我也会使用这个单位。
text-indent: 2em
视口单位 vw | vh
vw: 1vw = 视口宽度的 1%
vh: 1vh = 视口高度的 1%
我们知道以 rem 单位设计的弹性布局,是需要在头部加载一段脚本来进行监听分辨率的变化来动态改变根元素字体大小,使得 CSS 与 JS 耦合了在一起。
那么有没有方案解决这个耦合的问题呢?
答案就是视口单位 vw | vh。
以下就是前人给出的使用方案:
$vm_fontsize: 75;
@function rem($px) {
@return ($px / $vm_fontsize ) * 1rem;
}
$vm_design: 750;
html {
font-size: ($vm_fontsize / ($vm_design / 2)) * 100vw;
@media screen and (max-width: 320px) {
font-size: 64px;
}
@media screen and (min-width: 540px) {
font-size: 108px;
}
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
max-width: 540px;
min-width: 320px;
}
1px 方案
做过移动端需求的前端肯定是避免不了处理 1px 细线问题,这个问题的原因就是 UI 对页面美观度的要求越来越高(不要和我说这是 retina 屏的问题)。
据查阅资料所知好像没有什么兼容性特别好的方案,这里我只是提供三种相对较好的方案。
使用伪类 + transform
.border_bottom {
overflow: hidden;
position: relative;
border: none!important;
}
.border_bottom:after {
content: ".";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 1px;
background-color: #d4d6d7;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
当然这个方案在一些版本较低的机型也是会出现粗细不均、细线消失断裂的兼容性问题。不过现在在已经 2019 年了,版本较低的机型也淘汰的差不多了。
使用 box-shadow 模拟
.border_bottom {
box-shadow: inset 0px -1px 1px -1px #d4d6d7;
}
这个方案基本可以满足所有场景,不过有个缺点也就是颜色会变浅。
第三种方案
这种方案对 dpr 做了不同的处理,可谓更加精细。
.min-device-pixel-ratio(@scale2, @scale3) {
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
transform: @scale2;
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
transform: @scale3;
}
}
.border-1px(@color: #DDD, @radius: 2PX, @style: solid) {
&::before {
content: "";
pointer-events: none;
display: block;
position: absolute;
left: 0;
top: 0;
transform-origin: 0 0;
border: 1PX @style @color;
border-radius: @radius;
box-sizing: border-box;
width: 100%;
height: 100%;
@media screen and (min-device-pixel-ratio: 2), (-webkit-min-device-pixel-ratio: 2) {
width: 200%;
height: 200%;
border-radius: @radius * 2;
transform: scale(.5);
}
@media screen and (min-device-pixel-ratio: 3), (-webkit-min-device-pixel-ratio: 3) {
width: 300%;
height: 300%;
border-radius: @radius * 3;
transform: scale(.33);
}
}
}
.border-top-1px(@color: #DDD, @style: solid) {
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 100%;
border-top: 1Px @style @color;
transform-origin: 0 0;
.min-device-pixel-ratio(scaleY(.5), scaleY(.33));
}
}