移动端界面的适配


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 相关文章推荐
Js,alert出现乱码问题的解决方法
Jun 19 Javascript
Jjcarousellite 实现图片列表滚动的简单实例
Nov 29 Javascript
js判断字符是否是汉字的两种方法小结
Jan 03 Javascript
JS使用ajax方法获取指定url的head信息中指定字段值的方法
Mar 24 Javascript
javascript实现十秒钟后注册按钮可点击的方法
May 13 Javascript
简单实现js倒计时功能
Feb 13 Javascript
前端开发之CSS原理详解
Mar 11 Javascript
阿里大于短信验证码node koa2的实现代码(最新)
Sep 07 Javascript
js实现页面多个日期时间倒计时效果
Jun 20 Javascript
js实现时钟定时器
Mar 26 Javascript
js根据后缀判断文件文件类型的代码
May 09 Javascript
JavaScript实现拖动对话框效果的实现代码
Oct 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转盘抽奖接口实例
2015/02/09 PHP
PHP日期函数date格式化UNIX时间的方法
2015/03/19 PHP
php实现的验证码文件类实例
2015/06/18 PHP
PHP中TP5 上传文件的实例详解
2017/07/31 PHP
PHP基于MySQLI函数封装的数据库连接工具类【定义与用法】
2017/08/11 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
图片格式的JavaScript和CSS速查手册
2007/08/20 Javascript
JQuery 选择和过滤方法代码总结
2010/11/19 Javascript
JQuery入门——事件切换之hover()方法应用介绍
2013/02/05 Javascript
js中arguments,caller,callee,apply的用法小结
2014/01/28 Javascript
使用jQuery实现返回顶部
2015/01/26 Javascript
Backbone.js框架中Model与Collection的使用实例
2016/05/07 Javascript
AngularJs 常用的过滤器
2017/05/15 Javascript
浅谈vue路径优化之resolve
2017/10/13 Javascript
vue组件中使用iframe元素的示例代码
2017/12/13 Javascript
如何从0开始用node写一个自己的命令行程序
2018/12/29 Javascript
Js生成随机数/随机字符串的方法小结【5种方法】
2020/05/27 Javascript
python使用点操作符访问字典(dict)数据的方法
2015/03/16 Python
Python pickle模块用法实例
2015/04/14 Python
RC4文件加密的python实现方法
2015/06/30 Python
python之DataFrame实现excel合并单元格
2021/02/22 Python
与Django结合利用模型对上传图片预测的实例详解
2019/08/07 Python
pygame实现烟雨蒙蒙下彩虹雨
2019/11/11 Python
Django 解决新建表删除后无法重新创建等问题
2020/05/21 Python
在Python中字典按值排序的实现方法
2020/11/12 Python
Belle Maison倍美丛官网:日本千趣会旗下邮购网站
2016/07/22 全球购物
澳大利亚UGG工厂直销:Australian Ugg Boots
2017/10/14 全球购物
英国最大的自有市场,比亚马逊便宜:Flubit
2019/03/19 全球购物
J2SDK1.5与J2SDK5.0有什么区别
2012/09/19 面试题
2014年大学生就业规划书
2014/04/04 职场文书
安全宣传标语口号
2014/06/06 职场文书
公务员培的训心得体会
2014/09/01 职场文书
学校党的群众路线教育实践活动整改措施
2014/10/25 职场文书
2016年元旦致辞
2015/08/01 职场文书
2016简历自荐信优秀范文
2016/01/29 职场文书
详解用Python把PDF转为Word方法总结
2021/04/27 Python