• 解决一像素问题的另一种解决方案


    https://www.cnblogs.com/lunarorbitx/p/5287309.html   

    现在的PM和UI总以看app的眼光看html5, html页面要做的专业美观,而且必须很精细.

    去年的时候UI就告诉我h5上的边框线太粗,把整站都给拉low了. 当时工期紧就没太在意1px粗细, 好在那个版本没上线就迭代掉了,后面的版本针对这个问题做了些尝试, 这里总结下1px细线的处理方法

    移动端1px变粗的原因

    为什么移动端css里面写了1px, 实际看起来比1px粗. 其实原因很好理解:这2个’px’的含义是不一样的. 移动端html的header总会有一句

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

    这句话定义了本页面的viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放. viewport通俗的讲是浏览器上可用来显示页面的区域, 这个区域是可能比屏幕大的.

    根据这篇文章http://www.cnblogs.com/2050/p/3877280.html的分析, 手机存在一个能完美适配的理想viewport, 分辨率相差很大的手机的理想viewport的宽度可能是一样的, 这样做的目的是为了保证同样的css在不同屏幕下的显示效果是一致的, 上面的meta实际上是设置了ideal viewport的宽度.

    以实际举例: iphone3和iphone4的屏幕宽度分别是320px,640px, 但是它们的ideal viewport的宽度都是320px, 设置了设备宽度后, 320px宽的元素都能100%的填充满屏幕宽. 不同手机的ideal viewport宽度是不一样的, 常见的有320px, 360px, 384px. iphone系列的这个值在6之前都是320px, 控制viewport的好处就在于一套css可以适配多个机型.

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

    1px解决方案

    1.用小数来写px值

    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 }
    }
    复制代码

    如果使用less/sass的话只是加了1句mixin

    缺点: 安卓与低版本IOS不适用, 这个或许是未来的标准写法, 现在不做指望

    2.border-image

    0053594940-3_thumb

    这样的1张6X6的图片, 9宫格等分填充border-image, 这样元素的4个边框宽度都只有1px

    @media screen and (-webkit-min-device-pixel-ratio: 2){ 
        .border{ 
            border: 1px solid transparent;
            border-image: url(border.gif) 2 repeat;
        }
    }

    图片可以用gif, png, base64多种格式, 以上是上下左右四条边框的写法, 需要单一边框只要定义单一边框的border, 代码比较直观.

    border-image兼容性:

    border-images-brower_thumb[1]

    缺点: 对于圆角样式, 将图片放大修改成圆角也能满足需求, 但这样无形中增加了border的宽度

             存在多种边框颜色或者更改的时候麻烦

    3. background渐变

    背景渐变, 渐变在透明色和边框色中间分割, frozenUI用的就是这种方法, 借用它的上边框写法:

    复制代码

    @media screen and (-webkit-min-device-pixel-ratio: 2){
        .ui-border-t {
            background-position: left top;
            background-image: -webkit-gradient(linear,left bottom,left top,color-stop(0.5,transparent),color-stop(0.5,#e0e0e0),to(#e0e0e0));
        }
    }

    复制代码

    这样更改颜色比border-image方便, 兼容性

    gradient-browser_thumb[1]

    缺点: 代码量大, 而且需要针对不同边框结构, frozenUI就定义9种基本样式

              而且这只是背景, 这样做出来的边框实际是在原本的border空间内部的, 如果元素背景色有变化的样式, 边框线也会消失.

              最后不能适应圆角样式

    4. :before, :after与transform

    之前说的frozenUI的圆角边框就是采用这种方式, 构建1个伪元素, 将它的长宽放大到2倍, 边框宽度设置为1px, 再以transform缩放到50%.

    复制代码
    .radius-border{
        position: relative;
    }
    @media screen and (-webkit-min-device-pixel-ratio: 2){
        .radius-border:before{
            content: "";
            pointer-events: none; /* 防止点击触发 */
            box-sizing: border-box;
            position: absolute;
             200%;
            height: 200%;
            left: 0;
            top: 0;
            border-radius: 8px;
            border:1px solid #999;
            -webkit-transform(scale(0.5));
            -webkit-transform-origin: 0 0;
            transform(scale(0.5));
            transform-origin: 0 0;
        }
    }
    复制代码

    需要注意<input type="button">是没有:before, :after伪元素的

    优点: 其实不止是圆角, 其他的边框也可以这样做出来

    缺点: 代码量也很大, 占据了伪元素, 容易引起冲突

    5. flexible.js

    这是淘宝移动端采取的方案, github的地址:https://github.com/amfe/lib-flexible. 前面已经说过1px变粗的原因就在于一刀切的设置viewport宽度, 如果能把viewport宽度设置为实际的设备物理宽度, css里的1px不就等于实际1px长了么. flexible.js就是这样干的.

    <meta name=”viewport”>里面的scale值指的是对ideal viewport的缩放, flexible.js检测到IOS机型, 会算出scale = 1/devicePixelRatio, 然后设置viewport

    metaEl = doc.createElement('meta');
    metaEl.setAttribute('name', 'viewport');
    metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');

    devicePixelRatio=2时输出meta如下, 这样viewport与ideal viewport的比是0.5, 也就与设备物理像素一致

    <meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">

    另外html元素上的font-size会被设置为屏幕宽的1/10, 这样css可以以rem为基础长度单位进行改写, 比如rem是28px, 原先的7px就是0.25rem. border的宽度能直接写1px.

    复制代码
    function refreshRem() {
        var width = docEl.getBoundingClientRect().width;
        if (width / dpr > 540) { //大于540px可以不认为是手机屏
            width = 540 * dpr;
        }
        var rem = width / 10; 
        docEl.style.fontSize = rem + 'px';
        flexible.rem = win.rem = rem;
    }
    复制代码

    px和rem相互转换的计算方法会暴露在window.lib.flexible中. 这样可以为less/sass编写宏方法. 具体的css改写方法参照大漠的文章http://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html

    项目中特别指出了为了防止字体模糊, 出现奇数字号的字体, 字体的实际单位还是要以px为单位.

    缺点: 不适用安卓, flexible内部做了检测 非iOS机型还是采用传统的scale=1.0, 原因在于安卓手机不一定有devicePixelRatio属性, 就算有也不一定能响应scale小于1的viewport缩放设置, 例如我的手机设置了scale=0.33333333, 显示的结果也与scale=1无异.

    综合使用

    对于IOS, flexible.js处理的已经很好了, 对于Android,方法2,3,4结合起来大体可以满足要求. flexible.js虽然不适用于安卓, 但它里面的这一段代码可以用来做对安卓机的部署.

    复制代码
    if (!dpr && !scale) {
        var isAndroid = win.navigator.appVersion.match(/android/gi);
        var isIPhone = win.navigator.appVersion.match(/iphone/gi);
        var devicePixelRatio = win.devicePixelRatio;
        if (isIPhone) {
            // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
            if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
                dpr = 3;
            } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
                dpr = 2;
            } else {
                dpr = 1;
            }
        } else {
            // 其他设备下,仍旧使用1倍的方案
            dpr = 1;
        }
        scale = 1 / dpr;
    }
    复制代码

    这里对安卓做检测, 如果是安卓, js动态加载css.

    var link = document.createElement('link');
    link.setAttribute("rel","stylesheet");
    link.setAttribute("type","text/css");
    link.setAttribute("href",".......Android.css");
    document.querySelector('head').appendChild(link);
    1. <span style="font-size:18px;"><html>  
    2.   
    3.     <head>  
    4.         <title>1px question</title>  
    5.         <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">  
    6.         <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">       
    7.         <style>  
    8.             html {  
    9.                 font-size: 1px;  
    10.             }             
    11.             * {  
    12.                 padding: 0;  
    13.                 margin: 0;  
    14.             }  
    15.               
    16.             .bds_b {  
    17.                 border-bottom: 1px solid #ccc;  
    18.             }  
    19.               
    20.             .a,  
    21.             .b {  
    22.                 margin-top: 1rem;  
    23.                 padding: 1rem;                
    24.                 font-size: 1.4rem;  
    25.             }  
    26.               
    27.             .a {  
    28.                  30rem;  
    29.             }  
    30.               
    31.             .b {  
    32.                 background: #f5f5f5;  
    33.                  20rem;  
    34.             }  
    35.         </style>  
    36.         <script>  
    37.           
    38.             var viewport = document.querySelector("meta[name=viewport]");  
    39.             //下面是根据设备像素设置viewport  
    40.             if (window.devicePixelRatio == 1) {  
    41.                 viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no');  
    42.             }  
    43.             if (window.devicePixelRatio == 2) {  
    44.                 viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no');  
    45.             }  
    46.             if (window.devicePixelRatio == 3) {  
    47.                 viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no');  
    48.             }  
    49.             var docEl = document.documentElement;  
    50.             var fontsize = 10 * (docEl.clientWidth / 320) + 'px';  
    51.             docEl.style.fontSize = fontsize;  
    52.               
    53.         </script>  
    54.     </head>  
    55.   
    56.     <body>  
    57.         <div class="bds_b a">下面的底边宽度是虚拟1像素的</div>  
    58.         <div class="b">上面的边框宽度是虚拟1像素的</div>  
    59.     </body>  
    60.   
    61. </html></span>
  • 相关阅读:
    疑问:遍历器 Iterator 接口和生成器函数 Generator 之间的关系?
    2020-03-05:JSX、透传、函数式组件、createElement渲染函数
    疑问:现代浏览器是如何组织模块的?
    2020-03-04:各种遍历方法的区别和 Iterator 遍历器
    2020-03-04:vue-styled-components
    操作系统学习笔记(十一)-- 文件系统管理(下)
    操作系统学习笔记(十)-- 文件系统管理(上)
    操作系统学习笔记(九)-- 虚拟内存管理
    操作系统学习笔记(八)-- 内存管理
    操作系统学习笔记(七)-- 死锁
  • 原文地址:https://www.cnblogs.com/chaoyuehedy/p/9178807.html
Copyright © 2020-2023  润新知