[ html5 ] 移动端适配方案 flex布局+rem布局

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

viewport视口

viewport是严格的等于浏览器的窗口。viewport与跟viewport有关的meta标签的关系,详细建议读一读这篇文章:移动前端开发之viewport的深刻理解,viewport与布局的关系,能够看下这篇文章:在移动浏览器中使用viewport元标签控制布局css

1. flex布局

flex简介

之因此把flex布局放在第一位,是由于它在某些场景下确实很方便,好比垂直居中,再或者某些栅格化的适配,一旦用习惯以后,不天然就很依赖这种布局了。html

传统布局,是基于盒子模型,严重依赖于display属性,float属性,position属性。
而flex布局能够更加简洁的实现各类布局场景,且已经被全部浏览器兼容,在移动端上应用起来更加驾轻就熟。前端


flex相关属性

flex属性内容比较少,只要理解应用起来很随意,学习详见阮一峰flexandroid

Flex布局父容器属性
flex-direction / flex-wrap / flex-flow / justify-content / align-items / align-content

水平(主轴上)对齐方式:web

justify-content:flex-start | flex-end | center | space-between | space-around;

flex-start(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,子元素间隔相等
space-around:子元素两侧的间隔相等

十字交叉轴上对齐方式浏览器

align-items:flex-start | flex-end | center | baseline | stretch

flex-start:上对齐
flex-end:下对齐
center:交叉轴对齐
baseline: 第一行文字的基线对齐
stretch(默认值):若是子元素未设置高度或设为auto,将占满整个容器

项目排列方向app

flex-direction:row | row-reverse | column | column-reverse;

row(默认值):从左1/2/3/...
row-reverse:从左../3/2/1
column:从上1/2/3/...
column-reverse:从上../3/2/1

换行方式iphone

flex-wrap:nowrap(不换行) | wrap(向下换) | wrap-reverse(向上换)

flex-flowsvg

flex-direction和flex-wrap的简写

flex-flow:row nowrap

多根轴线的对齐方式布局

align-content:flex-start | flex-end | center | space-between | space-around | stretch;

flex-start:上对齐。
flex-end:下对齐。
center:居中对齐。
space-between:两端对齐,间隔平均。
space-around:间隔相等。
stretch(默认值):占满。
Flex布局子元素属性
order/flex-grow/flex-shrink/flex-basis/flex/align-self
1.order/ (num) order定义自身排列顺序。数值越小,越靠前,默认为0。-1/0/1/2/3/...

2.flex-grow/ (num) flex-grow 定义自身放大比例,默认为0不放大。例如:1/2/1=25%:50%:25%

3.flex-shrink/  定义了空间不足时自身缩小比例,默认为1自动缩小,0不缩小。

4.flex-basis/ flex-basis定义最小空间,默认值为auto,即自身的原本大小。

5.flex属性/  flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

6.align-self属性/  align-self定义自身对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,若是没有父元素,则等同于stretch。
flex兼容
//Webkit内核的浏览器,必须加上-webkit前缀。
.box{
  display: -webkit-flex; /* Safari */
  display: flex;
}

2.rem布局

rem简介

rem是根据html的font-size大小来变化,正是基于这个出发,咱们能够在每个设备下根据设备的宽度设置对应的html字号,从而实现了自适应布局。

rem原理

rem布局的本质是等比缩放,通常是基于宽度。

假设咱们将屏幕宽度平均分红100份,每一份的宽度用x表示,x = 屏幕宽度 / 100,若是将x做为单位,x前面的数值就表明屏幕宽度的百分比.

若是想要页面元素随着屏幕宽度等比变化,咱们须要上面的x单位,不幸的是css中并无这样的单位,幸运的是在css中有rem,经过rem这个桥梁,能够实现神奇的x

经过上面对rem的介绍,能够发现,若是子元素设置rem单位的属性,经过更改html元素的字体大小,就可让子元素实际大小发生变化,若是让html元素字体的大小,恒等于屏幕宽度的1/100,那1rem和1x就等价了

rem应用

  1. js动态设置
//designWidth:设计稿的实际宽度值,须要根据实际设置
//maxWidth:制做稿的最大宽度值,须要根据实际设置
//这段js的最后面有两个参数记得要设置,一个为设计稿实际宽度,一个为制做稿最大宽度,例如设计稿为750,最大宽度为750,则为(750,750)
;(function(designWidth, maxWidth) {
	var doc = document,
	win = window,
	docEl = doc.documentElement,
	remStyle = document.createElement("style"),
	tid;

	function refreshRem() {
		var width = docEl.getBoundingClientRect().width;
		maxWidth = maxWidth || 540;
		width>maxWidth && (width=maxWidth);
		var rem = width * 100 / designWidth;
		remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
	}

	if (docEl.firstElementChild) {
		docEl.firstElementChild.appendChild(remStyle);
	} else {
		var wrap = doc.createElement("div");
		wrap.appendChild(remStyle);
		doc.write(wrap.innerHTML);
		wrap = null;
	}
	//要等 wiewport 设置好后才能执行 refreshRem,否则 refreshRem 会执行2次;
	refreshRem();

	win.addEventListener("resize", function() {
		clearTimeout(tid); //防止执行两次
		tid = setTimeout(refreshRem, 300);
	}, false);

	win.addEventListener("pageshow", function(e) {
		if (e.persisted) { // 浏览器后退的时候从新计算
			clearTimeout(tid);
			tid = setTimeout(refreshRem, 300);
		}
	}, false);

	if (doc.readyState === "complete") {
		doc.body.style.fontSize = "16px";
	} else {
		doc.addEventListener("DOMContentLoaded", function(e) {
			doc.body.style.fontSize = "16px";
		}, false);
	}
})(750, 750);
  1. css媒体查询
html {
    font-size : 20px;
}
@media only screen and (min-width: 401px){
    html {
        font-size: 25px !important;
    }
}
@media only screen and (min-width: 428px){
    html {
        font-size: 26.75px !important;
    }
}
@media only screen and (min-width: 481px){
    html {
        font-size: 30px !important; 
    }
}
@media only screen and (min-width: 569px){
    html {
        font-size: 35px !important; 
    }
}
@media only screen and (min-width: 641px){
    html {
        font-size: 40px !important; 
    }
}

上面的作的设置固然是不能全部设备全适配,可是用JS是能够实现全适配。具体用哪一个就要根据本身的实际工做场景去定了。

  1. 阿里团队优化了rem布局 开源了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;
    }

    docEl.setAttribute('data-dpr', dpr);
    if (!metaEl) {
        metaEl = doc.createElement('meta');
        metaEl.setAttribute('name', 'viewport');
        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);
        }
    }

    function refreshRem(){
        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;
    }

    win.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(refreshRem, 300);
    }, false);
    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'] = {}));

从上面的代码,主要是改变了dpx和document的font-size大小。大小为docEl.getBoundingClientRect().width / 10 + ‘px’;


移动端适配方案比较多,还有改变viewport暴力适配,和新出的属性vw,vh等等,我我的开发中经常使用的就是flex布局和js动态设置的rem布局,偶然也会加入vw,vh,百分比等等,灵活适配。 但愿对各位有些许的帮助。。。