像素适配

1px 问题

所谓 1px 问题,即在高清屏下,移动端的 1px 会很粗,如下两个边框对比:

1px 问题示意

window.devicePixelRatio=物理像素 / CSS像素

目前主流的屏幕 DPR=2(iPhone 8),或者 3(iPhone 8 Plus)。拿 2 倍屏来说,设备的物理像素要实现 1 像素,而 DPR=2,所以 css 像素只能是 0.5。一般设计稿是按照 750 来设计的,它上面的 1px 是以 750 来参照的,而我们写 css 样式是以设备 375 为参照的,最简单的办法就是将边框设置为 0.5px。

border: 0.5px solid #e5e5e5;

或者使用边框图片:

border: 1px solid transparent;
border-image: url("./../../image/96.jpg") 2 repeat;

或者使用阴影:

box-shadow: 0 -1px 1px -1px #e5e5e5, //上边线
  1px 0 1px -1px #e5e5e5,
  //右边线
  0 1px 1px -1px #e5e5e5, // 下边线
  -1px 0 1px -1px #e5e5e5; /** 左边线 */

或者使用伪元素:

.setOnePx {
  position: relative;
  &::after {
    position: absolute;
    content: "";
    background-color: #e5e5e5;
    display: block;
    width: 100%;
    height: 1px; /*no*/
    transform: scale(1, 0.5);
    top: 0;
    left: 0;
  }
}

可以看到,将伪元素设置绝对定位,并且和父元素的左上角对齐,将 width 设置 100%,height 设置为 1px,然后进行在 Y 方向缩小 0.5 倍。最后,终极解决办法是使用基于 vw 进行适配:

<html>
  <head>
    <title>1px question</title>
    <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"
    />
    <style>
      html {
        font-size: 1px;
      }
      * {
        padding: 0;
        margin: 0;
      }
      .top_b {
        border-bottom: 1px solid #e5e5e5;
      }

      .a,
      .b {
        box-sizing: border-box;
        margin-top: 1rem;
        padding: 1rem;
        font-size: 1.4rem;
      }

      .a {
        width: 100%;
      }

      .b {
        background: #f5f5f5;
        width: 100%;
      }
    </style>
    <script>
      var viewport = document.querySelector("meta[name=viewport]");
      //下面是根据设备像素设置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.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no"
        );
      }
      var docEl = document.documentElement;
      var fontsize = 32 * (docEl.clientWidth / 750) + "px";
      docEl.style.fontSize = fontsize;
    </script>
  </head>
  <body>
    <div class="top_b a">下面的底边宽度是虚拟 1 像素的</div>
    <div class="b">上面的边框宽度是虚拟 1 像素的</div>
  </body>
</html>

软键盘将页面顶起来、收起未回落问题

Android 手机中,点击 input 框时,键盘弹出,将页面顶起来,导致页面样式错乱。移开焦点时,键盘收起,键盘区域空白,未回落。我们在 app 布局中会有个固定的底部。安卓一些版本中,输入弹窗出来,会将解压 absolute 和 fixed 定位的元素。导致可视区域变小,布局错乱。

解决方案是软键盘将页面顶起来的解决方案,主要是通过监听页面高度变化,强制恢复成弹出前的高度。

// 记录原有的视口高度
const originalHeight =
  document.body.clientHeight || document.documentElement.clientHeight;

window.onresize = function () {
  var resizeHeight =
    document.documentElement.clientHeight || document.body.clientHeight;
  if (resizeHeight < originalHeight) {
    // 恢复内容区域高度
    // const container = document.getElementById("container")
    // 例如 container.style.height = originalHeight;
  }
};

键盘不能回落问题出现在 iOS 12+ 和 wechat 6.7.4+ 中,而在微信 H5 开发中是比较常见的 Bug。兼容原理,1.判断版本类型 2.更改滚动的可视区域

const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d\.]+)/i);
if (!isWechat) return;
const wechatVersion = wechatInfo[1];
const version = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);

// 如果设备类型为 iOS 12+ 和 wechat 6.7.4+,恢复成原来的视口
if (+wechatVersion.replace(/\./g, "") >= 674 && +version[1] >= 12) {
  window.scrollTo(
    0,
    Math.max(document.body.clientHeight, document.documentElement.clientHeight)
  );
}

window.scrollTo(x-coord, y-coord),其中 window.scrollTo(0, clientHeight)恢复成原来的视口

iPhone X 系列安全区域适配问题

头部刘海两侧区域或者底部区域,出现刘海遮挡文字,或者呈现黑底或白底空白区域。iPhone X 以及它以上的系列,都采用刘海屏设计和全面屏手势。头部、底部、侧边都需要做特殊处理。才能适配 iPhone X 的特殊情况。具体操作为:viewport-fit meta 标签设置为 cover,获取所有区域填充。判断设备是否属于 iPhone X,给头部底部增加适配层:

  • auto:此值不影响初始布局视图端口,并且整个 web 页面都是可查看的。
  • contain:视图端口按比例缩放,以适合显示内嵌的最大矩形。
  • cover:视图端口被缩放以填充设备显示。强烈建议使用 safe area inset 变量,以确保重要内容不会出现在显示之外。

设置 viewport-fit 为 cover

<meta
  name="viewport"
  content="width=device-width, initial-scale=1.0, user-scalable=yes, viewport-fit=cover"
/>

增加适配层

/* 适配 iPhone X 顶部填充*/
@supports (top: env(safe-area-inset-top)) {
  body,
  .header {
    padding-top: constant(safe-area-inset-top, 40px);
    padding-top: env(safe-area-inset-top, 40px);
    padding-top: var(safe-area-inset-top, 40px);
  }
}
/* 判断iPhoneX 将 footer 的 padding-bottom 填充到最底部 */
@supports (bottom: env(safe-area-inset-bottom)) {
  body,
  .footer {
    padding-bottom: constant(safe-area-inset-bottom, 20px);
    padding-bottom: env(safe-area-inset-bottom, 20px);
    padding-top: var(safe-area-inset-bottom, 20px);
  }
}

safe-area-inset-top, safe-area-inset-right, safe-area-inset-bottom, safe-area-inset-left safe-area-inset-*由四个定义了视口边缘内矩形的 top, right, bottom 和 left 的环境变量组成,这样可以安全地放入内容,而不会有被非矩形的显示切断的风险。对于矩形视口,例如普通的笔记本电脑显示器,其值等于零。对于非矩形显示器(如圆形表盘,iPhoneX 屏幕),在用户代理设置的四个值形成的矩形内,所有内容均可见。

其中 env() 用法为 env( , ? ),第一个参数为自定义的区域,第二个为备用值。其中 var() 用法为 var( , ? ),作用是在 env() 不生效的情况下,给出一个备用值。

上一页
下一页