移动端物理像素问题

场景描述

设备像素比就是设备的物理像素与逻辑像素的比值,公式表达如下:dpr = window.devicePixelRatio。retina 屏的手机, dpr 值等于 2 或 3,什么意思呢?就是 css 样式里写的 1px 映射到物理像素上就有 2px 或 3px。

如何破

伪类 + transform + 媒体查询

不使用原来 border 设置边框,改用其伪类来模拟 border,通过媒体查询和 transform 的 scale 大小来改变边框值。

.border-1px {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  padding: 10px;
  text-align: center;
}

.border-1px:after {
  position: absolute;
  top: 0;
  left: 0;
  content: '';
  box-sizing: border-box;
  border: 1px solid red;
  border-radius: 10px;
  transform-origin: 0 0;
}

@media (-webkit-min-device-pixel-ratio: 2) {
  .border-1px::after {
    width: 200%;
    height: 200%;
    transform: scale(0.5);
  }
}

@media (-webkit-min-device-pixel-ratio: 3) {
  .border-1px::after {
    width: 300%;
    height: 300%;
    transform: scale(0.333);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

transforms

  • 优点是所有场景都能满足
  • 缺点是兼容性

小数方案 + 媒体查询

通过媒体查询,给不同 dpr 设置不同单位边框值。

.border-1px {
  text-align: center;
  padding: 10px;
  border-radius: 5px;
  border: 1px solid red;
}
@media screen and (-webkit-min-device-pixel-ratio: 2) {
  .border-1px {
    border: 0.5px solid red;
  }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
  .border-1px {
    border: 0.333px solid red;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

min-device-pixel-ratio

  • IOS8+ 苹果系列都已经支持单位小数方案了
  • IOS7及以下和 Android,小数方案会显示为 0px,可以通过 window.devicePixelRatio 来写 Hack
  • 优点就是简单,代码少
  • 缺点就是兼容性差

viewport + rem

通过 javascript 更改 viewportrem 基准值,这样就可以继续写 px 了。

  • 比如 dpr = 2 时,<meta name="viewport" content="width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
  • 比如 dpr = 3 时,<meta name="viewport" content="width=device-width,initial-scale=0.333, maximum-scale=0.333, minimum-scale=0.333, user-scalable=no">
.border-1px {
  box-sizing: border-box;
  padding: 1rem;
  text-align: center;
  border: 1px solid red;
  border-radius: 0.5rem;
}
1
2
3
4
5
6
7
const viewport = document.querySelector('meta[name=viewport]');
const dpr = window.devicePixelRatio || 1;
const scale = (1 / dpr).toFixed(3);
viewport.setAttribute('content', 'width=device-width,' + 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

const docEl = document.documentElement;
const fontsize = 10 * (docEl.clientWidth / 320) + "px";
docEl.style.fontSize = fontsize;
1
2
3
4
5
6
7
8
  • 优点就是只适用于新项目,一套代码兼容所有布局
  • 缺点就是老项目修改代价大

图片方式

不管通过 border-image 还是 background-image,修改边框颜色需要换图片,圆角还需单独处理且边缘模糊。

插件 postcss-write-svg

项目中使用 PostCSS,然后安装这个插件。

@svg border-1px-base {
  height: 2px;
  @rect {
    fill: var(--color, gray);
    width: 100%;
    height: 50%;
  }
}
.border-1px {
  border: 1px solid transparent;
  border-image: svg(border-1px param(--color red)) 2 2 stretch;
}
1
2
3
4
5
6
7
8
9
10
11
12

组件库

总结

  • 小数方案 + 媒体查询,需要给浏览器一些时间来支持的
  • 老项目,建议采用:伪类 + transform + 媒体查询
  • 新项目,建议采用:viewport + rem
  • 新项目用 ant-design-mobile 组件库
  • 没有圆角可使用:postcss-write-svg