移动端 Retina屏 各大主流网站1px的解决方案


Retina屏的移动设备如何实现真正1px的线?

在retina屏下面,如果你写了这样的meta <meta name="viewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
你将永远无法写出1px宽度的东西,除此之外,inline的SVG等元素,也会按照逻辑像素来渲染,整个页面的清晰度会打折;

先看看  “诸子百家 ”  是如何实现的;

先看看百度糯米的 

1
2
3
4
5
6
7
8
9
10
11
12
@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-device-pixel-ratio:2) {
.normal-goods .good-content {
border: none;
background-image: -webkit-linear-gradient(90deg,#e0e0e0,#e0e0e0 50%,transparent 50%);
background-image: -moz-linear-gradient(90deg,#e0e0e0,#e0e0e0 50%,transparent 50%);
background-image: -o-linear-gradient(90deg,#e0e0e0,#e0e0e0 50%,transparent 50%);
background-image: linear-gradient(0,#e0e0e0,#e0e0e0 50%,transparent 50%);
background-size: 100% 1px;
background-repeat: no-repeat;
background-position: bottom
}
}

  

 再看看  大众点评的

复制代码
.index-rec .home-tuan-list .cnt {
    padding: 7px 10px 10px 0;
    display: box;
    display: -webkit-box;
    display: -ms-flexbox;
    height: 78px;
    background-image: url(//www.dpfile.com/mod/app-m-style/1.7.2/css/img/repeat-x.png);
    background-repeat: repeat-x;
    background-position: 0 bottom;
    background-size: auto 1px
}
复制代码

 

微信WeUI的

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
.weui_grid:before {
    content: " ";
    position: absolute;
    right: 0;
    top: 0;
    width: 1px;
    height: 100%;
    border-right: 1px solid #D9D9D9;
    color: #D9D9D9;
    -webkit-transform-origin: 0 100%;
    transform-origin: 0 100%;
    -webkit-transform: scaleX(0.5);
    transform: scaleX(0.5);
    right: -1px;
}
 
.weui_grid:after {
    content: " ";
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    border-bottom: 1px solid #D9D9D9;
    color: #D9D9D9;
    -webkit-transform-origin: 0 100%;
    transform-origin: 0 100%;
    -webkit-transform: scaleY(0.5);
    transform: scaleY(0.5);
}

  

再看再看看 阿里去啊 ,利用.5px  其中 hairlines挂到  <html class='hairlines'上>   ios8以上支持  .5px

1
2
3
4
5
6
7
8
9
<script>
    if (/iP(hone|od|ad)/.test(navigator.userAgent)) {  //  就是放到html根节点上的   ios8现在普及率高了,可以省略
        var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/), version = parseInt(
                v[1], 10);
        if (version >= 8) {
            document.documentElement.classList.add('hairlines')
        }
    };
</script>

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.r1bt {
    border-top: 1px solid rgba(32,35,37,.15)
}
 
.r1bb {
    border-bottom: 1px solid rgba(32,35,37,.15)
}
 
.r1bl {
    border-left: 1px solid rgba(32,35,37,.15)
}
 
.r1br {
    border-right: 1px solid rgba(32,35,37,.15)
}
 
.r1b {
    border: 1px solid rgba(32,35,37,.15)
}
 
.hairlines .r1bt,.hairlines .r1bb,.hairlines .r1bl,.hairlines .r1br,.hairlines .r1b {
    border-width: .5px!important
}

阿里去啊 另一种 transform: scale(x)  缩放 ,兼容性适用性非常好 推荐使用

 

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
/*Retian 1px border start */
.retinabt,.retinabb,.retinabl,.retinabr,.retinab{position:relative;}
.retinabt:before,.retinabb:after{pointer-events:none;position:absolute;content:"";height:1px;background:rgba(32,35,37,.14);left:0;right:0;z-index:26;}
.retinabt:before{top:0;z-index:26;}
.retinabb:after{bottom:0;z-index:26;}
.retinabl:before,.retinabr:after{pointer-events:none;position:absolute;content:"";width:1px;background:rgba(32,35,37,.14);top:0;bottom:0}
.retinabl:before{left:0;z-index:26;}
.retinabr:after{right:0;z-index:26;}
.retinab:after{position:absolute;content:"";top:0;left:0;-webkit-box-sizing:border-box;box-sizing:border-box;width:100%;height:100%;border:1px solid rgba(32,35,37,.14);pointer-events:none;z-index:26;}
@media (-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution:144dpi),(min-resolution:1.5dppx){
.retinabt:before,.retinabb:after{-webkit-transform:scaleY(.5);transform:scaleY(.5) }
.retinabl:before,.retinabr:after{-webkit-transform:scaleX(.5);transform:scaleX(.5) }
.retinab:after{width:200%;height:200%;-webkit-transform:scale(.5);transform:scale(.5) }
.retinabt:before,.retinabl:before,.retinab:after{-webkit-transform-origin:0 0;transform-origin:0 0}
.retinabb:after,.retinabr:after{-webkit-transform-origin:100% 100%;transform-origin:100% 100%}
}
@media (-webkit-device-pixel-ratio:1.5){
.retinabt:before,.retinabb:after{-webkit-transform:scaleY(.6666);transform:scaleY(.6666) }
.retinabl:before,.retinabr:after{-webkit-transform:scaleX(.6666);transform:scaleX(.6666)}
.retinab:after{width:150%;height:150%;-webkit-transform:scale(.6666);transform:scale(.6666) }
}
@media (-webkit-device-pixel-ratio:3){
.retinabt:before,.retinabb:after{-webkit-transform:scaleY(.3333);transform:scaleY(.3333)}
.retinabl:before,.retinabr:after{-webkit-transform:scaleX(.3333);transform:scaleX(.3333)}
.retinab:after{width:300%;height:300%;-webkit-transform:scale(.3333);transform:scale(.3333)}
}
@media (-webkit-min-device-pixel-ratio:4),(min-device-pixel-ratio:4){
.retinabt:before,.retinabb:after{-webkit-transform:scaleY(.25);transform:scaleY(.25)}
.retinabl:before,.retinabr:after{-webkit-transform:scaleX(.25);transform:scaleX(.25)}
.retinab:after{width:400%;height:400%;-webkit-transform:scale(.25);transform:scale(.25)}
}
/*Retina 1px border end */

  

 

然后 再看看rem的解决方案

美团的   (使用rem,但是不随屏幕大小而计算根节点html的font-size,适合列表较多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript">
        //根据屏幕大小及dpi调整缩放和大小
        (function() {
            var scale = 1.0;
            var ratio = 1;
            if (window.devicePixelRatio >= 2) {
                scale *= 0.5;
                ratio *= 2;
            }
            var text = '<meta name="viewport" content="initial-scale=' + scale + ', maximum-scale=' + scale +', minimum-scale=' + scale + ', width=device-width, user-scalable=no" />';
            document.write(text);
            document.documentElement.style.fontSize = 50*ratio + "px";
        })();
    </script>

  

我们把美团的 拷贝过来使用,发现 安卓自带的浏览器(app内嵌h5不得不考虑)有的 不兼容 开始整体字体放大,应该是没有正确获取设备的实际宽度,(手头没有那么多安卓测试手机,主要是自带浏览器出现问题),不知到美团怎么处理的,我想到的用这个   target-densittydpi=device-dpi   hack下;是可以的 或者加个 计时器 延迟 50毫秒 获取设备的正确实际宽度;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<meta name="viewport" content="target-densitydpi=device-dpi">  <!--安卓自带的 device-width 先不加 否则iphone 随进线条出现问题 -->
<script> 
+function(win,doc,undefined) {//根据屏幕大小及dpi调整缩放和大小
    var scale = 1.0,ratio = 1,dc=doc,viewporttexts='';
    if (win.devicePixelRatio && devicePixelRatio >= 1.5) {
        ratio = devicePixelRatio;
        scale = scale/(devicePixelRatio);  
    }
    //var texts = '<meta  name="viewport" content="initial-scale=' + scale + ', maximum-scale=' + scale +', minimum-scale=' + scale + ', width=device-width, user-scalable=no" />';
    // dc.write(texts);
      viewporttexts = ' width=device-width, initial-scale=' + scale + ', maximum-scale=' + scale +', minimum-scale=' + scale + ',user-scalable=no';
      doc.querySelector('meta[name="viewport"]').setAttribute("content",viewporttexts);
       
       console.log('111');
      dc.documentElement.style.fontSize =doc.getElementsByTagName("html")[0].style.fontSize=Math.ceil(50*ratio) + "px";
}(window,document);
 </script>

最后淘宝的 等等(大部分rem都是 随屏幕大小而计算 根节点大小 )  这段代码有点旧了    https://github.com/amfe/lib-flexible

 

 

  

 

用rem写1px 维护行方便;图片高度可以用rem固定高度,防止加载时出现高度自动网速加载慢导致的明显塌陷;   

缺点: 动态控制  viewport  retina下,无论美团还是淘宝用 rem始终还有许多细小的问题;在ios上浏览器打开仔细看还是看的出的,安卓上没看出来;

有时候retina下, viewport  缩放动态控制字体大小;<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">  竖线或者奇数偶数行横线 或者动态添加显示的元素   之后的1px线条,有的1.1px  或者1.2px等等...拿手机仔细看下,观察iphone5 以及iphone6  safari以及其他浏览器对比下就知道,以下是截图出来问题的(只是示范一下 红色箭头的 border 线条 ),同样都是像素比 ratio=2  真机上细看还是明显的;

先看看iphone6 的截图 文字 ktv右侧的1px border正常;

再看看 下面 iphone5s的截图 刷选左侧的1px正常;     ip6第一条正常;ip5s最后一条正常;  

 

上面iphone5s 截图 美团KTV 全城 默认排序 刷选的 分割线 ;iphone5s 刷选的那条是正常的鹅;前面3条1px多了点;ip6上则不是;   

有的 竖线始终 感觉 宽度是 不是1px;宽了一点点;首页美食类目进去;每个店铺边框  偶尔几条线条是1px多了一点点;

下面是iphone6 plus 的截图 100状态下 ; iphone6 plus 的截图还是看的出来 ;比较明显  奇数偶数行线条 不一致的 问题;;

淘宝网 iophone5s  横向 屏幕截图

 

喜欢那种就用那种好了;

顺便附个H5  Canvas  Retina屏幕处理的1px的函数

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/**
 * HiDPI Canvas Polyfill (1.0.9)
 *
 * Author: Jonathan D. Johnson (http://jondavidjohn.com)
 * Homepage: https://github.com/jondavidjohn/hidpi-canvas-polyfill
 * Issue Tracker: https://github.com/jondavidjohn/hidpi-canvas-polyfill/issues
 * License: Apache 2.0
*/
;(function(prototype) {
 
    var pixelRatio = (function(context) {
            var backingStore = context.backingStorePixelRatio ||
                        context.webkitBackingStorePixelRatio ||
                        context.mozBackingStorePixelRatio ||
                        context.msBackingStorePixelRatio ||
                        context.oBackingStorePixelRatio ||
                        context.backingStorePixelRatio || 1;
 
            return (window.devicePixelRatio || 1) / backingStore;
        })(prototype),
 
        forEach = function(obj, func) {
            for (var p in obj) {
                if (obj.hasOwnProperty(p)) {
                    func(obj[p], p);
                }
            }
        },
 
        ratioArgs = {
            'fillRect': 'all',
            'clearRect': 'all',
            'strokeRect': 'all',
            'moveTo': 'all',
            'lineTo': 'all',
            'arc': [0,1,2],
            'arcTo': 'all',
            'bezierCurveTo': 'all',
            'isPointinPath': 'all',
            'isPointinStroke': 'all',
            'quadraticCurveTo': 'all',
            'rect': 'all',
            'translate': 'all',
            'createRadialGradient': 'all',
            'createLinearGradient': 'all'
        };
 
    if (pixelRatio === 1) return;
 
    forEach(ratioArgs, function(value, key) {
        prototype[key] = (function(_super) {
            return function() {
                var i, len,
                    args = Array.prototype.slice.call(arguments);
 
                if (value === 'all') {
                    args = args.map(function(a) {
                        return a * pixelRatio;
                    });
                }
                else if (Array.isArray(value)) {
                    for (i = 0, len = value.length; i < len; i++) {
                        args[value[i]] *= pixelRatio;
                    }
                }
 
                return _super.apply(this, args);
            };
        })(prototype[key]);
    });
 
     // Stroke lineWidth adjustment
    prototype.stroke = (function(_super) {
        return function() {
            this.lineWidth *= pixelRatio;
            _super.apply(this, arguments);
            this.lineWidth /= pixelRatio;
        };
    })(prototype.stroke);
 
    // Text
    //
    prototype.fillText = (function(_super) {
        return function() {
            var args = Array.prototype.slice.call(arguments);
 
            args[1] *= pixelRatio; // x
            args[2] *= pixelRatio; // y
 
            this.font = this.font.replace(
                /(\d+)(px|em|rem|pt)/g,
                function(w, m, u) {
                    return (m * pixelRatio) + u;
                }
            );
 
            _super.apply(this, args);
 
            this.font = this.font.replace(
                /(\d+)(px|em|rem|pt)/g,
                function(w, m, u) {
                    return (m / pixelRatio) + u;
                }
            );
        };
    })(prototype.fillText);
 
    prototype.strokeText = (function(_super) {
        return function() {
            var args = Array.prototype.slice.call(arguments);
 
            args[1] *= pixelRatio; // x
            args[2] *= pixelRatio; // y
 
            this.font = this.font.replace(
                /(\d+)(px|em|rem|pt)/g,
                function(w, m, u) {
                    return (m * pixelRatio) + u;
                }
            );
 
            _super.apply(this, args);
 
            this.font = this.font.replace(
                /(\d+)(px|em|rem|pt)/g,
                function(w, m, u) {
                    return (m / pixelRatio) + u;
                }
            );
        };
    })(prototype.strokeText);
})(CanvasRenderingContext2D.prototype);
;(function(prototype) {
    prototype.getContext = (function(_super) {
        return function(type) {
            var backingStore, ratio,
                context = _super.call(this, type);
 
            if (type === '2d') {
 
                backingStore = context.backingStorePixelRatio ||
                            context.webkitBackingStorePixelRatio ||
                            context.mozBackingStorePixelRatio ||
                            context.msBackingStorePixelRatio ||
                            context.oBackingStorePixelRatio ||
                            context.backingStorePixelRatio || 1;
 
                ratio = (window.devicePixelRatio || 1) / backingStore;
 
                if (ratio > 1) {
                    this.style.height = this.height + 'px';
                    this.style.width = this.width + 'px';
                    this.width *= ratio;
                    this.height *= ratio;
                }
            }
 
            return context;
        };
    })(prototype.getContext);
})(HTMLCanvasElement.prototype);

  本文地址 http://www.cnblogs.com/surfaces/p/4324044.html

 

posted @   surfaces  阅读(10178)  评论(2编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示