移动端界面的适配


Posted in Javascript onJanuary 11, 2017

阅读目录

  • 适配的要求
  • 适配的方法,3个步骤
  • 适配中背景图片的处理
  • 适配的原理解析

摘要:在进行移动端界面的书写的时候,如果把宽度高度或者字体大小全部写死的话,那么在所有手机上看到的大小都一样,存在的问题就是同样大小的字体,或者一个盒子模型,

在大屏幕手机上看起来会有点偏小。比如iphone6PLUS。如果是做成适配的话,就很好的解决了这个问题,大屏幕显示的内容大一点,小屏幕显示的小一点。

所以今天做一个移动端页面适配的小小总结

适配的要求

1、在不同分辨率的手机上,页面看起来是自适应的。整体效果看起来比较和谐。不会说大屏幕上看起来特别小。小屏幕上看起来特别大

2、主要是关注字体,宽高,间距,图片大小等。

3、所提供的设计图一般是手机分辨率的两倍,才能方便做适配。

4、使用rem做单位,而不是传统的px

适配的方法,3个步骤

步骤1:

设置viewport,也就是平时写移动端页面都要加上的:

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

步骤2:

首先我们在我们的页面引入下面的flexible.js,

这段适配的js代码是拿淘宝的来用的。

适配的js代码的github地址如下:https://github.com/amfe/lib-flexible/blob/master/src/flexible.js。

步骤3:

页面上我们的css代码可以这样写,比如设计图给我们的尺寸是750*1000的。某个容器在设计图的宽度是150px*225px,那我们在css里面

宽度:150px/750px/10=150px/75px=2rem;

高度为:225px/75px=3rem;

一句话:布局的时候,各元素的css尺寸=设计稿标注尺寸/设计稿横向分辨率/10;

div{
 width: 2rem;
 height: 3rem;
}

通过上面的3个步骤,我们就可以将我们的移动端页面做成适配的了。

css换算方法

不过有一点,一直算来算去挺烦的。所以在写css的时候,最好使用css预处理器,比如sass、less来写,这样就方便很多了。

或者在sublimeText3中安装cssREM插件,正常书写px单位,然后编辑器自动帮你换算成rem.

cssREM插件的安装教程:https://github.com/flashlizi/cssrem

注意点:

容器的宽度高度我们用rem为单位,但是字体大小font-size我们还是用px,而不是用rem

原因:

flexible.js的作者winter是这样解释的:考虑到字体的点阵信息,一般文字尺寸多会采用 16px 20px 24px等值,若以rem指定文字尺寸,会产生诸如21px,19px这样的值,会导致字形难看,毛刺,甚至黑块,故大部分文字应该以px设置。

一般标题类文字,可能也有要求随屏幕缩放,且考虑到这类文字一般都比较大,超过30px的话,也可以用rem设置字体。

下面粘贴一下flexible.js的源码:加了注释

flexible.js

;(function(win, lib) {
 var doc = win.document;
 var docEl = doc.documentElement;
 var metaEl = doc.querySelector('meta[name="viewport"]');
 var flexibleEl = doc.querySelector('meta[name="flexible"]');
 var dpr = 0;
 var scale = 0;
 var tid;
 var flexible = lib.flexible || (lib.flexible = {});
 if (metaEl) {
 console.warn('将根据已有的meta标签来设置缩放比例');
 var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
 if (match) {
  scale = parseFloat(match[1]);
  dpr = parseInt(1 / scale);
 }
 } 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)); 
  }
 }
 }
 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;
 }
 //为html标签添加data-dpr属性
 docEl.setAttribute('data-dpr', dpr);
 if (!metaEl) {
 metaEl = doc.createElement('meta');
 metaEl.setAttribute('name', 'viewport');
 // 动态设置meta 
 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);
 }
 }
 //根据dpr和物理像素设置rem
 function refreshRem(){
 //getBoundingClientRect().width相当于物理像素
 var width = docEl.getBoundingClientRect().width;
 // width / dpr > 540等于独立像素
 if (width / dpr > 540) {
  width = 540 * dpr;
 }
 var rem = width / 10; // 将屏幕宽度分成10份, 1份为1rem. rem转化px计算公式=d*(width/10)
 docEl.style.fontSize = rem + 'px';
 flexible.rem = win.rem = rem;
 }
 // 监听窗口变化,重新设置尺寸
 win.addEventListener('resize', function() {
 clearTimeout(tid);
 tid = setTimeout(refreshRem, 300);
 }, false);
 // 当重新载入页面时,判断是否是缓存,如果是缓存,执行refreshRem()
 win.addEventListener('pageshow', function(e) {
 if (e.persisted) {
  clearTimeout(tid);
  tid = setTimeout(refreshRem, 300);
 }
 }, false);
 if (doc.readyState === 'complete') {
 doc.body.style.fontSize = 12 * dpr + 'px';
 } else {
 doc.addEventListener('DOMContentLoaded', function(e) {
  doc.body.style.fontSize = 12 * dpr + 'px';
 }, false);
 }
 refreshRem();
 flexible.dpr = win.dpr = dpr;
 flexible.refreshRem = refreshRem;
 flexible.rem2px = function(d) {
 var val = parseFloat(d) * this.rem;
 if (typeof d === 'string' && d.match(/rem$/)) {
  val += 'px';
 }
 return val;
 }
 flexible.px2rem = function(d) {
 var val = parseFloat(d) / this.rem;
 if (typeof d === 'string' && d.match(/px$/)) {
  val += 'rem';
 }
 return val;
 }
})(window, window['lib'] || (window['lib'] = {}));

适配中背景图片的处理

1、如何使用background-size

因为是使用了rem来做单位,我们在写移动端的背景图的时候,一般使用background-size来控制大小,那要怎么来换算呢?

换算单位如下:

background-size=背景图的大小/该设计图的宽度*10

打个比方:我的背景图是16*18,设计图是按照640的宽度来设计的。那么我的background-size值为

background-size: 16/640*10rem 16/640*10rem   也就是 background-size:0.25rem 0.28125rem;

通过这样控制之后,我们的背景图也做到了适配的效果

2、雪碧图的适配!!!!

刚开始做适配的时候,有一件事是比较头疼的,那就是雪碧图的适配,主要是background-size和background-position的配置比较烦。那么怎么进行在使用fexible.js的时候适配雪碧图呢,方法如下:

假如我有下面这张雪碧图,设计图给我的是按640的分辨率来做的。

这张雪碧图的大小为200px*458px

移动端界面的适配

假设现在我们要用的那个勋子的背景图。分为以下几步:

1、测量勋字这张背景图的大小,大小为:75px*85px移动端界面的适配

2、测量这个勋字在雪碧图的位置,也就是设置background-position:.经测量,他在雪碧图的位置为 x:-123px,y:-7px

3、对着张雪碧图进行换算:看下面代码:

知道了上面的尺寸,我们就行换算即可,将每个值除以640再乘以10   为什么这么算,可以看看源码

要使用这样雪碧图:

<!-- 长宽为: -->
width: 75/640*10=1.171875rem;
height: 85/640*10=1.328125rem;
<!-- background-size为 -->
<!-- 因为整张雪碧图的宽度为200px, -->
background-size: 200/640*10rem auto;
<!-- background-position为: -->
background-position: -123/640*10rem -7/640*10rem;

html:

<i class="item1"></i>

css:

.item1{
 width: 75/640*10=1.171875rem;
 height: 85/640*10=1.328125rem;
 margin: 20px auto;
 background: url('../images/itemBg.png') no-repeat;
 // 因为整张雪碧图的宽度为200px,
 background-size: 200/640*10rem auto;
 等于
 background-size: 3.125rem auto;
 // 该背景图在雪碧图的位置
 background-position: -123/640*10rem -7/640*10rem;
 等于
 background-position: -1.921875rem -0.109375rem;
 display: block;
}

因为换算比较麻烦,所以建议使用sass或者less来进行计算。具体效果我放在了github上,可以看看:

https://github.com/xianyulaodi/flexibleDemo

适配的原理解析

先来了解一些概念

在进行分析之前,首先得知道下面这些关键性基本概念(术语)。

物理像素(physical pixel)

物理像素又被称为设备像素,他是显示设备中一个最微小的物理部件。一个物理像素是显示器(手机屏幕)上最小的物理显示单元,在操作系统的调度下,每一个设备像素都有自己的颜色值和亮度值。

其实可以类比为分辨率。打个比方,一张图片有n多个很小很小个格子组成。

移动端界面的适配

盗图,哈哈

设备独立像素(density-independent pixel)

设备独立像素(也叫密度无关像素),可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css像素),然后由相关系统转换为物理像素。

所以说,物理像素和设备独立像素之间存在着一定的对应关系,这就是接下来要说的设备像素比。

可以理解为css像素,比如宽度为20px等等。

设备像素比(device pixel ratio ),简称dpr

设备像素比(devicePixelRatio简称dpr)定义了物理像素和设备独立像素的对应关系,它的值可以按如下的公式的得到:

设备像素比 = 物理像素 / 设备独立像素  

在javascript中,可以通过window.devicePixelRatio获取当前设备的dpr

css中的px可以看做是设备的独立像素,所以通过devicePixelRatio,我们可以知道该设备上一个css像素代表多少个物理像素。

例如,在Retina屏的iphone上,devicePixelRatio的值为2,也就是说1个css像素相当于2个物理像素。

再举个例子:iphone6中:

设备宽高为375×667,可以理解为设备独立像素(或css像素)。

dpr为2,根据上面的计算公式,其物理像素就应该×2,为750×1334。

是不是有点头晕了,可以看看这篇文章消化一下:http://div.io/topic/1092

理解了上面的概念,就比较好理解它的实现原理了

理解它的原理有两点:

1、了解利用meta标签对viewport进行控制

我们可以看看我们通常在head里面加的meta标签

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

该meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。

content="width=device-width,让viewport的宽度等于设备的宽度,如果不这样的设定的话,那就会使用那个比屏幕宽的默认viewport,会出现横向滚动条。

如果改变initial-scale的值,那么就可以让页面达到缩放

meta viewport 有6个属性,可以了解一下

width 设置layout viewport  的宽度,为一个正整数,或字符串"device-width"
initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
height 设置layout viewport  的高度,这个属性对我们并不重要,很少使用
user-scalable 是否允许用户进行缩放,值为"no"或"yes", no 代表不允许,yes代表允许

2、淘宝的移动端页面和flexible.js源码解析:

第一个要点:

淘宝触屏版布局的前提就是viewport的scale根据devicePixelRatio(设备像素比) 动态设置:

设备像素比的简单介绍:http://www.zhangxinxu.com/wordpress/2012/08/window-devicepixelratio/

来看一下flexible.js源码:

移动端界面的适配

移动端界面的适配

根据不同的像素设备比,来对页面进行不同的缩放。页面缩放的 scale=1/dpr ;

来看移动端淘宝接下来的三张图:https://m.taobao.com/#index

三星galasy S4 

data-dpr=1, 所以 initial-scale=1  (因为源码上 scale = 1 / dpr;)

移动端界面的适配

iPhone5:

data-drp=2,所以initial-scale=0.5

移动端界面的适配

iphone 6 Plus

移动端界面的适配

第二个要点:

动态设置html的font-size,html元素的font-size的计算公式,font-size = deviceWidth / 10。我们也可以看到上面三张截图的html里面的font-size是不同的

源码如下:

var width = docEl.getBoundingClientRect().width;
 if (width / dpr > 540) {
  width = 540 * dpr;
 }
 var rem = width / 10;
 docEl.style.fontSize = rem + 'px';
 flexible.rem = win.rem = rem;

其实flexible的实质就干了以下几件事

1、动态改写meta标签

2、给元素添加data-dpr属性,并且动态改写data-dpr的值。也就是动态改写dpr

3、给元素添加font-size属性,并且动态改写font-size的值

以上就是移动端适配的小小总结,之前只是直接用,没有好好的理解它的原理。发觉整理的时候,资料查下来也还是学到很多概念,学到挺多东西的。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
javascript不同页面传值的改进版
Sep 30 Javascript
jQuery EasyUI NumberBox(数字框)的用法
Jul 08 Javascript
js关闭浏览器窗口及检查浏览器关闭事件
Sep 03 Javascript
Javascript中string转date示例代码
Nov 01 Javascript
javascript实现倒计时并弹窗提示特效
Jun 05 Javascript
使用AngularJS实现可伸缩的页面切换的方法
Jun 19 Javascript
jQuery动态背景图片效果实现方法
Jul 03 Javascript
javascript实现在指定元素中垂直水平居中
Sep 13 Javascript
详解JS中定时器setInterval和setTImeout的this指向问题
Jan 06 Javascript
js实现简单的获取验证码按钮效果
Mar 03 Javascript
基于JavaScript表单脚本(详解)
Oct 18 Javascript
vue路由懒加载的实现方法
Mar 12 Javascript
bootstrapValidator bootstrap-select验证不可用的解决办法
Jan 11 #Javascript
HTML5 js实现拖拉上传文件功能
Nov 20 #Javascript
Bootstrap表单控件使用方法详解
Jan 11 #Javascript
老生常谈的跨域处理
Jan 11 #Javascript
bootstrap选项卡使用方法解析
Jan 11 #Javascript
常用的javascript设计模式
Jan 11 #Javascript
WebView启动支付宝客户端支付失败的问题小结
Jan 11 #Javascript
You might like
php 从数据库提取二进制图片的处理代码
2009/09/09 PHP
PHP 日,周,月点击排行统计
2012/01/11 PHP
Thinkphp中的volist标签用法简介
2014/06/18 PHP
PHP实现清除MySQL死连接的方法
2016/07/23 PHP
laravel获取不到session的三种解决办法【推荐】
2018/09/16 PHP
Jquery数独游戏解析(一)-页面布局
2010/11/05 Javascript
js调用后台、后台调用前台等方法总结
2014/04/17 Javascript
javascript html5移动端轻松实现文件上传
2020/03/27 Javascript
Bootstrap 粘页脚效果
2016/03/28 Javascript
JS学习之表格的排序简单实例
2016/05/16 Javascript
js仿搜狐视频记录片列表展示效果
2020/05/30 Javascript
jQuery使用DataTable实现删除数据后重新加载功能
2017/02/27 Javascript
vue2.0在table中实现全选和反选的示例代码
2017/11/04 Javascript
基于jQuery实现无缝轮播与左右点击效果
2018/05/13 jQuery
Node.js EventEmmitter事件监听器用法实例分析
2019/01/07 Javascript
在Vue项目中取消ESLint代码检测的步骤讲解
2019/01/27 Javascript
JS实现盒子跟着鼠标移动及键盘方向键控制盒子移动效果示例
2019/01/29 Javascript
koa2服务端使用jwt进行鉴权及路由权限分发的流程分析
2019/07/22 Javascript
layer.open回调获取弹出层参数的实现方法
2019/09/10 Javascript
Python自动化测试工具Splinter简介和使用实例
2014/05/13 Python
Python sqlite3事务处理方法实例分析
2017/06/19 Python
django多文件上传,form提交,多对多外键保存的实例
2019/08/06 Python
python Dijkstra算法实现最短路径问题的方法
2019/09/19 Python
python实现文法左递归的消除方法
2020/05/22 Python
Python函数的迭代器与生成器的示例代码
2020/06/18 Python
PyCharm+PyQt5+QtDesigner配置详解
2020/08/12 Python
scrapy-splash简单使用详解
2021/02/21 Python
为什么会有内存对齐
2016/10/10 面试题
利用异或运算实现两个无符号数的加法运算
2013/12/20 面试题
学习自我鉴定
2014/02/01 职场文书
职位说明书范文
2014/05/07 职场文书
党员个人公开承诺书
2014/08/29 职场文书
先进集体事迹材料范文
2014/12/25 职场文书
雷峰塔导游词
2015/02/09 职场文书
如何写辞职信
2015/05/13 职场文书
政协常委会议主持词
2015/07/03 职场文书