zoukankan      html  css  js  c++  java
  • 1像素边框问题

    本文原链接:https://www.jianshu.com/p/fa670b737a29

    移动端1像素边框问题

      对于前端开发者来说,要处理这个问题,必须先补充一个知识点,就是设备的 物理像素[设备像素] & 逻辑像素[CSS像素]

      • 物理像素
        移动设备出厂时,不同设备自带的不同像素,也称硬件像素;
      • 逻辑像素
        即css中记录的像素。

    # 为什么1px变粗了?

      为什么移动端CSS里面写了1px,实际上看起来比1px粗;了解设备物理像素和逻辑像素的同学应该很容易理解,其实这两个px的含义其实是不一样的,UI设计师要求的1px是指设备的物理像素1px,而CSS里记录的像素是逻辑像素,它们之间存在一个比例关系,可以用javascript中的window.devicePixelRatio来获取,也可以用媒体查询的-webkit-min-device-pixel-ratio来获取。当然,比例多少与设备相关。
    在手机上border无法达到我们想要的效果。这是因为devicePixelRatio特性导致,iPhone的devicePixelRatio==2,而border- 1px描述的是设备独立像素,所以,border被放大到物理像素2px显示,在iPhone上就显得较粗。

      移动端开发常需要在html的header里添加如下一句:
      <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
      这句话定义了本页面的viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放。

      你的疑问可能来了,老是看到viewport,除了知道中文名叫视口,到底是啥意思?其实,它就是设备屏幕上能用来显示我们网页内容的那一块区域,具体点就是浏览器或app中的webview用来显示网页的那部分区域,但viewport又不局限于浏览器可视区域的大小,可能大也可能小。体现在用户是否缩放了屏幕。

      心细的同学应该都有感觉,meta标签中常设置user-scalable=no也就是禁止用户缩放,那用户缩放到底会造成什么影响呢? 其实也就是显示上的变化。缩放一倍,CSS像素(逻辑像素)所代表的物理像素也就缩放了一倍,即设备物理像素和设备独立像素的比例增大(减小)了一倍。

      或许你已经明白1px变粗的原因是啥了, viewport的设置和屏幕物理分辨率是按比例而不是相同的. 移动端window对象有个devicePixelRatio属性, 它表示设备物理像素和css像素的比例, 在retina屏的iphone手机上, 这个值为2或3, css里写的1px长度映射到物理像素上就有2px或3px那么长。

    # 如何解决1px问题

    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 }
    }
    

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

    2. 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;
    var fontsize = 10 * (docEl.clientWidth / 320) + 'px';
    docEl.style.fontSize = fontsize;
    

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

    3. 设置 border-image 方案
    .border-image-1px {
        border- 1px 0px;
        -webkit-border-image: url("border.png") 2 0 stretch;
        border-image: url("border.png") 2 0 stretch;
    }
    

    【解释】border-width 指定边框的宽度,可以设定四个值,分别为上右下左border- top right bottom left
    border-image 该例意为:距离图片上方2px(属性值上没有单位)裁剪边框图片作为上边框,下方2px裁剪作为下边框。距离左右0像素裁剪图片即没有边框,以拉伸方式展示

    结合起来就是:在边框图片中,裁剪图片上下方的2个像素宽度作为上下边框,并展示在宽度为1个像素的边框空间里。左右没有边框。 注意这里的1个像素是特殊的,专指物理像素,而平时设定的长宽1px则表示逻辑像素(该观点为个人理解)。

      你可能并没有理解,裁剪边框是什么鬼?为啥还要裁剪?border-image确实是个很难理解的属性,是去w3c看了之后更加蒙圈了。w3c内容提炼的太精致了,但解释根本就不够。因此奉上张鑫旭老师 border-image详解 一文,特别要理解九宫格裁剪法。

    当然,这种方式引入了图片,我们还能将图片装换成base64形式表现

    .border-image-1px {
        border- 1px 0px;
        -webkit-border-image: url("data:image/png;base64,xxx") 2 0 stretch;
        border-image: url('如上');
    }
    

    【缺点】需要制作图片,圆角可能出现模糊

    4. background-image 渐变实现

      除了使用图片外,当然也能使用纯css来实现,百度糯米团就是采用的这种方案。

    .border {
          background-image:linear-gradient(180deg, red, red 50%, transparent 50%),
          linear-gradient(270deg, red, red 50%, transparent 50%),
          linear-gradient(0deg, red, red 50%, transparent 50%),
          linear-gradient(90deg, red, red 50%, transparent 50%);
          background-size: 100% 1px,1px 100% ,100% 1px, 1px 100%;
          background-repeat: no-repeat;
          background-position: top, right top,  bottom, left top;
          padding: 10px;
      }
    

    【思路】将原本1个物理像素的边框大小利用线性渐变分割成几个部分(百分比控制),实现小于1像素效果
    【解释】linear-gradient指定线性渐变,接受大于等于3个参数,第一个为渐变旋转角度,第二个开始为渐变的颜色和到哪个位置(百分比)全部变为该颜色,该例子中,第一句就是,渐变方向旋转180度,即从上往下(默认为0度从下往上),从红色开始渐变,到50%的位置还是红色,再渐变为继承父元素颜色。
    【缺点】因为每个边框都是线性渐变颜色实现,因此无法实现圆角

    5. box-shadow 方案

    利用阴影也可以实现,优点是没有圆角问题,缺点是颜色不好控制

    div {
        -webkit-box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
    }
    

    【理解】来回顾一下box-shadow属性的用法。

    box-shadow: h-shadow v-shadow [blur] [spread] [color] [inset];

    参数分别表示: 水平阴影位置,垂直阴影位置,模糊距离, 阴影尺寸,阴影颜色,将外部阴影改为内部阴影,后四个可选。该例中为何将阴影尺寸设置为负数?设置成-1px 是为了让阴影尺寸稍小于div元素尺寸,这样左右两边的阴影就不会暴露出来,实现只有底部一边有阴影的效果。从而实现分割线效果(单边边框)。

    6. transform: scale(0.5) 方案 - 推荐: 很灵活

      在以上的用法种,无非逃不开一种思想,就是将1px缩小为0.5px来展示,然而。0.5px并不是所有的设备或浏览器都支持,就考虑用媒体查询或viewport将其缩放比例。其实1像素问题的产生基本发生在设置边框或分割线的时候,场景并不覆盖全局样式,因此,直接缩放需要设置的元素,才是我们真正需要的。tranform就能实现这个需求。

    1. 设置height: 1px,根据媒体查询结合transform缩放为相应尺寸。
    div {
        height:1px;
        background:#000;
        -webkit-transform: scaleY(0.5);
        -webkit-transform-origin:0 0;
        overflow: hidden;
    }
    

    2.用::after::befor,设置border-bottom:1px solid #000,然后在缩放-webkit-transform: scaleY(0.5);可以实现两根边线的需求

    div::after{
        content:'';100%;
        border-bottom:1px solid #000;
        transform: scaleY(0.5);
    }
    

    3.用::after设置border:1px solid #000; 200%; height:200%,然后再缩放scaleY(0.5); 优点可以实现圆角,京东就是这么实现的,缺点是按钮添加active比较麻烦。

    .div::after {
        content: '';
         200%;
        height: 200%;
        position: absolute;
        top: 0;
        left: 0;
        border: 1px solid #bfbfbf;
        border-radius: 4px;
        -webkit-transform: scale(0.5,0.5);
        transform: scale(0.5,0.5);
        -webkit-transform-origin: top left;
    }
    
    # 6.5 媒体查询 + transfrom 对方案1的优化
    /* 2倍屏 */
    @media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
        .border-bottom::after {
            -webkit-transform: scaleY(0.5);
            transform: scaleY(0.5);
        }
    }
    
    /* 3倍屏 */
    @media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
        .border-bottom::after {
            -webkit-transform: scaleY(0.33);
            transform: scaleY(0.33);
        }
    }
    

     

  • 相关阅读:
    二进制状态压缩对应 bool 数组中的常用操作
    [Acwing 327] 玉米田 题解
    [CF Contest] Web of Lies 题解
    ArchLinux安装vscode
    ArchLinux安装并且配置fcitx5
    【日常训练】取数问题
    Oracle sql 转 Hive sql一些语法问题
    Oracle中的connect by 转成hive的 lateral view explode
    Hive之分析函数
    数据仓库之拉链表设计
  • 原文地址:https://www.cnblogs.com/leftJS/p/11088643.html
Copyright © 2011-2022 走看看