移动端Retina屏boder 1px显示为2px或3px的解决方法

时间: 2018-02-24阅读: 441标签: px

我们在开发移动端web项目时经常遇到设置border:1px,但是显示的边框却为2px或是3px粗细,这是因为设备像素比devicePixelRatio为2或3引起的。

何为“设备像素比devicePixelRatio”

设备上物理像素和设备独立像素(device-independent pixels (dips))的比例。

公式表示就是:window.devicePixelRatio = 物理像素 / dips   dip或dp,(device independent pixels,设备独立像素)与屏幕密度有关。dip可以用来辅助区分视网膜设备还是非视网膜设备。

所有非视网膜屏幕的iphone在垂直的时候,宽度为320物理像素。当你使用<meta name="viewport" content="width=device-width">的时候,会设置视窗布局宽度(不同于视觉区域宽度,不放大显示情况下,两者大小一致,见下图)为320px, 于是,页面很自然地覆盖在屏幕上。这样,非视网膜屏幕的iphone上,屏幕物理像素320像素,独立像素也是320像素,因此,window.devicePixelRatio等于1.

而对于视网膜屏幕的iphone,如iphone4s, 纵向显示的时候,屏幕物理像素640像素。同样,当用户设置<meta name="viewport" content="width=device-width">的时候,其视区宽度并不是640像素,而是320像素,这是为了有更好的阅读体验 – 更合适的文字大小。
这样,在视网膜屏幕的iphone上,屏幕物理像素640像素,独立像素还是320像素,因此,window.devicePixelRatio等于2.

每个像素点实际上有4倍的普通像素点,如下示意(© smashingmagazine):

1个CSS像素点实际上有4个位图像素点,1个分成4个,显然不够分啊,只能颜色近似选取,于是,图片感觉就是模糊的(© smashingmagazine)!。这就是为什么使用两倍图。

视网膜屏幕下图片就显示OK了(非视网膜屏幕图片被压缩-减少像素取样——资源浪费!)

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

微信WeUI的实现:

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

阿里的实现:

js:

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

css:

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

阿里的另一种实现:

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

美团的实现:

<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毫秒 获取设备的正确实际宽度;

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

淘宝的实现:

;(function(win, lib) {var doc = win.document;var docEl = doc.documentElement;var metaEl = doc.querySelector('meta[name="viewport"]');var flexibleEl = doc.querySelector('meta[name="flexible"]');var dpr = 0;var scale = 0;var tid;var flexible = lib.flexible || (lib.flexible = {});if (metaEl) {console.warn('将根据已有的meta标签来设置缩放比例');var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);if (match) {scale = parseFloat(match[1]);dpr = parseInt(1 / scale);}} else if (flexibleEl) {var content = flexibleEl.getAttribute('content');if (content) {var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);if (initialDpr) {dpr = parseFloat(initialDpr[1]);scale = parseFloat((1 / dpr).toFixed(2));   }if (maximumDpr) {dpr = parseFloat(maximumDpr[1]);scale = parseFloat((1 / dpr).toFixed(2));   }}}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;}docEl.setAttribute('data-dpr', dpr);if (!metaEl) {metaEl = doc.createElement('meta');metaEl.setAttribute('name', 'viewport');metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');if (docEl.firstElementChild) {docEl.firstElementChild.appendChild(metaEl);} else {var wrap = doc.createElement('div');wrap.appendChild(metaEl);doc.write(wrap.innerHTML);}}function refreshRem(){var width = docEl.getBoundingClientRect().width;if (width / dpr > 540) {width = 540 * dpr;}var rem = width / 10;docEl.style.fontSize = rem + 'px';flexible.rem = win.rem = rem;}win.addEventListener('resize', function() {clearTimeout(tid);tid = setTimeout(refreshRem, 300);}, false);win.addEventListener('pageshow', function(e) {if (e.persisted) {clearTimeout(tid);tid = setTimeout(refreshRem, 300);}}, false);if (doc.readyState === 'complete') {doc.body.style.fontSize = 12 * dpr + 'px';} else {doc.addEventListener('DOMContentLoaded', function(e) {doc.body.style.fontSize = 12 * dpr + 'px';}, false);}refreshRem();flexible.dpr = win.dpr = dpr;flexible.refreshRem = refreshRem;flexible.rem2px = function(d) {var val = parseFloat(d) * this.rem;if (typeof d === 'string' && d.match(/rem$/)) {val += 'px';}return val;}flexible.px2rem = function(d) {var val = parseFloat(d) / this.rem;if (typeof d === 'string' && d.match(/px$/)) {val += 'rem';}return val;}})(window, window['lib'] || (window['lib'] = {}));

用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  真机上细看还是明显的。

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 adjustmentprototype.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; // xargs[2] *= pixelRatio; // ythis.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; // xargs[2] *= pixelRatio; // ythis.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);

viewport + REM的方式来实现:

在devicePixelRatio = 2 时,输出viewport

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

在devicePixelRatio = 3 时,输出viewport

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

代码:

<script type="text/javascript">(function() {var value=1;if (window.devicePixelRatio == 2) {value=0.5; }else if(window.devicePixelRatio == 3){value=0.333333333333333; }var text = '<meta name="viewport" content="initial-scale=' + value + ', maximum-scale=' + value +', minimum-scale=' + value + ', width=device-width, user-scalable=no" />';document.write(text);})();
</script>

同时通过设置对应viewport的rem基准值,这种方式就可以像以前一样轻松愉快的写1px了。

需要使用rem,我们需要设置根的font-size。具体请查考:移动端web app要使用rem实现自适应布局:font-size的响应式

移动端Retina屏boder 1px显示为2px或3px的解决方法相关推荐

  1. html在苹果手机上显示不出来,苹果手机下拉菜单显示不出来也下不来的解决方法...

    苹果手机下拉菜单显示不出来也下不来的解决方法 iOS8下拉菜单显示不出来也下不来怎么办?如何解决?不管是 iOS7 系统还是 iOS8 系统,在使用过程中,都有机率遇到下拉通知中心或控制中心的菜单出不 ...

  2. php页面花屏,window_win7电脑突然花屏死机的几种原因和快速解决方法(推荐),win7系统电脑突然发生花屏死机 - phpStudy...

    win7电脑突然花屏死机的几种原因和快速解决方法(推荐) win7系统电脑突然发生花屏死机现象相信很多用户都有遇到过,特别是在玩游戏或者看电影到精彩部分的时候就发生花屏或死机现象了.许多电脑小白都在为 ...

  3. win10与linux 开机黑屏,win10系统开机黑屏进不去的两种原因及解决方法

    win10电脑用久了总会会遇到某些故章,较为普遍的属于黑屏了.黑屏的故障就可以分为多种状况,比如开机黑屏,只有鼠标出现.或者是驱动不兼容导致电脑黑屏等,造成黑屏的原因也有很多,所以不同情况,修复方法也 ...

  4. mysql表中的中文是乱码_mysql插入表中的中文显示为乱码或问号的解决方法

    版权声明:本文为博主原创文章,未经博主允许不得转载. 今天在做ssh的博客项目时发现mysql数据库中的中文显示为问号,网上查阅了很多资料,都不是很全,所以我总结一下,供大家参考和自己复习. 1.我的 ...

  5. Adobe Illustrator CS6 界面文字按钮太小,高分屏win10PS/AI等软件界面字太小解决方法

    Adobe Illustrator CS6 界面文字按钮太小,高分屏win10PS/AI等软件界面字太小解决方法 参考文章: (1)Adobe Illustrator CS6 界面文字按钮太小,高分屏 ...

  6. u盘属性显示制造商不可用问题的解决方法

    u盘属性显示制造商不可用问题的解决方法 参考文章: (1)u盘属性显示制造商不可用问题的解决方法 (2)https://www.cnblogs.com/jiela/p/11010528.html 备忘 ...

  7. 计算机可移动磁盘无法显示图片,手机插电脑不显示可移动磁盘的详细解决方法...

    有时候因为某些原因,我们需要将手机上的文件转移到电脑上,但是在操作的过程中,却遇到了手机插电脑不显示可移动磁盘的情况,不知道如何处理很是苦恼.所以针对这一问题,今天本文为大家整理的就是关于手机插电脑不 ...

  8. Tips--Solidworks 2016绘制工程图时显示gtol.sym文件缺失的解决方法

    Solidworks 2016绘制工程图时显示gtol.sym文件缺失的解决方法 在绘制工程图时没有各种标注图例,点击图例显示gtol.sym文件缺失.按照网上的通用的步骤,将gtol.sym文件找到 ...

  9. 移动端前端笔记 — 遇到的常见JS与CSS问题及解决方法

    移动端前端笔记 - 遇到的常见JS与CSS问题及解决方法 参考文章: (1)移动端前端笔记 - 遇到的常见JS与CSS问题及解决方法 (2)https://www.cnblogs.com/zhaoda ...

  10. csv 中 数值被自动转换成科学计数法 的问题 excel打开后数字用科学计数法显示且低位变0的解决方法

    csv 中 数值被自动转换成科学计数法 的问题 excel打开后数字用科学计数法显示且低位变0的解决方法 参考文章: (1)csv 中 数值被自动转换成科学计数法 的问题 excel打开后数字用科学计 ...

最新文章

  1. 微软宣布 Power Fx 开源
  2. [LOJ 6288]猫咪[CF 700E]Cool Slogans
  3. HDOJ1536 S-nim
  4. CodeProject上的最近几篇关于IronPython技术的文章
  5. java访问本地文件_java 读取本地文件 更改
  6. duilib学习领悟(2)
  7. 1011 Sticks
  8. 关于.net 页面提交后 css失效或者部分失效的问题
  9. 运维必读:避免故障、拒绝背锅的 10 大原则!
  10. matlab:绘制box函数和高斯函数曲线并进行傅里叶变换
  11. 浅谈对软件工程的认识与理解
  12. Matlab使用-norm函数
  13. FS2116C输入3.7V输出12V2.2A高效升压IC芯片
  14. anaconda安装及pytorch、tf、jupyter环境配置
  15. 项目记录——ANSYS Fluent入门翼型风洞模型计算
  16. 不了解外贸装箱,这一篇够够的
  17. Fansblog  HDU-6608(费马小定理、威尔逊定理)
  18. 数据结构(c语言版 第二版 严蔚敏)第一张绪论笔记
  19. 最好玩的计算机游戏排行,10款好玩的电脑单机游戏 好玩的单机游戏排行
  20. PhotoZoom图像缩放方法效果对比

热门文章

  1. 我的孤独自学之路----kali 安装及更新源
  2. 怎样在 iPhone 或 iPad 上使用“快捷指令”?
  3. 趋势病毒软件卸载攻略
  4. python3连接到sql server数据库,检查是否存在某个数据库,没有则创建
  5. PHP实现队列(二)Redis 实现队列
  6. MPB:湖南师大尹佳组-抑菌圈和药敏实验研究益生菌拮抗病原菌和抗生素敏感性的方法...
  7. 计算机自我鉴定范文7月,学员自我鉴定表7篇
  8. 直线端点画垂线lisp_AutoCAD中利用AutoLISP开发小程序,实现快速画直线对称中心线...
  9. 在Novell NetWare中支持IP
  10. NetWare 客户服务禁用了欢迎屏幕和快速切换恢复方法