移动端 1px 边框的问题

2021-07-27  本文已影响0人  前端小白的摸爬滚打

物理像素[设备像素] & 逻辑像素[CSS 像素]

背景

拿2倍屏来说,设备的物理像素要实现1像素,而DPR=2,所以css 像素只能是 0.5。一般设计稿是按照750来设计的,它上面的1px是以750来参照的,而我们写css样式是以设备375为参照的,所以我们应该写的0.5px就好了啊! 试过了就知道,iOS 8+系统支持,安卓系统不支持。

在浏览器中无法设置小于 1px 的边框 设置了也不会生效

为什么 1px 变粗了

设计师要求的 1px 是指设备的物理像素 1px,而 CSS 里记录的像素是逻辑像素,它们之间存在一个比例关系,可以用 javascript 中的 window.devicePixelRatio 来获取。当然,比例多少与设备相关。

移动端开发常需要在 html 的 header 里添加如下一句:

  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

设备像素比 = 物理像素 / css 像素

如何解决

核心思想就是设置 1px 大小 然后将 1px 缩小为 0.5px 来展示

  1. 媒体查询利用设备像素比缩放,设置小数像素

IOS8 下已经支持带小数的 px 值, media query 对应 devicePixelRatio 有个查询值-webkit-min-device-pixel-ratio, css 可以写成这样

.border { border: 1px solid #999 }
@media screen and (-webkit-min-device-pixel-ratio: 2) {
    .border { border: 0.5px solid #999 }
}
@media screen and (-webkit-min-device-pixel-ratio: 3) {
    .border { border: 0.333333px solid #999 }
}

【缺点】对设备有要求,小数像素目前兼容性较差。

  1. transform: scale(0.5) 方案 推荐
div {
    height:1px;
    background:#000;
    -webkit-transform: scaleY(0.5);
    -webkit-transform-origin:0 0;
    overflow: hidden;
}
div::after{
    content:'';
    width:100%;
    border-bottom:1px solid #000;
    transform: scaleY(0.5);
}

pixel-border.css 这个工具库就是用来解决移动端 1px 边框的问题,用到的思想和我下面写的一致

.one-pixel-border::before {
  display: block;
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200%;
  height: 200%;
  border: 1px solid red;
  transform: translate(-50%, -50%) scale(0.5, 0.5);
}

pixel-border.css

/**
 * version:0.0.1
 * https://github.com/JofunLiang/pixel-border
 * Licensed under the MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 */
:root {
  --pixel-border-dpr: 1;
}

@media screen and (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
  :root {
    --pixel-border-dpr: 2;
  }
}

@media screen and (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) {
  :root {
    --pixel-border-dpr: 3;
  }
}

@media screen and (-webkit-min-device-pixel-ratio: 4), (min-resolution: 4dppx) {
  :root {
    --pixel-border-dpr: 4;
  }
}

[pixel-border],
[pixel-border=true] {
  position: relative;
  border-width: 0;
  box-sizing: border-box;
}

[pixel-border]::before,
[pixel-border=true]::before {
  --scale: calc(1 / var(--pixel-border-dpr));
  --size: calc(var(--pixel-border-dpr) * 100%);
  content: '';
  pointer-events: none;
  display: block;
  box-sizing: inherit;
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--size);
  height: var(--size);
  border: inherit;
  border-width: 1px;
  border-image: inherit;
  border-radius: inherit;
  transform: translate(-50%, -50%) scale(var(--scale), var(--scale));
}
  1. viewport + rem 方案

该方案是对上述方案的优化,整体思路就是利用 viewport + rem + js 动态的修改页面的缩放比例,实现小于 1 像素的显示。在页面初始化时,在头部引入原始默认状态如下:

<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">

接下来的任务就是 js 的动态修改缩放比 以及 实现 rem 根元素字体大小的设置。

var viewport = document.querySelector("meta[name=viewport]")
if (window.devicePixelRatio == 1) {
    viewport.setAttribute('content', 'width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no')
}
if (window.devicePixelRatio == 2) {
    viewport.setAttribute('content', 'width=device-width, initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no')
}
if (window.devicePixelRatio == 3) {
    viewport.setAttribute('content', 'width=device-width, initial-scale=0.333333333, maximum-scale=0.333333333, minimum-scale=0.333333333, user-scalable=no')
}

var docEl = document.documentElement;
// 320指的是我们以宽度为320的设备作为依据 iPhone 5/SE,这样设置的 1rem 就是 20px 为的就是比较好计算
// 比如说 iphone 6/7/8的宽度是 375 那么这里我们就可以设置为 375 然后我们的 1rem 在这个设备上就是 20px (CSS像素)但是一般给的
// 设计稿都是 物理像素 也就是CSS像素 * 设备像素比(一般是2)
var fontsize = 20 * (docEl.clientWidth / 320) + 'px';
docEl.style.fontSize = fontsize;

【缺点】以为缩放涉及全局的 rem 单位,比较适合新项目,对于老项目可能要涉及到比较多的改动。

  1. 在iOS下,你可以这样写
border:0.5px solid #E5E5E5

可能你会问为什么在3倍屏下,不是0.3333px 这样的?经过我测试,在Chrome上模拟iPhone 8Plus,发现小于0.46px的时候是显示不出来。

上一篇 下一篇

猜你喜欢

热点阅读