移动端开发(web app) 之移动端布局 知识总结

App 分类

移动端开发(web app) 之移动端布局 知识总结

如上图,Native app 是使用原生开发的 app, 优点是性能更好,还能调用系统的 api ,但是发布 app 流程繁琐,而且不跨平台。

Web app, 优点是跨平台,修改方便,缺点是不能调用原生的 api, 而且用户体验不如原生 app, 好。

Hybrid app, 结合了上面两个的优点,可以说是很 nice。

尺寸相关概念

CSS 像素

又称为设备独立像素、逻辑像素。CSS中使用的一个抽象的概念,单位是 px

值是相对的,并不是绝对的,根据 dpr 来确定一个 CSS 像素代表几个物理像素,还有一些情况,例如用户缩放的时候,,dpr 也会跟着变为 2, 此时一个 CSS 像素代表两个物理像素。

注意: 电脑当中的一个设备像素一般是等于一个 CSS 像素。所以我们在 PS 当中的切图大小,一般也代表物理像素表示的大小。

设备像素

又称为物理像素,任何设备屏幕的物理像素的数量自出场开始就是固定不变的,单位是 pt(点)。一个物理像素即屏幕上一个发光的点。物理像素单个点的大小由厂商决定,大小不固定。

屏幕尺寸

指的是屏幕对角线的长度,单位为英寸,注意英寸是长度单位,不是面积单位。1英寸(inch)=2.54厘米(cm)

屏幕尺寸=屏幕斜边的像素/PPI。

移动端开发(web app) 之移动端布局 知识总结

像素密度 PPI

单位面积上(英寸)像素(设备像素)的数量。它是一个定值,是一个固定的参数。PPI=屏幕斜边的像素/屏幕尺寸。如下:

移动端开发(web app) 之移动端布局 知识总结

所以要提高 PPI的话,需要增加水平和竖直方向的像素点数量。因此,同一尺寸(inch)下,PPI提高了一倍,那像素会变为原来的4倍,也就是所 PPI 增加 N 倍,单位面积上的像素点的数量变为原来的 N^N 倍,如下图。查询不同设备的 ppi

移动端开发(web app) 之移动端布局 知识总结

PPI 的值越高,代表在一定尺寸的屏幕上像素数量越多,屏幕越清晰。如下同一张图片。

移动端开发(web app) 之移动端布局 知识总结

但同时,由于单位面积(英寸)并没有变,由于 PPI 的变大,对应的物理像素点会缩小。和上面的物理像素点的规律相反,PPI 增加 N 倍,单位面积上的像素点的大小将变为原来的 1 / N^N 倍。如下图:

移动端开发(web app) 之移动端布局 知识总结

同时对于一张图像,放到比当前 PPI 大的设备上时,也将按照上面的比例变小。例如一个放到一个 PPI 为自己的 2 倍的设备上时,图像将缩小四倍,如下图。

移动端开发(web app) 之移动端布局 知识总结

但是在实际当中我们更希望图片保持原有大小,并且最好是也不变模糊,保持原有的清晰度。对于上面的情况,解决方案是设备的 DPR 需要是原来设备的 2 倍,即使一个 CSS 像素需要代表的物理像素点需要变为原来的 2 倍,这就保持了图像的大小未发生改变,同时图片的分辨率也需要变为原来的两倍,这样就保持图像的清晰度未发生改变。

像素比 DPR

DPR=物理像素/css像素。获取:window.devicePixelRatio查询1查询2

它的本质是指一个 CSS 像素代表几个 物理像素。它的意义是为了让图像能够早高清屏上显示。

其实上当像素比超过 2 后,肉眼是识别不出来的。

视口 ViewPort

PC 端的页面直接放到移动端的情况的话,因为移动端一般默认视口的大小为 980px,而一般移动端的 css 像素达不到那么大,所以默认会进行缩小, 使在移动端的页面整体被缩放,所有的东西看起来都很小,为了解决这个问题,需要正确设置 viewport。移动端一般默认视口的大小为 980px,超出便会出现滚动条,或者直接进行缩放,看手机,我测试的时候, inphone6 是进行缩放, huaweip20 是出现滚动条。
如果要进行缩放,缩放比=物理像素(即设备像素)/ 视口宽度(html 的宽度,默认是 980px, 用document.documentElement.clientWidth方法获取)。所以在移动端开发的时候,我们需要正确的设置 ViewPort的值,否者页面看上去会变得很小。这个缩放比没什么用,下面的缩放比可以用来设置 viewport 的宽度。

典型的 ViewPort 的设置值为下面这样:

<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
  • width: device-width(设备的实际宽度--即设备的 css 像素)。
  • user-scalable: 是否允许用户在设置或者默认的视口宽度上进行页面缩放,值为no或yes,代表不允许与允许。
  • initial-scale: 页面初始缩放值,值为一个数字(可以带小数)。大于 1 表示放大。
  • minimum-scale: 页面最小能够缩放的比例,值为一个数字(可以带小数)。
  • maximum-scale: 页面最大能够缩放的比例,值为一个数字(可以带小数)。

需要注意的是,在移动端,如果不限制缩放,那么为了保证页面全部可见,会对页面整体进行缩放, 缩放的计算方式为: 缩放比(initial-scale)=css像素(设备css像素)/viewport宽度(html宽度)

initial-scale = 1,可以达到和 width=device-width, 一样的效果。如果都设置了,取两者的最大值。

需要注意的是不管 viewport 设置为多少,只要超过 viewport 的宽度,就会进行缩放,比如就算将 width=device-width 设置上,如果不禁止缩放的话,超过 viewport 的宽度的时候,还是会进行缩放, 一般当我们都禁止缩放,如下:

minimum-scale=1,maximum-scale=1,user-scalable=no

当设置了正确的 viewport 和禁止了缩放之后,那么超过视口之后就会出现滚动条,而不会进行缩放。

还需要注意,上面两个缩放比是不一样得到,第一个是指如果 viewport 超过了设备的 css 像素的时候,viewport 如何进行缩放,而第二个是不管 viewport 为多少的时候,当超过 viewport 的时候,如何进行缩放。

一般根据上面的第二个公式,设置了正确的 viewport 后,我们可以通过设置缩放比,动态的设置 viewport 的宽度。

meta 标签

禁止识别电话与邮箱(但是邮箱没效果)
            <meta name="format-detection" content="telephone=no,email=no" />

            设置添加到主屏后的标题(ios)
            <meta name="apple-mobile-web-app-title" content="标题">

            添加到主屏幕后,全屏显示,删除苹果默认的工具栏和菜单栏(无用)
            <meta name="apple-mobile-web-app-capable" content="yes" />

            放在桌面上的logo
            <link rel="apple-touch-icon-precomposed" href="iphone_logo.png" />

            启动时候的画面(无用)
            <link rel="apple-touch-startup-image" href="logo_startup.png" />

            设置x5内核浏览器只能竖屏浏览(只有UC有效)
            <meta name="x5-orientation" content="portrait" />
            
            设置x5内核浏览器全屏浏览
            <meta name="x5-fullscreen" content="true" />
            
            设置UC浏览器只能竖屏浏览
            <meta name="screen-orientation" content="portrait">
            
            设置UC浏览器全屏浏览
            <meta name="full-screen" content="yes">
            如果想屏蔽所有浏览器的横屏的话,需要在后面陀螺仪那章节讲

移动端样式重置

1、禁止用户选中文字,安卓无效(在事件那章解决,包括长按的时候会出菜单,用阻止touchstart后的默认行为搞定)
                    -webkit-user-select: none;
2、禁止长按弹出系统菜单
                    -webkit-touch-callout: none;
3、去除android下a/button/input标签被点击时产生的边框 & 去除ios下a标签被点击时产生的半透明灰色背景
                    -webkit-tap-highlight-color: rgba(0,0,0,0);
4、切换横竖屏或者用户自己通过浏览器设置的话,可以改变字体的大小(需要给body下的所有元素)
                    -webkit-text-size-adjust: 100%;
5、按钮在ios下都是圆角
                    -webkit-appearance: none;    //button与input都会有个默认背景
                    border-radius: 0;    //input有个默认圆角
6、修改placeholder的样式
                    input::-webkit-input-placeholder{
                        color:#000;    //默认的样式
                    }
                    input:focus::-webkit-input-placeholder{
                        color:#f00;    //点击后的样式
                    }
7、字体
                    ios
                        默认中文字体是Heiti SC
                        默认英文字体是Helvetica
                        默认数字字体是HelveticaNeue
                        无微软雅黑字体
                    android
                        默认中文字体是Droidsansfallback
                        默认英文和数字字体是Droid Sans
                        无微软雅黑字体
                    
                    font-family: helvetica;

移动端适配

适配是为了一版设计稿走天下,适配的范围包括字体、宽高、间距、图像

在熟悉了解了前面的概念之后,下面就来介绍移动端的适配几种方式。

百分比适配

根据父级设置宽度。但是有时候百分比不太好算,而且计算的时候需要知道父级的高度。

viewport 缩放适配

根据前面 缩放比 = 设备 css 像素 / viewport 宽度 => viewport 宽度 = 设备 css 像素 / 缩放比。

把所有机型的 css 像素设置成一致的,那么切图的时候只需要切一种尺寸的就可以了。代码如下

(function(){
                //获取css像素(viewport没有缩放)
                var curWidth=document.documentElement.clientWidth;
                var targetWidth=375; // 最终尺寸
                var scale=curWidth/targetWidth; // 缩放比
                var view=document.getElementById('view');
                view.content='initial-scale='+scale+',user-scalable=no,minimum-scale='+scale+',maximum-scale='+scale+''; // 这样设置了以后,所有设备上的 viewport 都为 375了。
            })();

注意这里也需要将最大和最小缩放比设置为和初始的值一样,不然如果它们都还是 1 的话,前面的就没有效果了,因为现在还是规定最大最小的缩放比为 1。

缺点:因为现在在任何设备上的viewport的宽度都为固定的宽度,例如 375 的图片放到 大屏幕上,因为宽度是固定的,所以肯定会变得模糊

dpr 缩放适配

根据dpr的值,把视口进行缩放,缩放到物理像素,也就是把css像素的值设置成物理像素,让所有的设备都变成一个css像素对应一个设备像素,方便切图

(function(){
                /*
                    375*2    750 
                    320*2    640

                    375/?=750    => 375/750=2
                    1/dpr // 公式

                    320/scale=640    =>     scale=320/640    1/2
                 */

                var meta=document.querySelector('meta[name="viewport"]');
                var scale=1/window.devicePixelRatio;

                if(!meta){
                    //这个条件成立说明用户没有写meta标签,我需要创建一个
                    meta=document.createElement('meta');
                    meta.name='viewport';
                    meta.content='width=device-width,initial-scale='+scale+',user-scalable=no,minimum-scale='+scale+',maximum-scale='+scale+'';
                    document.head.appendChild(meta);
                }else{
                    meta.setAttribute('content','width=device-width,initial-scale='+scale+',user-scalable=no,minimum-scale='+scale+',maximum-scale='+scale+'');
                }
            })();

rem 适配

rem 是相对于根节点(html)的字体大小。

它的原理是把所有的设备都分成相同的若干份,再计算元素宽度所占的份数,开发的时候只需要使用一个特定的设备 并且使用rem 那么在其他设备上会自动出现相同的效果即自动适配(很神奇),并且设计稿需要以物理像素为准来设计 例如分辨率宽为 375 drp为2,那么实际针对该机型的设计搞应该为750。

媒体查询方式

即通过媒体查询,对所有范围内的尺寸给定一个 html 根结点字体大小。可参考苏宁易购。

js简单方式

  1. 计算出每一列的宽度:viewport宽度(css像素)/16(列数),并将其设置为根结点字体大小。
  2. 根据设计稿量出的实际尺寸除以 dpr,然后再根据换算后的尺寸,除以一列的宽度(1rem),元素占 n 列
  3. 然后设置元素的宽度为 nrem。
(function(){
                var html=document.documentElement;    //html
                var width=html.clientWidth;        //css像素
                // 方式一
                html.style.fontSize=width/16+'px';    //把屏幕分成了16列,以iphone为例得出一个列的值为整数
                // 方式二
                html.style.fontSize= 16 * clientWidth / 375 + 'px'; // 以iphone6 为基准,当大于 iphone6 的尺寸时,字体大小大于 16px, 同理小于时,字体小于 16px。
            })();

js经典方式

相比于上面的普通方式,经典方式省掉了,根据设计稿量出的实际尺寸除以 dpr 这一步,直接将设计稿当中量出来的尺寸除以 100 然后单位为 rem。下面依然以 iphone6 为例。

(function(doc, win, designWidth) {
                const html = doc.documentElement;
                const refreshRem = () => {
                    const clientWidth = html.clientWidth;
                    if (clientWidth >= designWidth) { //给宽度一个最大值,如果设备的宽度已经超过设计稿的尺寸了,统一按一个值去算(传的第三个参数)
                        html.style.fontSize = '100px';
                    } else {
                        html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'; // 这种方式是我们在切图的时候不需要手动去除dpr, 已经帮我们处理好来,但是我们在计算的时候还是必须使用1rem = 100px来计算
                    }
                };
                
                //dom加载完的一个事件
                doc.addEventListener('DOMContentLoaded', refreshRem);
            })(document, window, 750);

vw 适配

vw 将页面分为 100 份,相关单位如下:

  1. vw Viewport's width的简写,1vw等于视口宽度的1%
  2. vh Viewport's height的简写,1vh等于视口高度的1%
  3. vmin 取vw和vh中最小的值
  4. vmax 取vw和vh中最大的值

兼容性如下:

  • >=ios 8
  • >=Android 4.4

并且 vw、vh分别代表水平和竖直方向,和横竖屏没有关系,会自动根据横竖屏来计算得到每一份的宽度,如图:

移动端开发(web app) 之移动端布局 知识总结

有两种使用 vw 方式

方式一

通篇使用 vw, 和前面 rem 部分提到的 js 简单部分差不多,只不过省略了计算每一份的过程,浏览器自动帮我们计算 1vw 是多少。注意切图的使用还是要除以 dpr。可以采用 scss 等 css 预处理语言来简化这个过程。

方式二

通过 vw 设置根节点字体大小,页面里的尺寸依然使用rem。但是前提是你要知道页面当中根节点的字体大小,根节点字体的大小的计算参考前面 rem 布局部分的 js 经典方式

移动端存在的几个问题

固定定位

在 ios 下面,如果页面里有一个固定定位(fixed)的元素以及一个 input 元素的时候,用户但凡点击input调出键盘的时候,固定定位元素就会不起作用。解决方式:

  1. 将需要滚动的元素放到一个容器里面,容器超出就滚动,这样容器和固定定位元素就会在一个页面,就算失效了,也在一个页面内可见。

1px 问题

在移动端 dpr, 往往是 2 及其以上,所以 1px 往往代表多个物理像素,这就是 1px 问题, 解决办法:

  1. dpr 缩放适配不存在这个问题,因为 dpr 缩放适配是 1px = 1pt
  2. 可以使用 css3 提供的 scale 来缩放边框为 0.5 倍, 下面是创建了一个伪类,并缩放它的边框来达到缩放的目的.
section::after{
                content: '';
                position: absolute;
                left: 0;
                top: 0;
                border: 1px solid #000;
                box-sizing: border-box;
                width: 100%;
                height: 100%;
                transform-origin: 0 0;
                transform: scale(0.5);
            }

相关推荐