如何解决移动端Retina屏1px像素的问题?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何解决移动端Retina屏1px像素的问题?相关的知识,希望对你有一定的参考价值。

参考技术A

首先我们先聊下Retina屏的概念。
Retina: 一种新型高分辨率的显示标准,又称视网膜显示屏。

在移动端开发中,UI设计稿中设置边框为1px,前端在开发中如果出现border:1px,测试会发现在Retina屏机型中,1px会比较粗,即是经典的移动端1px像素问题。
比如iphone6的屏幕宽度为375px,设计师的视觉稿一般是750px,如果UI的border 1px,而前端实际开发的时候是不能采用1px border的,应该是1px/2。

参考高级前端进阶

移动端 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等元素,也会按照逻辑像素来渲染,整个页面的清晰度会打折;

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

先看看百度糯米的 

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

技术分享

 

再看再看看 阿里去啊 ,其中 hairlines挂到  <html class=‘hairlines‘上>

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

  

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

早期阿里去啊不是这样的 ,是这样写的,兼容性非常好

/*retain 1px border start*/
.retainbt,.retainbb,.retainbl,.retainbr,.retainb { position: relative;position: relative !important}
.retainbt:before,.retainbb:after {pointer-events: none;position: absolute;content: ""; height: 1px; background: rgba(32,35,37,.24);left: 0;right: 0}
.retainbt:before {top: 0}
.retainbb:after {bottom: 0}
.retainbl:before,.retainbr:after {pointer-events: none;position: absolute;content: ""; width: 1px; background: rgba(32,35,37,.24); top: 0; bottom: 0}
.retainbl:before {left: 0}
.retainbr:after {right: 0}
.retainb: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,.24); pointer-events: none}

@media (-webkit-min-device-pixel-ratio:1.5),(min-device-pixel-ratio:1.5),(min-resolution: 144dpi),(min-resolution:1.5dppx) {
.retainbt:before,.retainbb:after {-webkit-transform:scaleY(.5);transform: scaleY(.5) }
.retainbl:before,.retainbr:after {-webkit-transform: scaleX(.5); transform: scaleX(.5) }
.retainb:after { width: 200%; height: 200%;-webkit-transform: scale(.5); transform: scale(.5) }
.retainbt:before,.retainbl:before,.retainb:after {-webkit-transform-origin: 0 0;transform-origin: 0 0}
.retainbb:after,.retainbr:after { -webkit-transform-origin: 100% 100%;transform-origin: 100% 100%}
}

@media (-webkit-device-pixel-ratio:1.5) {
.retainbt:before,.retainbb:after { -webkit-transform: scaleY(.6666); transform: scaleY(.6666) }
.retainbl:before,.retainbr:after {-webkit-transform: scaleX(.6666); transform: scaleX(.6666)}
.retainb:after {width: 150%; height: 150%;-webkit-transform: scale(.6666); transform: scale(.6666) }
}

@media (-webkit-device-pixel-ratio:3) {
.retainbt:before,.retainbb:after { -webkit-transform: scaleY(.3333); transform: scaleY(.3333)}
.retainbl:before,.retainbr:after { -webkit-transform: scaleX(.3333); transform: scaleX(.3333)}
.retainb:after {width: 300%;height: 300%; -webkit-transform: scale(.3333);transform: scale(.3333)}
}

 然后 再看看rem的解决方案

美团的 

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

  我们把美团的 改变一下也可以

美团的 改变一下
 <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>

   最后淘宝的 这段代码有点旧了    https://github.com/amfe/lib-flexible

<script>
!function(N, M) {
    function L() {
        var a = I.getBoundingClientRect().width;
        a / F > 540 && (a = 540 * F);
        var d = a / 10;
        I.style.fontSize = d + "px",
        D.rem = N.rem = d
    }
    var K, J = N.document, I = J.documentElement, H = J.querySelector(‘meta[name="viewport"]‘), G = J.querySelector(‘meta[name="flexible"]‘), F = 0, E = 0, D = M.flexible || (M.flexible = {});
    if (H) {
        console.warn("将根据已有的meta标签来设置缩放比例");
        var C = H.getAttribute("content").match(/initial\-scale=([\d\.]+)/);
        C && (E = parseFloat(C[1]),
        F = parseInt(1 / E))
    } else {
        if (G) {
            var B = G.getAttribute("content");
            if (B) {
                var A = B.match(/initial\-dpr=([\d\.]+)/)
                  , z = B.match(/maximum\-dpr=([\d\.]+)/);
                A && (F = parseFloat(A[1]),
                E = parseFloat((1 / F).toFixed(2))),
                z && (F = parseFloat(z[1]),
                E = parseFloat((1 / F).toFixed(2)))
            }
        }
    }
    if (!F && !E) {
        var y = N.navigator.userAgent
          , x = (!!y.match(/android/gi),
        !!y.match(/iphone/gi))
          , w = x && !!y.match(/OS 9_3/)
          , v = N.devicePixelRatio;
        F = x && !w ? v >= 3 && (!F || F >= 3) ? 3 : v >= 2 && (!F || F >= 2) ? 2 : 1 : 1,
        E = 1 / F
    }
    if (I.setAttribute("data-dpr", F),
    !H) {
        if (H = J.createElement("meta"),
        H.setAttribute("name", "viewport"),
        H.setAttribute("content", "initial-scale=" + E + ", maximum-scale=" + E + ", minimum-scale=" + E + ", user-scalable=no"),
        I.firstElementChild) {
            I.firstElementChild.appendChild(H)
        } else {
            var u = J.createElement("div");
            u.appendChild(H),
            J.write(u.innerHTML)
        }
    }
    N.addEventListener("resize", function() {
        clearTimeout(K),
        K = setTimeout(L, 300)
    }, !1),
    N.addEventListener("pageshow", function(b) {
        b.persisted && (clearTimeout(K),
        K = setTimeout(L, 300))
    }, !1),
    "complete" === J.readyState ? J.body.style.fontSize = 12 * F + "px" : J.addEventListener("DOMContentLoaded", function() {
        J.body.style.fontSize = 12 * F + "px"
    }, !1),
    L(),
    D.dpr = N.dpr = F,
    D.refreshRem = L,
    D.rem2px = function(d) {
        var c = parseFloat(d) * this.rem;
        return "string" == typeof d && d.match(/rem$/) && (c += "px"),
        c
    }
    ,
    D.px2rem = function(d) {
        var c = parseFloat(d) / this.rem;
        return "string" == typeof d && d.match(/px$/) && (c += "rem"),
        c
    }
}(window, window.lib || (window.lib = {}));
</script> 

用rem写1px 维护行方便;   

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

有时候retain下, 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  下就知道,但是截图出来也看不出来问题的(只是示范一下),真机上细看还是明显的;

 

 

技术分享技术分享

 

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

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

技术分享

 

 

喜欢那种就用那种好了;

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

/**
 * 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);

  

 

以上是关于如何解决移动端Retina屏1px像素的问题?的主要内容,如果未能解决你的问题,请参考以下文章

移动端1px显示异常解决方案

移动端debug-5可恶的1px万能实现方案

移动端Retina屏幕1px边框问题

屏幕适配

box-shadow实现移动端(Retina屏)超细边框

H5开发基础移动端1像素边框问题的解决方案