前端移动端页面开发(布局篇)【转】

前言的一些碎碎念:最近一直在写移动端的页面,不过一直是用的别人造好的轮子,不少时候并无想那是为何,那是怎么样要那么写,就跟着别人的文档 去了。本觉得本身对移动端的那一丢丢理解,结果不少东西都特么有问题,因此,今天停下了手中的一些东西,来谈下移动端的布局方案吧html

内容有些长,这也是我第一次写博客,不足之处还请严厉指出前端

一. viewport

  1. 什么是viewportandroid

    简单来说,viewport就是浏览器上,用来显示网页的那一部分区域了,也就是说,浏览器的实际宽度,是和咱们手机的宽度不同的,不管你的手机 宽度是320px,仍是640px,在手机浏览器内部的宽度,始终会是浏览器自己的viewport。现在的浏览器,都会给本身的自己提供一个 viewport的默认值,多是980px,或者是其余值。就以手机来讲吧,目前,新版本的手机浏览器,绝大部分是以980px做为默认的 viewport值的。我这里对新版本的不一样平台下的浏览器作了测试,通过测试,iphone下的默认viewport为980px,安卓下的浏览器,目 前主流的最新浏览器(好比chrome,还有不少国产的像qq,uc)的viewport也是980px了。ios

  2. viewport是用来干什么的chrome

    viewport的默认值,通常来讲是大于手机屏幕的。这样就能够作到当咱们在浏览桌面端网页的时候,可让桌面端端网页正常显示(咱们普通页面设 计的时候,通常页面的主区域是以960px来作的,因此980px这个值,能够作到桌面端网页的正常显示)。可是,其实咱们手机的屏幕宽度是没有 960px的,所以浏览器会出现横向滚动条。同时,即便是基于980的viewport,咱们在移动端浏览咱们的桌面页面的体验其实也并很差,因此,通常 的,咱们会专门给浏览器设计一个移动端的页面。浏览器

  3. 对viewport的控制app

    现在能够绝大部分浏览器里(即主流的安卓浏览器和ios),都支持对viewport的一个控制了。通常的,咱们会这么写。iphone

    viewport默认有6个属性布局

    • width: 设置viewport的宽度(即以前所说起到的,浏览器的宽度详),这里能够为一个整数,又或者是字符串"width-device"
    • initial-scale: 页面初始的缩放值,为数字,能够是小数
    • minimum-scale: 容许用户的最小缩放值,为数字,能够是小数
    • maximum-scale: 容许用户的最大缩放值,为数字,能够是小数
    • height: 设置viewport的高度(咱们通常而言并不能用到)
    • user-scalable: 是否容许用户进行缩放,'no'为不容许,'yes'为容许

    咱们把这个标签是在head里面,像这样性能

    <meta name="viewport" content="initial-scale=1">

    这样就能够作到对viewport的控制了

二. 关于咱们的设备

  1. 三个须要了解的概念:

    • PPI: 能够理解为屏幕的显示密度
    • DPR: 设备物理像素和逻辑像素的对应关系,即物理像素/逻辑像素
    • Resolution: 就是咱们常说的分辨率
  2. 物理像素与逻辑像素

    看了咱们上面内容一的第一点以后,或许有些人会有些疑问,个人安卓手机,或者iphone6plus(目前应该仅限于这一款机型吧),买回来的是1920x1080的或者其余更高的,比我以前所谓的那个viewport默认的980px要大。

    这样的问题,也就是我以前所说的物理像素与逻辑像素的关系了(即DPR)。以1920x1080为例,1080为物理像素,而咱们在 viewport中,获取到的,好比"width-device",是逻辑像素。因此以前viewport的默认值,所比对的大小,实际上是逻辑像素的大 小,而非物理像素的大小。

    以iphone6为例,在不作任何缩放的条件下,iphone6的获取到的'width-device'为375px,为屏幕的逻辑像素。而购买时咱们所知的750px,则为屏幕的物理像素。

  3. CSS的问题

    有了上面第二点的一些基础,仍是以iphone6为例,咱们能够知道,其实咱们所写的1px,在iphone6上为2px的物理像素。因此,最后的,给出一个结论。就是咱们写的1px,在移动端,是逻辑像素的1px,而非物理像素的1px。

三. 使用rem布局

  1. 简单说下rem

    rem是根据页面的根元素的font-size的一个相对的单位,即

    html{
    	font-size: 16px;
    }

    好比当咱们在一个div中,如此写

    div{
    	width: 2rem;
    }

    那么咱们的width,是16*2 = 32px

  2. rem作到适配不一样分辨率

    这个是如今手机淘宝的移动端的解决方案,即便用rem的特性,来对页面进行布局。

    下面举一个例子

    假定设计稿的大小为750,那么咱们则将整个图分红100份来看(下面的题外话会说明为何会分红100份来看)

    那么,咱们如今就让根部元素的font-size为75px

    html{
    	font-size: 75px;
    }

    那么,咱们如今就能够比对设计稿,好比设计稿中,有一个div元素,宽度,高度都为75px,那么咱们这样写便可

    div{
    	height: 1rem;
    	width: 1rem;
    }

    可能看到这里,一些人仍是不明白怎么用rem作到适配不一样的分辨率,那么咱们再来

    如今,咱们换设备了,不用这个设备是一个width为640的手机

    那么这个时候,咱们的rem单位就起到做用了。

    咱们的rem全是根据html的font-size来改变的,因此说,这个时候,咱们只须要把html下的font-size改为64px。那么, 咱们以前的div,由于是根据html下的font-size动态变化的,那么。此时也就变成了宽度和高度都为64px的东西了。这样,就能够作到适配不 同的屏幕分辨率了。(其实就是个等比缩放)

    总结一下,咱们的解决方案,其实就是 设计稿的像素/html的font-size = 用来代替px的rem。

    这一个步骤,咱们须要经过JS来进行操做。

    对于js的操做在下面会提到。

  3. DPR的问题

    视觉姐姐给了咱们设计稿,并交由咱们实现,那么,咱们应该去认真的实现:-)(试想你作了一张图,而前端不少地方并无按照你所想的,你所给的去作,而是私自改变了不少东西,你确定会不高兴的)

    那么1px会出现什么问题呢。

    还记得咱们第二大点讲的,咱们的设备,是有物理像素和逻辑像素的。而假设咱们的设计稿是750的,同时仍是以iphone6为例,此时若是咱们的viewport是这样的

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

    以前说过,在不作任何缩放的条件下,iphone6获取到的viewport为375px。

    而后咱们的页面中有个div,他有一个边框值,以下

    div{
    	height: 5rem;
    	widht:5rem;
    	border: 1px solid #000
    }

    此时咱们写的1px,其实是逻辑像素,而咱们在iphone6上看到的是物理像素,因而这个时候,咱们眼睛所看到的实际上是2px(参考第二点第三个问题)

    因此此时咱们须要在viewport上作文章了,此时先明确,若是要获取到真正的1px,那么咱们须要这么作,将viewport改成

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

    即对屏幕作0.5倍的缩放。这样,咱们就能获得实际的1px。

    因此到这里,咱们还要明确一点,viewport的meta标签,咱们这里也只能经过js来动态生成。

    同时,这样写,听说还能够避免好比inline的SVG等元素按照逻辑像素的渲染。避免了整个页面清晰度的打折(其实我并不能看出来)

  4. 文字适配问题

    最近深深纠结与rem与px作字体单位的问题,仍是先分别谈下两者吧。

    rem与px的特色:

    • 以rem做为字体单位:咱们可让页面总体的文字,也跟随着html的font-size来进行改变,这样,在不一样的屏幕下,能够作到文字相对屏幕的比例是同样的。

    • 以px做为字体单位: 这个是目前不少网站仍是依然采用的方法。由于以上面所写的,以rem做为字体单位。不管在任何屏幕下面,咱们的文字都会根据屏幕作一个适应。试想这样一个 场景。你买了一个大屏手机(5.7寸的),而别人用的是4寸的手机。以rem做为字体单位的话,那大屏手机看到的文字多少和小屏手机确实同样的了。这样来 作,其实并不符合咱们买大屏手机的期待。同时,以rem做为字体单位,可能会致使出现不少奇怪的字体大小(毕竟是根据html的font-size动态变 化的嘛),同时这其中还涉及到了一个点阵尺寸的概念,这个在下面来说。

    字体大小引起的系列问题:

    • 字体大小:咱们平时也看过,不少网站,是不以奇数做为字体大小的。我稍微查了些东西,在知乎上的如今网页设计中的为何少有人用 11px、13px、15px 等奇数的字体?问题下,有一些比较好的解答,我就再也不多说(我也并不能比这个问题说的更多),总的来讲,其实就是偶数宽度的字体可以显得均衡,以及一个点 阵的问题。不过由于要谈及点阵,因此我拿上面回答中的一个内容举例。

      • 假若一个字体,只提供了12px,14px,16px的点阵。那么当你写13px,15px,17px的时候。就并无其字体大小所对应的点阵。 那么这样就形成了一个问题。他们会使用其相邻的点阵,好比对应使用了12px,14px,16px的点阵,而致使一个问题,文字占用的大小确实改变,但点 阵却并无改变。

    文字适配的解决方案:

    上面说了这么多,咱们总要有一套解决方案吧

    对于一些标题性的文字,咱们依然能够用rem。让他随着屏幕来进行缩放,由于标题性文字通常较大,而较大的文字,点阵对其影响就越小。这样,即便出现奇怪的尺寸,也可以让字体获得很好的渲染。

    对于一些正文内容的文字(即站在使用者的角度,你不但愿他进行缩放的文字)。咱们采用px来进行处理。

四.安卓与ios不得不说的问题(解决篇)

  1. 在 三.使用rem布局 里面,咱们给出了各类状况的解决方案,而且,在我举例的时候,热衷于使用iphone来举例,但其实,上面的全部问题,不是仅仅iphone会出现的问 题,安卓也是同样。可是,若是你已经看完了上面,那么这里,才是真正给出咱们解决方案的地方,而且,这个解决方案并不完善。

  2. 谈谈iphone的r屏与安卓的各类屏

    rem布局也好,用viewport进行缩放也罢,文字的适配问题也是,都是基于咱们想对各个不一样的设备所进行的匹配。这套方案很好,然而也有其兼顾不到的地方。即安卓和ios的屏幕的一些问题,固然,细的东西咱们不谈,咱们只谈dpr。

    • 先谈iphone

      • 其实iphone为开发者考虑到了不少东西,为了让开发者便于开发,在6plus出现以前,iphone的dpr始终也就是2(即前面所谈的物理像素/逻辑像素=2),即便是6plus出现了,iphone到底其实也就只有2,3这两个dpr。咱们很容易对其作到兼顾。
    • 再谈安卓

      • 安卓并无对本身的屏幕叫作r屏,可是其原理和iphone的r屏能够说是同样。r屏作的是什么,把两个(三个)物理像素,丢到了一个逻辑 像素里面,让屏幕展示的更清晰(固然,这是我片面的理解,不过我以为大致来讲并无错,咱们也不用去深刻探讨r屏还有什么东西,我也并不懂)。而安卓也是 同样,他也一样把n个物理像素丢到了一个逻辑像素里面。而这里的n,也就是dpr值(因此当我看到好多人问安卓为何不采用r屏的时候,我真的也是……醉 了?)。而安卓的dpr值,并不像iphone那样,就只有两个值。安卓的dpr是千奇百怪的,多是1.5,2,3,4,2.5等等的都有。(甚至我还 看到了1.7之类的,安卓的各个设备商,玩的真尼玛high啊。怎么高兴怎么来。)

        因此,对安卓的屏幕的dpr的处理,实际上是很头疼的,由于,他和咱们对字体的处理,有了很大的冲突。这个在下面说起

  3. 首先看看手淘的解决方案

    • rem布局

      用js获取到页面的宽度,而后对其进行宽度/10的处理,再将其写到html的font-size中。手淘的flexible.js里面的这一部分,并为了方便看懂作了些改写。大致就是这样的

      function refreshRem(){
       	var docEl = window.document.documentElement;
          var width = docEl.documentElement.getBoundingClientRect().width;
      
          var rootSize = width/10;
          docEl.style.fontSize = rootSize + 'px';
      }
    • dpr的配置

      首先,在引入flexible.js以前,咱们能够对dpr进行手动的配置,即便用自定义的meta标签来配置dpr(看清楚是flexible,而非viewport)

      <meta name="flexible" content="initial-dpr=2,maximum-dpr=3" />

      iniital-dpr是把dpr强制设定为给定的值,而maximum-dpr则是给出一个最大的dpr限制,而后对其和系统的dpr作一个比较。

      而后依然为了方便阅读我把flexble.js这一部分的代码抽象出来,

      var doc = window.document
          var metaEl = doc.querySelector('meta[name="viewport"]');
      	var flexibleEl = doc.querySelector('meta[name="flexible"]');
      	var dpr = 0;
      	var scale = 0;			//缩放比例
      	//在meta标签中,已经有了viewport,则使用已有的viewport,并根据meta标签,对dpr进行设置
      	if (metaEl) {
      		console.warn('将根据已有的meta标签来设置缩放比例');
      		var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
      		if (match) {
      			scale = parseFloat(match[1]);
      			dpr = parseInt(1 / scale);
      		}
      	//若是在meta标签中,咱们手动配置了flexible,则使用里面的内容
      	} 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));
      }
      }
      }

      这样,咱们经过flexible的分析与获取,对dpr进行了书写。不过其实这里,是有个问题的。即在书写maximum的的状况下,其实根本没有像文档中给咱们的说法同样,作一个比较,而是作了和initialDpr同样的一个处理。不过这里也不对其作一个探讨了。

      而后,这套解决方案,而后当咱们在meta标签里面并无对viewport以及flexible两个的任意一个进行书写的时候,他也是会自动获取一个dpr值的

      if (!dpr && !scale) {
      	var isAndroid = window.navigator.appVersion.match(/android/gi);
      	var isIPhone = window.navigator.appVersion.match(/iphone/gi);、
      	//devicePixelRatio这个属性是能够获取到设备的dpr的
      	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;
      }

      这里咱们能够看到,手机淘宝并无对安卓的dpr进行一个适配,缘由以后再讲。

      而后到了这里,咱们获取到了咱们须要的dpr值,并根据dpr值获取到了咱们所须要的缩放值(即scale)

      而后咱们要作的,就是在并无viewport的meta标签对状况下本身动态将这个标签写进咱们的header,形式是这样的

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

      这样,dpr的配置,也就完成了,固然,安卓设备并无对dpr进行一个配置(上面的动态生成就不给出js了)

    • 文字的解决方案

      因为手淘暂时并无对安卓作一个处理,因此,这里,只是对iphone作了一个处理

      即在html上,加入了一个自定义属性,data-dpr。

      <html data-dpr='dpr'></html>

      仍是以750的设计稿为例(即iphone6)

      假如设计稿上某a标签是32px,那么,咱们要这么写

      a{
      	font-size: 16px
      }
      /*iphone6*/
      [data-dpr='2'] a{
      	font-size: 32px
      }
      /*iphone6plus*/
      [data-dpr='3'] a{
      	font-size: 32px
      }
  4. 如今的一些问题

    正如咱们看到的,手淘目前的方案里面,是没有考虑到安卓dpr的问题的。即,这套方案,只对于iphone的r屏作了一个处理,而对于安卓,并无作dpr的处理。咱们来分析下缘由吧(我的拙见)。

    咱们但愿字体可以以px来展示,同时,咱们也但愿咱们的东西能对dpr作一个适配。对于ios,这天然是可行的,即采用了data-dpr的自定义 属性来调整文字。4到6写一套字体大小,6p写一套字体大小,而后在对dpr为1的屏幕写一套字体大小。其实这种写法仍是很恶心,不过基于对dpr的适 配,这样写也算是个解决方案了。

    不过一样的解决方案到安卓就不行了,安卓的dpr有时候会很乱(好比如今在goole的手机测试里面能够看到,安卓的dpr,lg的某些设备还采用了1.7那样的奇怪dpr)。而当1.7dpr这种不规范的数字出现的时候,咱们就不能采用以前的解决方案了,好比

    [data-dpr='1.7'] a{
    	font-size: 25px
    }

    这样的东西是不可能去写的,那万一还有2.25,2.5之类的呢?咱们都要拿去匹配么?

    其实如今,由于咱们经过devicePixelRatio能够获取到安卓的dpr值,便可以作到对安卓设备的dpr一个匹配。可是,文字若是采用px的话,确实是很难作到匹配的。

    即总结一下,就是说,对于安卓的dpr匹配,目前来讲,是没有什么问题的,可是,对于dpr匹配以后的字体,那确定是有问题的。

    常见的dpr下的字体,咱们依然能够解决,可是不常见的dpr,咱们确实很难作到对dpr的解决。那如何解决这些问题呢。目前以我本人这个不太灵光的脑壳,确实也不晓得该如何进行一个处理了,起码作不到很好的解决。

    不过,仍是丢上些我的的观点吧。

    在以前的对dpr的判断中,是根据了设备进行判断,即安卓不对dpr进行改变,仅对ios的设备进行改变。那么,咱们其实可不能够以dpr的值来作一个处理呢?即像这样写

    if (!dpr && !scale) {
    	//devicePixelRatio这个属性是能够获取到设备的dpr的
    	var devicePixelRatio = win.devicePixelRatio;
    	//判断dpr是否为整数
    	var isRegularDpr = devicePixelRatio.toString().match(/^[1-9]\d*$/g)
    	if (isRegularDpr) {
    	// 对因而整数的dpr,对dpr进行操做
     	if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {                
        	dpr = 3;
    	} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
        	dpr = 2;
    	} else {
        	dpr = 1;
    	}
    } else {
    	// 对于其余的dpr,人采用dpr为1的方案
    	dpr = 1;
    	}
    	scale = 1 / dpr;
    }

    咱们对这里作了一点点修改,即来判断dpr是不是规则的,也就是是不是咱们常见的1,2,3等,而后,咱们只对规则的dpr,来进行一个字体的处 理。这样,iphone依然仍是用以前的匹配方案。而其实目前安卓,不少的设备仍是比较常见的dpr了,因此咱们这里,将以前对设备的判断,转变成对 dpr是不是整数的一个判断。其余地方不变,能够解决对安卓dpr的部分匹配。

    一样,开发的时候,若是并不在意字体的问题的话,大能够直接使用rem。那样是能够作到dpr和文字都适配的问题。不过正如咱们讲到字体的时候所说的,使用rem是不少用户不但愿的(大屏机仍是和小屏机看到同样多的内容),同时,还有点阵的问题。

    好,东西写到这里,也将近到了尾声。第一次写这么长的东西,感受好累啊=_=。嗯还有篇2000字的反省要写,默默匿了去写反省了。

参考

手机淘宝的flexible设计与实现

题外话:

  1. iphone6plus颇有趣的地方

    iphone6plus照理来讲的,其实际dpr是2.87左右的,不过,为了方便开发者来开发,iphone6plus对其作了一个调整,将 dpr调整为3,而后在对屏幕进行了一个缩放。这样作,天然是方便了开发者前去开发,然而,这样作,也有了一些性能上的损失。(iphone为开发者考虑 的仍是挺周全的,看看隔壁安卓,dpr怎么爽怎么来,都特么本身玩本身的)

  2. 有意思的vh和vw

    vh,vw目前还存在很大程度的兼容性问题,因此还并无采用。

    vh,vw有什么特色呢

    这两个元素分别会把屏幕上的可视高度(说通俗点就是你手机屏幕那个框框头装起的东西),宽度,分红100份来看,好比先前咱们用rem来处理的地 方,咱们须要在html元素下写上font-size: 75px,而后再在div下写上width:1rem。而有了vh,vw以后,咱们如此处理html的font-size就好。

    html {
    	font-size: 10vw;
    }

    这样写,省去了一部js操做的步骤。

下面是个人公众号,你们能够关注一下,能够一块儿学习,一块儿进步:

 

转自:http://www.xiaoxiangzi.com/Programme/CSS/4298.html

相关文章
相关标签/搜索