移动端界面的适配


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 相关文章推荐
浏览器常用高宽的jquery插件
Feb 24 Javascript
js切换div css注意的细节
Dec 10 Javascript
深入理解JS中的变量及作用域、undefined与null
Mar 04 Javascript
jquery获取css的color值返回RGB的方法
Dec 18 Javascript
domReady的实现案例
Nov 23 Javascript
js微信分享实现代码
Oct 11 Javascript
jQuery选择器之子元素过滤选择器
Sep 28 jQuery
React通过redux-persist持久化数据存储的方法示例
Feb 14 Javascript
jQuery实现条件搜索查询、实时取值及升降序排序的方法分析
May 04 jQuery
JS实现查找数组中对象的属性值是否存在示例
May 24 Javascript
JavaScript实现打砖块游戏
Feb 25 Javascript
webpack5 联邦模块介绍详解
Jul 08 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正则表达式基本知识与应用详解【经典教程】
2017/04/17 PHP
PHP命名空间定义与用法实例分析
2019/08/14 PHP
客户端脚本中常常出现的一些问题和调试技巧
2007/01/09 Javascript
JavaScript在IE中“意外地调用了方法或属性访问”
2008/11/19 Javascript
Javascript attachEvent传递参数的办法
2009/12/14 Javascript
jQuery修改class属性和CSS样式整理
2015/01/30 Javascript
jQuery实现dialog设置focus焦点的方法
2015/06/10 Javascript
js实现省份下拉菜单效果
2017/02/15 Javascript
javascript基础知识之html5轮播图实例讲解(44)
2017/02/17 Javascript
浅谈jquery拼接字符串效率比较高的方法
2017/02/22 Javascript
JS操作input标签属性checkbox全选的实现代码
2017/03/02 Javascript
Angular 4环境准备与Angular cli创建项目详解
2017/05/27 Javascript
jquery.validate.js 多个相同name的处理方式
2017/07/10 jQuery
vue基于mint-ui的城市选择3级联动的示例
2017/10/25 Javascript
解读vue生成的文件目录结构及说明
2017/11/27 Javascript
Vue利用canvas实现移动端手写板的方法
2018/05/03 Javascript
jQuery发请求传输中文参数乱码问题的解决方案
2018/05/22 jQuery
VUE v-for循环中每个item节点动态绑定不同函数的实例
2018/09/26 Javascript
微信小程序云函数使用mysql数据库过程详解
2019/08/07 Javascript
vue父子模板传值问题解决方法案例分析
2020/02/26 Javascript
Python单例模式的两种实现方法
2017/08/14 Python
分享给Python新手们的几道简单练习题
2017/09/21 Python
python多线程同步之文件读写控制
2021/02/25 Python
python爬虫模拟浏览器的两种方法实例分析
2019/12/09 Python
mac 上配置Pycharm连接远程服务器并实现使用远程服务器Python解释器的方法
2020/03/19 Python
python能开发游戏吗
2020/06/11 Python
如何用python批量调整视频声音
2020/12/22 Python
The North Face意大利官网:服装、背包和鞋子
2020/06/17 全球购物
Java语言程序设计测试题选择题部分
2014/04/03 面试题
机电一体化专业应届生求职信
2013/11/27 职场文书
名企HR怎样看待求职信
2014/02/23 职场文书
初三新学期计划书
2014/05/03 职场文书
2014年秋季开学典礼主持词
2014/08/02 职场文书
2015年乡镇平安建设工作总结
2015/05/13 职场文书
七年级作文之秋游
2019/10/21 职场文书
《分一些蚊子进来》读后感3篇
2020/01/09 职场文书