PC页面的人聊的最多的就是兼容,这是因为浏览器之间的差异引起的。而移动端是基本没有兼容的问题的,全是CSS3。可是适配
的问题随之而来。
手机适配,目前有四种方法:
- 固定高度,宽度自适应
- 固定宽度,viewport缩放
- 利用 rem 布局
- 利用 vw 布局
这四种方法的核心都是视口的确定
1.固定高度,宽度自适应
目前拉勾网使用的就是这种方式,只适合简单的web app,单位使用px
- (1)以
较小宽度
(如 320px)的视觉稿作为参照进行布局 - (2)固定屏幕为
理想视口宽度
- (3)
垂直方向
使用定值
- (4)
水平方向
混合使用定值和百分比
或者利用弹性布局(Felx)
最终达到“当手机屏幕变化时,横向拉伸或者填充空白的效果
这是一种典型的弹性布局:关键元素高宽
和位置
都不变,只有容器元素在做伸缩变换。哪个宽度需要调整的时候使用响应式布局
调调就行。对于这类app,记住一个开发原则就好:文字流式,控件弹性,图片等比缩放
Flex 布局语法教程
2.固定宽度,viewport缩放
设计图、布局视口、视觉视口使用一个宽度,浏览器帮我们完成缩放,单位使用px
即可。这种方法也很少见,了解即可
网易金币商城在使用这种方法
原理
这种方法需要根据屏幕宽度来动态生成viewport
,生成的 viewport 基本是这样:
640 是我们根据设计图定下的,0.5625 是根据屏幕宽度动态生成的。
生成的viewport告诉浏览器网页的布局视口使用 640px,然后把页面缩放成56%,这是绝对的等比例缩放。图片、文字等等所有元素都被缩放在手机屏幕中。
通过 js 动态设定 initial-scale
var fixScreen = function() {
var metaEl = doc.querySelector('meta[name="viewport"]'),
metaCtt = metaEl ? metaEl.content : '',
matchScale = metaCtt.match(/initial\-scale=([\d\.]+)/),
matchWidth = metaCtt.match(/width=([^,\s]+)/);
if ( metaEl && !matchScale && ( matchWidth && matchWidth[1] != 'device-width') ) {
var width = parseInt(matchWidth[1]),
iw = win.innerWidth || width,
ow = win.outerWidth || iw,
sw = win.screen.width || iw,
saw = win.screen.availWidth || iw,
ih = win.innerHeight || width,
oh = win.outerHeight || ih,
ish = win.screen.height || ih,
sah = win.screen.availHeight || ih,
w = Math.min(iw,ow,sw,saw,ih,oh,ish,sah),
scale = w / width;
if ( scale < 1) {
metaEl.content += ',initial-scale=' + scale + ',maximum-scale=' + scale + ', minimum-scale=' + scale;
}
}
}
3.使用rem布局
依照某特定宽度设定 rem 值(即 html 的 font-size),页面任何需要弹性适配的元素,尺寸均换算为 rem 进行布局;当页面渲染时,根据页面有效宽度进行计算,调整 rem 的大小,动态缩放以达到适配的效果
最典型的就是网易新闻和手机淘宝Flexible方案。
3.1 网易新闻方案
(1) 网易新闻的设计稿应该是基于iphone6/7/8,所以它的设计稿竖直放时的横向分辨率为
750px
,为了计算方便,取1rem=100px
为参照,那么html元素的宽度就可以设置为width: 7.5rem,于是html的font-size=deviceWidth / 7.5
(2) 媒体查询改变根元素的字体大小
媒体查询参考
/**
* view-port list:
320x480
320x568
320x570
360x592
360x598
360x604
360x640
360x720
375x667
375x812
393x699
412x732
414x736
480x854
540x960
640x360
720x1184
720x1280
800x600
1024x768
1080x1812
1080x1920
*/
@media screen and (max-width: 320px) {
html {
font-size: 42.667px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 321px) and (max-width: 360px) {
html {
font-size: 48px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 361px) and (max-width: 375px) {
html {
font-size: 50px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 376px) and (max-width: 393px) {
html {
font-size: 52.4px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 394px) and (max-width: 412px) {
html {
font-size: 54.93px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 413px) and (max-width: 414px) {
html {
font-size: 55.2px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 415px) and (max-width: 480px) {
html {
font-size: 64px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 481px) and (max-width: 540px) {
html {
font-size: 72px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 541px) and (max-width: 640px) {
html {
font-size: 85.33px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 641px) and (max-width: 720px) {
html {
font-size: 96px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 721px) and (max-width: 768px) {
html {
font-size: 102.4px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 769px) {
html {
font-size: 102.4px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
缺点:媒体查询不能完全枚举,只能大体覆盖。如果想要精确覆盖要通过js实现
document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5+ 'px';
- (3) 视口设置理想视口
- (4) 布局时,设计图标注的尺寸除以100得到css中的尺寸(单位用rem)
3.2 使用Flexible实现手淘H5页面的终端适配
lib-flexible
Flexible要点:
- (1) 第一步是根据设备像素比动态设置viewport的scale
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;
}
var metaEl = doc.createElement('meta');
var scale = isRetina ? 0.5:1;
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
document.documentElement.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
documen.write(wrap.innerHTML);
}
适配结果:
- (2)第二步,设置html元素的font-size:font-size = deviceWidth / 10,即
1rem=deviceWidth / 10(px)
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10 + 'px';
- (3) 布局的时候,各元素的css尺寸(rem)=设计稿标注尺寸(px)/设计稿横向分辨率/10
font-size可能需要额外的媒介查询,并且font-size不使用rem。详细看使用Flexible实现手淘H5页面的终端适配
注意!!!
最新版(amfe-lexible)已经不再修改 viewport ,而是统一使用理想视口。
3.3网易与老版手机淘宝的做法不同点
- (1) 淘宝需要动态
设置viewport的scale
,网易直接设置为理想视口
。Flexible中,只对iOS设备进行dpr的判断,对于Android系列,始终认为其dpr为1。也就是说在安卓
下,淘宝的视口设置和网易新闻是一样的, - (2) 网易的做法,rem值很好计算,淘宝的做法肯定得用计算器或者sass和less这样的css处理器
4.使用vw进行布局
vw
:是Viewport's width的简写,1vw
等于window.innerWidth
的1%
Flexible方案是通过JavaScript来模拟vw
的特性。到今天为止,vw已经得到了众多浏览器的支持,也就是说,我们完全可以考虑将vw单位运用于我们的适配布局中,当然你也可以继续使用rem
参考
MobileWeb 适配总结
更多阅读
移动端Web页面适配方案
使用Flexible实现手淘H5页面的终端适配
了解真实的『REM』手机屏幕适配