移动客户端的开发类型(站在前端立场上来说),主要是三种:
Native App(原生APP),也就是完全使用移动设备系统语言写的客户端,iPhone iPad就是纯Object-C,安卓就是纯JAVA, 是性能最棒的开发方式,但灵活性不好。
Web App, 就是在移动浏览器里打开的,纯HTML+CSS+JS,说白了就是个网页,只不过非常的富应用,比如手机浏览器访问的GMAIL。就是在浏览器里打开的页面。IOS支持可以在桌面创建访问的快捷方式,但是说到底还是打开Safari跑。而且对设备硬件的接口什么的挺薄弱。
Hybrid App.[HTML5 in mobile devices] 。实际上是使用原生写了一个容器,然后使用HTML+CSS+JS来实现用户界面和交互。Web App的短处便可以克服(因为自己写的容器可以辅助暴露偏底层的接口,比如本地存储或者麦克风控制之类),同时比起纯原生的java或者object-c开发灵活性要高(更新可以更快更迅速,也不依赖于市场,因为说白了,就是自己下载更新网页资源。)实际上这种方式已经不限于移动端。豌豆荚其实是个pc端的hybrid app。
就目前来说,我们只需要考虑webkit内核的浏览器和chrome,uc,qq,小米手机浏览器就好了。移动端更新换代比pc快多了,兼容性问题会越来越少。但移动设备的尺寸不同,移动端需要做大量的适配工作。
一.移动端弹性布局适配
1.逻辑分辨率和物理分辨率
众所周知,手机屏幕分辨率是手机的重要参数之一。
大家都知道移动端设备屏幕尺寸非常多,碎片化严重。尤其是Android,你会听到很多种分辨率:480×800, 480×854, 540×960, 720×1280, 1080×1920,而且还有传说中的2K屏、4K、5k等。近年来iPhone的碎片化也加剧了:640×960, 640×1136, 750×1334, 1242×2208。
俗话说物理分辨率是硬件所支持的,逻辑分辨率是软件可以达到的。
物理尺寸是指屏幕的实际大小。大的屏幕同时必须要配备高分辨率,也就是在这个尺寸下可以显示多少个像素,显示的像素越多,可以表现的余地自然越大。而真正决定显示效果的,是逻辑像素尺寸。
我们在viewport中,获取到的,如"width-device",是逻辑像素。所以我们的适配是针对逻辑像素尺寸的。
dpi,表示的是每英寸所拥有的像素(pixel)数目,数值越高,即代表显示屏能够以越高的密度显示图像。当达到人眼的极限分辨率时,乔帮主给它取了一个很高端的名字——Retina。
2.rem
em单位是相对于父节点的font-size。CSS3新增了一个相对单位rem(root em,根em)。
这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。目前,除了IE8及更早版本外,所有浏览器均已支持rem。对于不支持它的浏览器,应对方法也很简单,就是多写一个绝对单位的声明。这些浏览器会忽略用rem设定的字体大小。
3.创建媒体查询
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
meta标签表示:强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览;
媒体查询还有很多参数,这里不再细述。
4.适配方案
我们采用rem进行移动端的适配。根据设计稿定高宽设计出来页面,然后转换为rem单位。
第一种:CSS
@media only screen and (max- 320px), only screen and (max-device-320px) { html { font-size:10px; } } @media only screen and (max- 640px), only screen and (max-device-640px) { html { font-size:20px; } } .test-div{ 10rem;}
那么这个.test-div的宽度在320px的分辨率下会是10 * 10 = 100px, 在640下是10 * 20 = 200px,从而达到了弹性缩放的目的。
但是这样做还是有2个问题:
①随着各种新手机的发布,分辨率也碎片化了,我们无法预知将来会出现的分辨率宽度,我们不可能把所有要兼容的分辨率写到css里。
②这样写只能做到页面适配不同的宽度,对于那种在各种屏幕上都要在一屏幕内显示的页面,就没有办法适配了。
第二种:CSS+js
比较理想解决适配的问题就得靠js了,思路非常简单,判断一下当前终端的宽度和设计稿宽度的比例,计算出需要缩放的倍数,然后根据这个倍数值改变html的字体大小即可。
如果需要横竖屏都适配,那么根据终端宽高比例较小的那一个来计算。用通俗的语言来说,如果终端屏幕比设计稿更加宽矮一些,那么久根据它和设计稿的高度比例来计算字体。插件代码如下:
/* # 按照宽高比例设定html字体, width=device-width initial-scale=1版 # @pargam win 窗口window对象 # @pargam option{ designWidth: 设计稿宽度,必须 designHeight: 设计稿高度,不传的话则比例按照宽度来计算,可选 designFontSize: 设计稿宽高下用于计算的字体大小,默认20,可选 callback: 字体计算之后的回调函数,可选 } # return Boolean; # xiaoweili@tencent.com # ps:请尽量第一时间运行此js计算字体 */ (function ($, window) { var init = function(option){ var count = 0, designWidth = option.designWidth, designHeight = option.designHeight || 0, designFontSize = option.designFontSize || 20, callback = option.callback || null, root = document.documentElement, body = document.body, rootWidth, newSize, t, self; !function () { rootWidth = root.getBoundingClientRect().width; self = self ? self : arguments.callee; //如果此时屏幕宽度不准确,就尝试再次获取分辨率,只尝试20次,否则使用win.innerWidth计算 if( rootWidth !== window.innerWidth && count < 20 ) { window.setTimeout(function () { count++; self(); }, 0); } else { newSize = getNewFontSize(designWidth,designHeight,designFontSize); //如果css已经兼容当前分辨率就不管了 if( newSize + 'px' !== getComputedStyle(root)['font-size'] ) { root.style.fontSize = newSize + "px"; return callback && callback(newSize); }; }; }(); orientchange(t); } //返回root元素字体计算结果 var getNewFontSize = function(designWidth,designHeight,designFontSize) { var scale = designHeight !== 0 ? Math.min(window.innerWidth / designWidth, window.innerHeight / designHeight) : window.innerWidth / designWidth; return parseInt( scale * 10000 * designFontSize ) / 10000; } //横竖屏切换的时候改变fontSize,根据需要选择使用 var orientchange =function(t) { window.addEventListener("onorientationchange" in window ? "orientationchange" : "resize", function() { clearTimeout(t); t = setTimeout(function () { self = self ? self : arguments.callee; }, 200); }, false); } var Mobileadapter = function(opt){ if (!opt) { throw("配置不可为空"); } var settings = $.extend({ designWidth: 640, designHeight: 1136, designFontSize: 20, callback: function (argument) { console.timeEnd("test") } }, opt); init.call(this, settings); } $.initMobileadapter = Mobileadapter; }(jQuery, window));
使用:
<script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script> <script type="text/javascript" src="mobileadp.js"></script> <script> $(function () { $.initMobileadapter({ designWidth: 650, designHeight: 1800, designFontSize: 20 }); }(jQuery, window)); </script>
几点问题:
①这段代码对viewport有要求,必须是width=device-width initial-scale=1,即窗口的大小是设备物理宽度(分辨率 / devicePixelRatio),并且禁止缩放。另外还有一种做法就是手机淘宝的做法,窗口大小是分辨率宽度,然后缩放倍数是1/devicePixelRatio,这里暂且不讨论。
②安卓上的问题。经过实测,有些安卓机器,使用1的viewport,在页面刚加载的时候。不管是读取window.innerWidth,还是doc的getBoundingClientRect().width,或者是body的clientWidth,都不是设备的物理宽度。所以使用setTimeout,异步100ms执行获取屏幕宽度的代码就准确了。
因为width=device-width initial-scale=1,documentElement的宽度又是100%,所以当这两个值相等的时候我们可以认为目前获取到的屏幕宽度是准确的。那么使用此条件作为判断条件,不断的setTimeout(fun(){}, 0)去判断,当此条件为真时改变documentElement的字体。可以尽可能快的执行目标代码。但是又万一这两个值一直不相等又不能无限的死循环下去,所以设置了一个尝试上限,到上限之后用窗口宽度来计算(缩放比例不对的话用户起码可以看到完整的页面)。在chrome下测试,执行40次代码的平均时间是230ms,考虑到安卓机的js引擎速度,将上限设为了20。
③建议将这段代码放到head里,第一时间计算好html的fontSize,避免重绘。如果你有有一些跟获取dom元素尺寸相关的操作,就得放到这个计算函数的回调里面了,这时候就不能放到head里(因为运行的时候dom都还没加载),只能放到底部或者doc的ready事件里了。最佳实践是有一个全屏的loading画面,当fontSize计算好了之后再把真正的页面展示出来。
4.基础适配器
以Chrome Emulation的Apple iPhone4为基础(最小屏幕)适配。
5.px与rem的转换
移动端设备的分辨率:iPhone设备分辨率宽度分别为640、750、828,现在的设计稿一般是使用640、750的宽度。实际开发时需要将宽高减半,包括字体。那么宽度为640px的设计稿对应的designFontSize就是20px。
将px转换为rem的公式:&rem=px /designFontSize * 1rem(&、px /designFontSize * 1rem(&、为数字);
在css编写中,我们可以通过函数计算出转换后的rem。如在sass编写rem转换函数如下:
// pixels to rems $designFontSize: 20px !default; //640px psd @function pxToRem($px) { @return $px / $designFontSize * 1rem; }
在编写css时直接量取psd设计稿的值,如:
.test-div{ pxToRem(60px);}
二.移动端页面开发
由于移动端的兼容性较好,尽量使用HTML5+CSS3。尤其CSS3。
CSS3,除了文字阴影(text-shadow)、盒子阴影(box-shadow)、圆角(border-radius)、背景渐变(background: linear-gradient(#000, #fff))、2D变换(transition)、动画(animation)等大家耳熟能详的常用属性外,还有如-webkit-mask、-webkit-text-stroke、-webkit-nbsp-mode、-webkit-tap-highlight-color、-webkit-box-reflect、-webkit-marquee、-webkit-box等。其中-webkit-box可以很好的布局。
三.移动端js:jQuery Mobile还是Zepto
jQuery Mobile和Zepto是移动端的js库。jQuery Mobile相当于PC端的jQuery UI,它提供了很多页面的UI库,能够很快的开发出漂亮的界面,适合公司没有UI设计师的前端开发人员来进行移动端的开发。Zepto相当于PC端的jQuery,它提供了很多方法和功能,能够很快的实现各种需求和功能,适合公司有UI设计师的前端开发人员来进行移动端的开发。
jQuery Mobile的缺点,主要有两点:一是重,二是UI限制太大。当然jQuery Mobile性能上没有zepto好。
zepto.js是一个专为mobile WebKit浏览器(如:Safari和Chrome)而开发的一个JavaScript框架。它标榜自己在其简约的开发理念,能够帮助开发人员简单、快速地完成开发交付任务。更重要的是这个JS框架,是超轻量级的,只有5KB。zepto.js的语法借鉴并且兼容jQuery。
若考虑移动端与WEB端的统一性选用jquery,单纯从移动端来讲zepto.js是首选。
四.移动端的模块化组件(大多基于Jquery)
1.整屏滚动组件
可以查看另外一篇文章《全屏滚动-jQuery插件实现》。
2.动画组件
使用预定义的动画Animate.css
①检测动画结束事件:
$('#yourElement').one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', doSomething);
②可以更改动画的持续时间,增加延迟或改变显示次数:
#yourElement { -vendor-animation-duration: 3s; -vendor-animation-delay: 2s; -vendor-animation-iteration-count: infinite; }
注意:一定要在CSS恬当的的前缀(webkit, moz等)代替“vendor”。
③添加延迟动画:
selector.delay(300).queue(function(){ //do something })
另外,可以使用move.js实现自定义动画。
3.拉拽刷新组件
4.touch对应的swipe事件组件。
-------------------------------------------------------------------------------------------------------------------------------------
完