对于移动端页面的开发自己很早就想做下简单的总结了,但感觉不知道该从何说起,有一天看了两篇网上一位作者的文章(后面资源里面会给出链接),觉得写得真的是很好,但是还有一些问题我不是很清楚,所以自己也查阅了相关的文献,结合前面那位作者的一些思路,写下了这篇总结。当然,有说的不对的地方,还望指出,谢谢。
移动端需求:给你一个750px的设计稿,上面画了一个200px的矩形框。
既然是写移动端页面,作为一名前端开发者,我想你们首先就会在里面敲下这样一句话:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0">
看上去只有那么熟悉了。这句话什么意思?它有什么作用呢?别着急,我们先来理解下viewport这个概念。
viewport 即视口,它的作用是限制页面的根元素即。在PC端,视口可以看成只有一个,即我们所看到的页面窗口,它的大小由浏览器窗口来决定。但是在M端视口分为两类:视觉视口(visual viewport)和布局视口(layout viewport),后文我会详细解释为什么移动端有这两个视口。(既然M端做了这样的划分,我们也可以对PC端采取同样的方式,即PC端viewport就是PC端的visual viewport)
所以,对于PC端来说,根元素的大小就可以由visual viewport来确定了。那么,怎么计算呢?下面分情况给出相应的计算方式:
html 的宽度 = document.documentElement.clientWidth = document.documentElement.offsetWidth = window.innerWidth
html 的宽度 = document.documentElement.clientWidth = document.documentElement.offsetWidth = window.innerWidth - 滚动条的宽度
html 的宽度 = document.documentELement.offsetWidth如此,PC端就可以按照其viewport的尺寸进行页面的布局。有了上面的基础,我们接下来看看M端页面。M端较之于PC端,一个显然的不同就是设备的尺寸。M端的屏幕宽度大多<=400px,不像PC端那样,动不动就是上千像素。这样PC端的页面到了移动端,可谓是不堪入目。
可以看到,在没有做M页面优化的情况下,PC端页面放到M端时,为了让你看清整个页面,强行把里面的内容进行缩放,以至于模糊不清,简直无法直视。(当然人家的页面是做了M端优化的,为了看效果我是把里面的给删掉了,哈哈哈,机智吧~)
既然移动端的视觉视口太窄(浏览器的宽度)而不能满足css布局的需求,那么就想办法弄一个更宽的视口出来,满足M端布局需求。此时,layout viewport应运而生。
布局视口比视觉视口要宽的多,M端就是根据布局视口来进行css布局的。而且在M端,元素的大小是由布局视口来确定的,就像我们之前谈到的PC端和视觉视口的关系那样。那么,布局视口到底有多大呢,以及他是怎样去计算的呢?
对于M端来说,其布局视口的大小取决于浏览器厂商,一些常见的布局视口取值如下表所示:

跟PC端有点类似,布局视口的大小可有下面的方式取得:
布局视口的宽度 = document.documentElement.clientWidth
布局视口的高度 = document.documentElement.clientHeight
有了这个布局视口的概念后,我们再来看看PC端页面放到M端,会呈现出怎样的情况。
看到了么,此时页面到了M端以后,不会强行的去缩放里面的内容,而是根据布局视口的大小去进行css布局,由于布局视口比视觉视口(浏览器的宽度)大很多,就出现了滚动条。所以,通过滚动页面你可以清楚的看到页面的整个内容了。但是,这还不是我们想要的M端页面,我们想要怎样一种效果呢?应该是这样:我们不想页面出现滚动条,而且想要页面所有的内容都呈现在我们的视野范围内。即要是我们能够自己去控制M端布局视口的大小就好了,让它以一个用户比较舒适的大小呈现在屏幕上,标签帮我们完成了这一举动,让我们继续看看吧。
最初是由苹果公司提出的,后来被多家浏览器厂商复制。其目的是为了调整M端布局视口的大小,以适应各种宽度的移动设备。该标签应该置于标签内,其语法如下所示:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0" user-scalable=no>
是不是很熟悉,对啦,我们在文章的开头时曾写过。关于viewport我们不再解释,下面简单说一下content里面的内容。content属性的值是一个字符串,字符串里面的内容是由逗号隔开的名值对,一共5个:
所以,列子中代码的含义就是,设置布局视口的宽度为设备的宽度,页面的初始缩放比例、最小缩放比例以及最大缩放比例值为1,不允许用户缩放页面。
好了,写到这里,大家或多或少对有了一定的了解吧,下面我们就开始真正的移动端页面开发了,哈哈哈,费了老大劲儿解释,但是我觉得很有必要吧。
&承接上面的需求,我们在750px的设计稿(其实这也是现在iPhone6/7的设计稿样子)上看到了一个宽高都是200px的绿色矩形框,于是我们这样设计代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>移动端页面开发</title>
<style>
* {
margin: 0;
padding: 0;
}
.weekend {
width: 200px;
height: 200px;
background-color: green;
}
</style>
</head>
<body>
<div class="weekend"></div>
</body>
</html>
咋一看,哎,不对呀,矩形框的宽高没有问题,但是设计稿上的设备宽度是750,而浏览器显示的宽度则是375,这是怎么回事呢。其实,设计稿是按照设备像素去设计的,所以在页面缩放比为1的情况下,根据公式:
DPR = 设备像素/视觉视口css像素个数(device-width)
在iphone7上DPR=2,所以2个css像素等效于2个设备像素,所以对于矩形框的宽度,应该设为200/2=100px,当然对于其他的DPR值,我们应该用设计稿上的设备像素/DPR,得到真正应该得到的 css 像素。
但是,如果我们不想每次都去计算真正的css像素值,而是让浏览器自动去计算呢。别忘了,我们前面提到的中content的 值initial-scale,它缩放的是css的像素,所以利用这个特性,设置initial-scale = 1/DPR,此时M端一个设备像素就等于一个css像素了,这样我们就可以直接将设计稿上的像素值设为css的像素值,而不用人为的去计算了。代码如下:
多说一句,利用 window.devicePixelRatio 可以获取设备的 DPR 值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>移动端页面开发</title>
<style>
* {
margin: 0;
padding: 0;
}
.weekend {
width: 200px;
height: 200px;
background-color: green;
}
</style>
</head>
<body>
<div class="weekend"></div>
<script>
var scale = 1/window.devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no')
</script>
</body>
</html>
这下好了,好像达到我们想要的效果了,可是为什么说好像呢,看到这里的朋友不知道有没有想过这样一个问题,假如现在你不是iphone7的设备,而是iphone5,他们的DPR值都为1,但是设备的宽度却是不一样的。然而我们的代码设计并没有考虑到这样的情况,我们来验证下这个想法,用iphone5看一下页面效果。
果然,iphone5上还是100px,这该如何是好呢?我们应该留意到了这样一个现象,随着设备的宽度在变化,因为我们设置了页面布局视口的宽度值等于device-width,而html的宽度又是由布局视口所决定的(document.documentElement.clientWidth),所以只要我们想到有什么办法能让页面中元素的宽度能够随着根元素的变化而变化,这样我们就能够达到想要的效果了。是的,没错,就是rem,我想对于rem这个概念应该并不陌生吧。简单来说,rem 是一个相对单位,是相对于html字体大小的单位。举个例子:
当我们设置 html {font-size: 10px} 时,设置页面某个 div {width: 2rem},此时div经过计算后的宽度就等于2*10即20px。所以,通过rem的这个功能,我们就可以实现我们想要的效果了。开始写吧,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>移动端页面开发</title>
<style>
* {
margin: 0;
padding: 0;
}
.weekend {
width: 2.666667rem;
height: 2.666667rem;
background-color: green;
}
</style>
</head>
<body>
<div class="weekend"></div>
<script>
var scale = 1/window.devicePixelRatio;
document.querySelector('meta[name="viewport"]').setAttribute('content', 'width=device-width, initial-scale=' + scale + ', user-scalable=no');
document.documentElement.style.fontSize = document.documentElement.clientWidth / 10;
</script>
</body>
</html>
代码修改的地方有两处,一个是script里面,添加了一个修改根元素fontSize的语句,为什么要除以100呢,因为直接取document.documentElement.clientWidth的值感觉有点略大了,这个标准孙便设计的,想怎么来怎么来。==但是有个地方我们得注意一下:==就是font-size的最小值问题,我们不能设置得太小否则无效== 。另外一个地方就是style里面css样式,因为是750px的设计稿,所以width值设置为200/(750/100),同样如果是640px的设计稿,就是200/(640/100),哈哈,大功告成。
终于,大功告成,真是不容易啊,可是,这个真的就是最佳的解决方案么?那可未必。你们看,我们在设置css的样式的时候,需要用设计稿上的样式去除以fontSize转化后的值,比如说750的设计稿,就要除以75;640的设计稿就要除以64,毕竟这些值不是什么容易除尽的值,所以计算起来还比较麻烦。那有没有什么解决方案呢?哈哈哈,办法总比困难多,根据网上有人提出的解决方案,完美的解决了这个问题,其实就一句话,我这里不做解释,留给各位自己去体会,相信在前面介绍的基础上,这段代码是很容易解释的,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="">
<title>移动端页面开发</title>
<style>
* {
margin: 0;
padding: 0;
}
.weekend {
width: 2rem;
height: 2rem;
background-color: green;
}
</style>
</head>
<body>
<div class="weekend"></div>
<script>
document.documentElement.style.fontSize = document.documentElement.clientWidth / 7.5 + 'px';
</script>
</body>
</html>
就一句话,巧妙的解决了移动端设计的问题,对于设计稿上的宽度,只要除以100即可。
终于,这篇文章算是完结了,花了自己一个周六的时间,当然里面关于最后移动端页面的设计思路是对别人文章的很大借鉴,但是整体很多地方都有自己的想法总结与移动端知识的延伸。其实,我们再来看M端,无外乎两个方面是我们需要考虑的问题:一个是移动端一个css像素和一个设备像素的对等关系,一个是不同设备的宽度自适应问题。知道怎么去解决这两个问题,那么你就算是基本上掌握了移动端页面设计的主要思想。
当然,不管是对别人借鉴也好,还是自己领悟也好,主要是自己对这部分知识点有所掌握,能够站在自己的角度独立完成这篇总结,对我而言也是极好的。里面有些说得不正确的地方,还望各位读者指出,谢谢~

