响应式布局方案

5次阅读

共计 7237 个字符,预计需要花费 19 分钟才能阅读完成。

说到响应式布局方案,我们首先需要了解视口这个概念
视口
在早期的时候我们没有专门针对手机尺寸写的页面,所以在用手机浏览页面的时候我们看到的都是专门针对 PC 端的页面,在这种情况下页面会被严重压缩,而且会极大的影响页面的结构和布局,为了解决这个 问题,苹果公司提出了视口的概念,因为我们早期的 PC 端的页面的版心一般是 960px,为了容纳这个页面,我们在手机建立一个虚拟的区域,大小一般为 980px,来容纳 PC 的页面,以方便用户来浏览页面
但是在如今移动端快速发展的时候,我们有了专门针对手机屏幕尺寸的的移动端页面,所以在写移动端的页面的时候我们需要调整视口的宽度,保证页面内容会在手机屏幕尺寸大小的页面上进行展示
<!– 告诉浏览器如何调整设备的视口大小 –>
<meta name=”viewport” content=”width=device-width ,initial-scale=1.0,user-scalable=no,maximun-scale=1.0,minimum-scale=1.0″>
<meta name=”viewport”>
<!– 告诉浏览器根据当前设备的尺寸调整视口的大小 –>
<meta name=”viewport” content=”width=device-width”>
<!– 初始缩放比例为 1,该声明和 width 是同样的效果,但是为了保证所有的浏览器都能兼容,所以我们都会写 –>
<meta name=”viewport” content=”initial-scale=1.0″>
<!– 设置用户不可以对页面大小进行缩放 –>
<meta name=”viewport” content=”user-scalbale-no”>
<meta name=”viewport” content=”maximum-scale=1.0,minimum-scale=1.0″>
媒体查询
媒体类型
媒体查询可以为不同的设备规定不同的样式,常用的一般有三种设备 screen(计算机屏幕, 该值是默认值)、print(打印预览)、all(所有设备)
@media screen
媒体属性
媒体属性用来规定在指定的符合某些条件的情况下为指定的元素设置样式,需要注意媒体属性必须用 () 包起来,否则无效,常用的媒体属性有
width 可视区域宽度,取值格式为指定宽度或者范围,如 @media (width: 900px){}height 可视区域高度,取值格式为指定宽度或者范围,如 @media (max-height: 900px){}device-width 设备宽度,取值格式为指定宽度或者范围,如 @media (max-device-width: 5000px) {}device-height 设备高度 取值格式为指定宽度或者范围,如 @media (max-device-height: 5000px) {}orientation 设备是竖屏还是横屏模式,可选值有 landscape(横屏)、portrait(竖屏),如 @media (orientation: landscape) {}aspect-ratio 可视区域宽高比,取值格式为水平像素 / 垂直像素,如 @media (device-aspect-ratio:16/9) {}device-aspect-ratio 设备宽高比,取值格式为水平像素 / 垂直像素,如 @media (device-aspect-ratio:16/9) {}
例如我们要针对某个设备的 body 在横屏时设置的背景颜色是黑色,在竖屏的时候背景是白色,那么可以使用以下写法
html, body {
height: 100%;
}
@media (orientation: landscape) {
body{
background-color:#000;
}
}
@media (orientation: portrait) {
body{
background-color:#fff;
}
}
逻辑操作符
媒体查询可以使用三种操作符,通过操作符配合媒体属性来判断是否载入媒体属性下的样式表

and 将每一个条件组合起来,只有当所有条件都成立时条件才成立,可以理解为 JavaScript 中的 &

not 对媒体查询的条件取反
or 只要有一个媒体属性条件成立就成立,可以理解为 JavaScript 中的 ||

配合逻辑操作符设置设备在可视窗口大小变化时背景颜色发生改变
html,body{
height: 100%;
}

@media screen and (max-width: 500px){<!– 在屏幕尺寸小于 500px 时 body 背景颜色为 skyblue–>
body{
background-color: skyblue;
}
}
@media screen and (min-width: 501px) and (max-width: 800px){<!– 在屏幕尺寸为 501-800px 时,背景颜色为灰色 –>
body{
background-color: #ccc;
}
}
@media screen and (min-width: 900px){<!– 在屏幕尺寸为 900px 以上时,body 背景颜色为 yellowgreen–>
body{
background-color: yellowgreen;
}
}
vw、vh 布局方案
在 CSS3 规范中引入了 vw、vh 单位,分别将视口划分为 100 份,一个 vw 单位相当于视口宽度的 1%,一个 vh 相当于视口高度的 1%,需要注意的是不同于百分比的布局方案,vw 和 vh 不受父元素宽高的影响,只由视口的大小决定同时还有两个单位 vmax、vmin,vmax 表示取视口宽度和高度中比较大的值,将其等分为 100 份,vmin 表示取比较小的值,将其等分为 100 份
<!doctype html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport”
content=”width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<style>
.box{
width: 100vmin;
height: 100vmax;
background-color:deepskyblue;
}
.box div{
width: 50vh;
height: 50vw;
background-color: mediumseagreen;
}
</style>
<title>vw、vh</title>
</head>
<body>
<div class=”box”>
<div/>
</div>
</body>
</html>
目前浏览器的支持情况

rem 布局
rem 是 css 中的一个单位,是根据根字体大小来设定的,也就是 html 的 font-size
在正常的 UI 设计稿件的时候一般设置大小为 640/750px 大小,我们一般选择将稿件等分为 20 份(20 份在设备大小和稿件大小一般可以除尽,我们尽量避免出现小数)
那么如果 UI 稿件为 640px 大小,每一份的大小为 32px,假设 UI 稿件上有一个 160*160 大小的元素,那么该元素占整个页面的 160/32 份
同时我们也将屏幕分为 20 份,以 Iphon 为例(屏幕大小为 320px),那么每份大小为 16px,我们通过媒体查询将 html 根字体大小设置为 16px,那么在 UI 稿件上 160px 大小的元素在 Iphon5 上的实际大小为 160/32 rem,那么通过这个公式我们就可以通过 JavaScript 设置不同的根节点字体大小来进行适配
我们来写一个简单的例子
(function (doc, win) {
var docEl = doc.documentElement,
/*
document.documentElement
返回文档的根节点
* orientationchange 在用户移动端设备屏幕垂直或水平旋转移动设备时被触发
* resize 事件,在绑定元素大小发生变化时触发该事件,例如检测屏幕变化:
* window.addEventLisenter(“resize”,function(){
* alert(“ 屏幕大小变化了 ”)
* })
*
* resize 属性,css3 新增属性,用来指定用户是否可以缩放该元素
* none: 用户无法调整该元素尺寸
* both:用户可以调整元素的高度和宽度
* horizontal:用户可调整元素的宽度
* vertical: 用户可调整元素的宽度
* */
resizeEvt = “orientationchange” in window ? “orientationchange” : “resize”,
recalc = function () {
var clientWidth = docEl.clientWidth;
if (clientWidth >= 640) {
clientWidth = 640
}
if (!clientWidth) return;

docEl.body.style.fontSize = (clientWidth / 640) * 100 + “px”;
};
win.addEventListener(“resizeEvt”, recalc, false);
/*
* DOMContentLoaded 该事件会在 load 事件之前触发,在 DOM 树构建完成时就触发
* 而 load 事件则是在 DOMContentLoaded 事件触发之后,继续加载图片等外部文件完成后触发
* */
doc.addEventListener(“DOMContentLoaded”, recalc, false)
})(document, window)
之前一直在使用手淘的 rem 布局方案,可以大概看一下源码
;(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); //1
}
} 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);
//window.devicePixelRatio 该属性返回当前显示设备的物理像素分辨率与 css 像素分辨率的比值,该值也可以被解释为像素大小的比例
// 简单来说就是一个 css 像素的大象相对于一个物理像素大小的比值
// 可以通过重写 window.devicePixelRatio 来更改此属性,例如 window.devicePixelRatio=2;
//dpr css/ 物理 比例
//scale 缩放比例
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);
// 判断页面是否存在 metaEl
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;
//getBoundingClientRect
// 返回一个 DOMRect 对象,包含一组矩形的集合,该集合内是与该元素相关的 css 边框集合
/*
* DOMRect
* bottom:8
* height:8
* left:0
* right:520
* top:0
* width:520
* x:0
* y:0
* */
if (width / dpr > 540) {
width = 540 * dpr;
}
// 阿里的布局方案默认将屏幕分成十份,当然,如果愿意我们可以对其进行更改
var rem = width / 10;
docEl.style.fontSize = rem + ‘px’;
flexible.rem = win.rem = rem;
}
// resize 在设备宽度发生改变时触发
win.addEventListener(‘resize’, function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
//pageshow firefox/open 的一个事件,在 chrome 中不会触发
// 在页面后退时静态资源会直接重缓存中读取
win.addEventListener(‘pageshow’, function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
//document.readyState 描述文档的加载状态
/*
* loding 文档仍然在加载中
* interactive 文档已经加载完成并已经被解析,但是图像,框架之类的资源仍然在加载中
* complete 说有资源都已经加载完成,load 事件即将被触发
* 在状态改变时 document.readyState 事件将被触发
* */
if (doc.readyState === ‘complete’) {
doc.body.style.fontSize = 12 * dpr + ‘px’;
} else {
//DOMContentLoaded 在文档加载完成后触发,不会等待图像,框架等资源,参考 $(function(){}) / $.ready()
doc.addEventListener(‘DOMContentLoaded’, function(e) {
doc.body.style.fontSize = 12 * dpr + ‘px’;
}, false);
}

refreshRem();

flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
//rem 2 px 转化方法
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === ‘string’ && d.match(/rem$/)) {
val += ‘px’;
}
return val;
}
//px 2 rem 转化方法
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’] = {}));
阿里的布局方法主要是通过 dpr 来设置不同屏幕下的不同比例关系,具体的关于 dpr 的解释可以看下面这篇文章
移动 web 开发之像素和 DPR

正文完
 0