如何解决移动端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像素的问题?的主要内容,如果未能解决你的问题,请参考以下文章