Grasp the value of adapting the three viewports on the mobile terminal & understand the meaning of width and initial-scale in meta & handle the 1px problem and image blur problem

What will you gain from this article?

  1. Clarify the significance of the existence of the three viewports on the mobile terminal.
  1. Have a clear understanding of the configuration and configuration <meta name="viewport">in the core tags of mobile terminal adaptation , and be able to solve practical problems through configurationwidthinitial-scaleinitial-scale
  1. Have a clear understanding of the "confusing behavior" flexible.jsset in - solve the "1px problem"initial-scale
  1. Understand and solve the problem of blurred pictures on mobile high-power screens.

The meaning of Initial-scale and width in the meta tag

A brief summary of the three viewports on the mobile terminal: the so-called layout viewport is the space where the dom layout we wrote is located, and the page layout is rendered in this space, and then the layout is rendered on the mobile phone through zooming (underlying automatic proxy) It is displayed on the screen one by one; the visual viewport refers to the space presented on the screen of the mobile phone, that is, what the user sees; the ideal viewport can be understood as a standard, a reference, dip(设备独立像素)and since Its own dipsize is similar to a hard-coded software and hardware attribute (in the initial state where the mobile page is not zoomed, what the developer wrote is 1css像素equal to 1dip)

First of all, clarify the significance of the three viewports for our front-end developers- to describe how many css pixels cover the mobile phone screen for developers (generally based on the width).

  • If you do not write <meta name="viewport">tags to configure the viewport, the width of the layout viewport may be 980pxa relatively large value, and eventually the page content on the layout viewport will be reduced and placed on the mobile phone screen. For us developers, it means development When we write 980pxthe box will fill the width of the phone screen.
  • If we <meta name="viewport">set widththe property to a specific value in or device-width, in fact, it is to modify the width of the layout viewport, the final effect is: write what we set during development to widthfill the screen. Because the layout viewport still needs to be reduced to the visual viewport, that is, the mobile phone screen, this is the processing logic of the mobile device.
  • <meta>Another attribute of the label initial-scale, the value is a number greater than 0 such as 1, 0.5. The meaning of this number is dipthe width of the device, that is, the ideal viewport width (fixed value) is the visual viewport width initial-scale, that is, the size of the visual viewport is set. In any case, the visual viewport of a mobile device is ultimately presented with an entire mobile phone screen, so to put it bluntly, by initial-scaleadjusting the size of the visual viewport, it is still describing how many css pixels the mobile phone screen (width) consists of for developers composition.

Setting widthis equivalent to modifying the size of the layout viewport, and finally the layout viewport is reduced and thrown into the mobile phone screen (both are completed by background software and hardware agents); setting initial-scaleis equivalent to directly modifying the size of the visual viewport ideal viewportas a reference, and the visual viewport is ultimately determined by The mobile phone screen is displayed (completed in the background), so the two seem to conflict : for example, if I set the layout viewport to 800, it means that it takes 800px to fill the width of the mobile phone screen, and I set it to initial-scale( 0.1assuming the device dipwidth is 300) , then it is equivalent to modifying the width of the visual viewport to 3000px, which means that it takes 3000px to fill the width of the mobile phone screen—a conflict occurs.

When it conflicts widthwith initial-scalethe description of the screen width of the mobile phone, the screen width (how many css pixels are in the screen) takes the larger value of the width described by the two attributes of width and initial-scale .

for example:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        div {
            width: 50vw;
            height: 50vw;
            border: 1px solid red;
        }
        .child {
            width: 380px;
            height: 380px;
            background: pink;
        }
    </style>
</head>
<body>
    <div>123
        <div class="child">456</div>
    </div>
    <script>
        console.log("width=device-width, initial-scale=1.0");
        console.log(window.devicePixelRatio);
        console.log("layout viewport:", document.documentElement.clientWidth);
        console.log("visual viewport:", window.innerWidth);
        console.log("ideal viewport:", window.screen.width);
    </script>
</body>
</html>

这段代码用Pixel 5机型(dip大小/理想视口大小: 393 * 851)打开,代码暂时只需要关注<meta>里,以及下面我描述的456盒子的大小widthinitial-scale所描述的屏幕宽度都是等于设备的dip宽度,也就是393px

效果如下(380px的456盒子基本占满了屏幕,也就是屏幕宽度就是393px):

移动端meta探索1.png 修改<meta>标签的initial-scale0.5,并修改456盒子的宽度为760px,代码与效果如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=0.5">
    <title>Document</title>
    <style>
        div {
            width: 50vw;
            height: 50vw;
            border: 1px solid red;
        }
        .child {
            width: 760px;
            height: 760px;
            background: pink;
        }
    </style>
</head>
<body>
    <div>
        123
        <div class="child">456</div>
    </div>
    <script>
        console.log("width=device-width, initial-scale=0.5");
        console.log(window.devicePixelRatio);
        console.log("layout viewport:", document.documentElement.clientWidth);
        console.log("visual viewport:", window.innerWidth);
        console.log("ideal viewport:", window.screen.width);
    </script>
</body>
</html>

移动端meta探索2.png 通过对比就可以印证我所说的:widthinitial-scale所描述的屏幕大小(多少css像素撑满屏幕)冲突时取较大值。plus:仔细观察也会发现相同font-size的文字,1px的边框在上面呈现的效果都不一样,印证了屏幕大小变了。

width与initial-scale的取值能为移动端适配带来什么

细心的读者会发现,上面的两个html例子中,都有一个123盒子,他的大小是50vw * 50vw,在两个例子中屏幕大小(css多少)不同的情况下,呈现的效果是一致的(都占了屏幕宽度的一半)。所以理论上讲:如果我们在移动端开发过程中所有的大小单位都采用vw或者rem(本质还是占屏幕的百分比),那么具体屏幕有多少css像素我们毫不关心,换句话说,widthinitial-scale的取值开发者完全不关心。?

但是这两个属性因为修改了移动端屏幕内的css像素多少,所以当我们在开发中使用px这种绝对单位时,手机屏幕具体有多少css像素就与最终的呈现效果息息相关了。如上两个html例子,粉色盒子border都是1px,但是上面的html例子中因为手机屏幕css大小比较小,1px就比较粗。

总结:width与initial-scale的取值影响了手机屏幕里的css像素多少,决定了项目中绝对单位(px)的呈现效果。

解读flexible.js适配方案的<meta>标签

下面是flexible.js中的一段源码,总体流程就是获取设备的dpr,然后设置meta标签的initial-scale1/dpr

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

理解上面我们所说的移动端适配方案思想的同学其实应该意识到了,flexible.js方案做到不同设备等比缩放其实如下几行代码就做到了:

function setRemUnit () {
    var rem = docEl.clientWidth / 10
    docEl.style.fontSize = rem + 'px'
}
setRemUnit();

之所以还要将initial-scale设置为1/dpr这个“多此一举”的操作其实是为了解决移动端高倍屏的“1px”问题。

“1px问题”解读

我来说一下我理解的何为“1px问题”:

设计稿上一些线条非常细,就比如设计师的要求就是我们需要一条1px物理像素的线条,那么对于高倍屏,比如dpr=2(两个物理像素绘制一个dip像素,可以简单理解为css像素),我们是不是就要写0.5px(我们开发时写的是dip像素,css像素),但是各种浏览器对于这个极小的px(小于1的px)处理逻辑是不统一的:

  • chrome:把小于0.5px的当成0,大于等于0.5px的当作1px
  • firefox:会把大于等于0.55px的当作1px
  • safiri:把大于等于0.75px的当作1px 进一步在手机上观察iOS的Chrome会画出0.5px的边,而安卓(5.0)原生浏览器是不行的。所以直接设置0.5px不同浏览器的差异比较大。

总之我们达不到设计的要求,换句话说浏览器的差异导致我们没办法在编码时统一1px(物理像素)的呈现效果。

回归<meta>标签对于initial-scale的设置问题,举个例子来说比较容易理解:设备dpr=2,编码开发时我们写的0.5px的绘制效果其实就是1个物理像素进行渲染,这里的0.5因为浏览器的处理逻辑不同就会出现各种不符合预期的情况,那么设置initial-scale=1/2之后手机屏幕内的css像素就变成了原本的两倍,此时一个css像素就由一个物理像素进行渲染,所以我们想要1px的物理像素,就直接写1px即可,因为各种浏览器对于1px的处理逻辑是统一的,所以就规避了“1px问题”。

如果你明白了上面的思想,那么网上各种对于“1px”的解决方法其实都不难理解了,就比如下面针对dpr=2的设备的解决方法:

.scale-1px{
  position: relative;
  border:none;
}
.scale-1px:after{
  content: '';
  position: absolute;
  bottom: 0;
  background: #000;
  width: 100%;
  height: 1px;
  -webkit-transform: scaleY(0.5);
  transform: scaleY(0.5);
  -webkit-transform-origin: 0 0;
  transform-origin: 0 0;
}

解决方案的核心是什么:就是在开发时,代码里写1px而不是写0.5px,这样就统一了各浏览器的绘制逻辑,然后通过scale缩放将其缩小至原来的一半。

移动端图片模糊问题

其实只要是理解了上面的思想,图片模糊问题就不难理解了,首先我们要知道我们平时使用的图片大都是位图(png、jpg...),位图是由一个个像素点组成的,每个像素都有具体的颜色,理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。 而在dpr > 1的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1的屏幕上就会模糊。

解决方案:

比如我们项目的<meta>标签中initial-scale=1,也就是说屏幕内的css像素数量就等于dip像素数量,也就是一个css像素由dpr个物理像素来渲染,那么我们就可以在不同dpr的设备上展示不同分辨率的图片来解决这个问题。

比如:在dpr=2的屏幕上展示两倍图(@2x),在dpr=3的屏幕上展示三倍图(@3x)本质上就是让位图的一个小格子由一个物理像素去渲染从而有最好的渲染效果

提供一种实施方案:

  1. srcset 使用img标签的srcset属性,浏览器会自动根据像素密度匹配最佳显示图片:
<img src="conardLi_1x.png"
     srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x">

参考文章

  1. 为什么会存在1px问题?怎么解决?(作者:花椰菜菜)
  2. 深入浅出移动端适配(总结版)(作者 :小小Mac)

Guess you like

Origin juejin.im/post/7250687457799815225