移动端适配

移动端适配 — 细节补充(二)

2020-01-21  本文已影响0人  西瓜鱼仔

相关链接:移动端适配 — 细节补充(一)


Retina屏幕&普通屏幕,模糊的由来

dpr的具体表现

有时候我们会发现,当我们在适某一机型的时候,显示上没什么问题。但是一旦我换到另外一部手机,发现出现了模糊的情况,尤其以图片更为显著。

其实这个问题,就是涉及到了上面讲到的一个属性:设备像素比,即我们经常说的dpr。下面先来看dpr的表现:

假设现在有一台iphone 6,那么它的设备独立像素是375x667,dpr为2,尺寸是4.7in,那么物理像素就是750x1334。同样的我们也有一台不知名的设备,它的设备独立像素刚好也是375x667,尺寸也是4.7in,但是dpr为1,此时的物理像素就是375x667。于是,它们的屏幕表现如下:

在不同的屏幕上,无论是普通屏幕还是retina屏幕,css像素所呈现的大小是一致的(如果不理解这句话,可以写一个2px的正方形使用谷歌控制台移动设备调试,在不同的设备之间来回切换,你会发现大小其实是一样的。一开始我总以为这个css像素的实际宽高因为受到dpr的影响而在不同设备上的长宽是不一致的)。

不同的是,1个css像素对应(覆盖)的物理像素个数。

所以,如果我们想要在这两个屏幕显示这么一个css样式:

width: 2px;
heigth: 2px;

在普通屏幕下,也就是dpr为1的屏幕中,1个css像素对应(覆盖)的是一个物理像素。在retina屏幕下,1个css像素对应(覆盖)的是4个物理像素。换句话说,就是dpr为2的设备。看下面这张图:

image

浅显的理解就是可以看作是2cm x 2cm的正方形被切割成四块,然后遇到dpr为2的时候,被切割的四块又被分别切割成四块,但是总面积不变。

模糊的产生

知道了1个css像素覆盖的物理像素可能不同,就好理解为什么会出现模糊的情况了。这里又讲到一个名词:位图像素

位图像素是栅格图像(如:png,jpg,gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息。(如:显示位置,颜色值,透明度等)

理论上来说,1个位图像素对应1个物理像素,图片才能达到完美清晰的展示。 但是上面说过,在retina屏幕上,会出现1个位图像素对应多个物理像素。

还是以iphone 6为例,1个位图像素对应4个物理像素。由于单个位图像素已经是最小的数据单位了,它不能再被进行切割。于是为了能够显示出来,就只能就近取色,从而导致所谓的图片模糊问题。如下:

如何解决

很明显,由于位图像素不够分而产生模糊的情况,解决的办法十分简单,就是使用跟dpr同个倍数大小的图片。比如iphone 6,一个200x300的img标签,原图就要提供400x600的大小。

那么当加载到img标签中,浏览器会自动对每1px 的css 像素减半,可以理解为此时还是维持着1:1的css像素:物理像素,不产生模糊。这个做法其实就是手淘团队在做retina适配的一个重要的原理之一,后面会讲到,这里先放着不说。

其他

反向思考一下,如果普通屏幕,也就是dpr为1的屏幕,也使用了两倍的图片,会发生什么样的情况呢?

很明显,在普通屏幕下,200×300的img标签,所对应的物理像素个数就是200×300个,而两倍图片的位图像素个数则是200x300x4,于是就出现一个物理像素点对应4个位图像素点,所以它的取色也只能通过一定的算法进行缩减,显示结果就是一张只有原图像素总数四分之一,肉眼看上去虽然图片不会模糊,但是会觉得有点色差(其实就是模糊的逆向过程)。

用图片来表示就是: image

这里摘取了网上一篇博文的demo来阐述上面所说的问题。

image

以上是一张100x100的图片,分别放在了100x100,50x50,25x25的容器中,在retina屏幕下面的显示效果。

通过取色器放大镜可以看出边界像素点的差别:
在图一中,边界像素点就近取色,色值介于红白之间,偏淡,图片看上去会模糊(可以理解为图片拉伸)。
在图二中,图片正常,很清晰。
在图三中,边界像素点就近取色,色值介于红白之间,偏浓,图片看上去有色差。

Retina屏幕下的处理与安卓手机的适配

分析手淘的flexible.js代码中可以知道,它布局仅仅只是针对iPhone进行适配,而默认所有的安卓设备都强制性设置dpr为1。于是,因为这个缘故,很多小伙伴可能就会产生这样的问题:为什么安卓不用retina屏幕,安卓下面是不是就不会有模糊的问题?

其实不然,模糊的本质是因为dpr,而安卓手机不同的设备的dpr也是不尽相同的。也就是说,安卓手机下也存在模糊的情况。只不过它的屏幕不叫retina屏幕,没有这个叫法,所以很多小伙伴都误认为安卓手机没有这个毛病。那么问题又来了?既然也有模糊的毛病,那么为什么安卓手机不进行适配呢?

问题就在这里了,有兴趣的小伙伴可以去看一下大中华的安卓手机,dpr参数五花八门,从1到4,连1.75、2.75这种奇葩的数字也有,所以个人觉得权衡之下,直接简单“粗暴”把安卓手机全部设置为1,是效率和收益更高的做法。

响应式与自适应的选择

最后,对于响应式和自适应的区别,网上有各种各样的解释。个人认为,其实没必要把它讲得那么复杂,知乎上有个小伙伴讲我觉得就很白话文:

响应式针对的是不同分辨率设备而进行的适配式设计,以利用@media规则为主要手段,而自适应则忽略@media以比例布局为主,目的是适应不同的浏览器窗口大小。

于是我们会发现,现今大型网站,例如说淘宝网,已经没有做响应式了。什么意思呢?我们会发现,淘宝网手机端和网页端使用的是两个域名,也就是说,不同的客户端已经不再共用一套dom结构了。而是区分开来做自适应。然后每次用户访问的时候它就根据客户端的类型重定向。

为什么呢?
试想一下淘宝这种大型网站,一个分页下的商品条目特别多,并且每个商品条目的dom结构又十分复杂,而且pc端往往显示的信息是要比手机端更多的。如果不分开做两套,而是直接用响应式的话,那么pc端上显示的很多dom就要在手机端上隐藏,结果这些dom都没有被用到,但是却加载了。在这个流量和速度至上的时代,代码冗余先不说,多加载的这些无用的代码而消耗的流量,从某种意义上来说就已经损失了很多的效益。

最后

考虑到兼容性的问题,原先我们在文章头部说到的那段代码:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

从 Chrome32+版本开始是会默认禁用用户缩放的,但是考虑到兼容大部分设备,还是要加上其他设置,让 meta标签能够有更好的容错性。也就是下面这段代码:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

需要注意的是,在 ios10+以上,尽管开发者设置了user-scalable=no, Safari还是允许用户通过手势来缩放。(安卓手机各大厂商的内置浏览器也逐渐开放用户缩放,即使使用meta 标签进行设置)
解决的方法也很简单,只需要检测touch 相关事件来阻止事件的触发即可。


window.onload = function() {
    // 同时按下两个手指
    document.addEventListener('touchstart', function(event) {
        if(event.touches.length > 1) {
            event.preventDefault()
        }
    })
    var lastTouchEnd = 0;
    // 特别注意300ms时差的设置
    document.addEventListener('touchend', function(event) {
        var now = (new Date()).getTime();
        if(now-lastTouchEnd <= 300) {
            event.preventDefault();
        }
        lastTouchEnd = now;
    })
}

以上,就是本文的全部啦。


原文地址:https://blog.csdn.net/xiaxiaoxian/article/details/79395694

上一篇 下一篇

猜你喜欢

热点阅读