byte-fe / intern-study

实习生互助学习
MIT License
33 stars 6 forks source link

[实习笔记]移动端1px线&will-change #16

Open sunshinelover opened 6 years ago

sunshinelover commented 6 years ago

移动端1px线

viewport告诉你1px为什么变粗

html的代码中经常会有这么一句

\

这句话的意思是当前viewport的宽度为设备宽度,初始缩放值和最大缩放值都为1,并禁止了用户缩放viewport通俗的讲是浏览器上可用来显示页面的区域。而这句话设置的意义是先不需要用户缩放和横向滚动条就能正常的查看网站的所有内容;第二,显示的文字的大小是合适,比如一段14px大小的文字,不会因为在一个高密度像素的屏幕里显示得太小而无法看清,理想的情况是这段14px的文字无论是在何种密度屏幕,何种分辨率下,显示出来的大小都是差不多的。

还有一个因素也会引起css中px的变化,那就是用户缩放。例如,当用户把页面放大一倍,那么css中1px所代表的物理像素也会增加一倍;反之把页面缩小一倍,css中1px所代表的物理像素也会减少一倍。

在早先的移动设备中,屏幕像素密度都比较低,如iphone3,它的分辨率为320x480,在iphone3上,一个css像素确实是等于一个屏幕物理像素的。后来随着技术的发展,移动设备的屏幕像素密度越来越高,从iphone4开始,苹果公司便推出了所谓的Retina屏,分辨率提高了一倍,变成640x960,但屏幕尺寸却没变化,这就意味着同样大小的屏幕上,像素却多了一倍,这时,一个css像素是等于两个物理像素的。其他品牌的移动设备也是这个道理。例如安卓设备根据屏幕像素密度可分为ldpi、mdpi、hdpi、xhdpi等不同的等级,分辨率也是五花八门,安卓设备上的一个css像素相当于多少个屏幕物理像素,也因设备的不同而不同,没有一个定论。

有一个devicePixelRatio属性,它的官方的定义为:设备物理像素和设备独立像素的比例,也就是 devicePixelRatio = 物理像素 / 独立像素。我们在css中使用的px就可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。例如,在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。

所以这就能解释为什么我们在css中设置的1px看上去比较粗了,也就是比如说我们当前的设备设置了width=device-width, initial-scale=1.0并且当前的设备devicePixelRatio为2,那么在css设置的1px就相当于物理像素的2px啦。

viewport的宽度可以通过 document.documentElement.clientWidth 来获取

1px解决方法

用小数的px

iOS8已经支持带小数的px,但是安卓和低版本的iOS不适用,所以在要考虑兼容性的情况下这个解决方案不太可取。

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

上文有提到devicePixelRatio,media query对应devicePixelRatio有个查询值-webkit-min-device-pixel-ratio,

transform缩放

版本1:

@mixin border1pxtop($color: rgb(232, 232, 232)) { position: relative; &:before { content: ''; position: absolute; background: $color; left: 0; top: 0; width: 100%; height: 1px; // dpr 小于2.9视为2倍屏 @media screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-max-device-pixel-ratio: 2.9) { -webkit-transform: scaleY(0.5); transform: scaleY(0.5); } // dpr 大于2.9视为3倍屏 @media screen and (-webkit-min-device-pixel-ratio: 2.9) { -webkit-transform: scaleY((1/3)); transform: scaleY((1/3)); } } }

版本2: 解释一下,先给外面的元素定位position:relative,然后给伪元素定位position:absolute,这样伪元素就相对于外面元素定位,所以需要给伪元素加上left:0,top:0,另外给伪元素设置边框border:1px,这边可以定制自己想要的border,包括颜色,上边框或者右边框之类的,然后给width:200%,height:200%,这样就相对于定位的那个元素放大了两倍,此时再加上 transform: scale(0.5);就可以将宽度、高度都缩放到和相对定位的那个元素一样大小,另外边框的宽度也会缩放一倍,达到我们想要的效果。

@mixin border1pxtop2($color: rgb(232, 232, 223)) { position: relative; &::before { pointer-events: none; content: ''; box-sizing: border-box; position: absolute; left: 0; top: 0; width: 100%; height: 100%; -webkit-transform-origin: 0 0; transform-origin: 0 0; border-top: 1px solid $color; @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { width: 200%; height: 200%; -webkit-transform: scale(0.5); transform: scale(0.5); } @media (-webkit-min-device-pixel-ratio: 3), (min-resolution: 3dppx) { width: 300%; height: 300%; -webkit-transform: scale(0.333333); transform: scale(0.333333); } } }

用background

@mixin border1pxleft2($color: rgb(232, 232, 232)) { background-size: 1px 100%; background-repeat: no-repeat; background-position: left top; background-image: linear-gradient(90deg, $color, $color 100%, transparent 0%); @media screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-max-device-pixel-ratio: 2.99) { background-image: linear-gradient( 90deg, $color, $color 50%, transparent 50% ); }

will-change

transform方法的版本1有在一些机型不奏效的情况,这个时候可以尝试加上will-change:transform属性。will-change是css3新增的属性。这个属性如果形象一点说,那就是在真正的行为触发之前告诉浏览器:注意咯,我一会儿要有变化啦,做好准备哦!作为回应,浏览器会把GPU给拉上,从容应对即将到来的变化。这样浏览器可以在元素属性真正发生变化之前提前做好对应的优化准备工作。 这种优化可以将一部分复杂的计算工作提前准备好,使页面的反应更为快速灵敏。这也应该是我们使用这个属性的初衷。

但是不得不说想要用好这个属性并不容易,试想那种全局都开启will-change的做法,等于是让浏览器的各个元素都随时GPU渲染加速待命,效果是适得其反的。

下图数据来自https://caniuse.com/#feat=will-change lark20180627-115620 will-change的几个取值:

catee commented 6 years ago

666

leecade commented 6 years ago

深入! 所以现在实现 1px 线的最佳方案是?

sunshinelover commented 6 years ago

推荐的最佳方案是transform的方式,比较灵活,而且版本2还能实现圆角的场景