什么是响应式?响应式的页面在不同的屏幕有不同的布局,换句话说,使用相同的html在不同的分辨率有不同的排版。如下图所示:
传统的手机端适配常见有三种解决方案,种是bootstrap的columns布局;第二种是使用全局的rem,先根据屏幕换算1rem等于多少个px,然后设置html标签的font-size为多少个rem,屏幕越大,则font-size越大,然后页面所有的元素的宽高和字体大小都用rem等比例缩放;第三种是阿里的flex box,这种方案和第二种类似,不同点是页面内容的字体大小是用的px,而不是比例缩放的rem。种需要额外引入一个框架。第三种相对第二种来说应该更合理点,因为正文的字体常用的为14px或者16px,如果一个页面在这个手机字号是15.5px,在另外一个手机又变成了14.9px,这样可能会有点奇怪。
而使用响应式布局就不需要进行rem的换算,下面通过上图的那个例子一步一步地分析怎么做响应式。
1.设置不同分辨率页面两边留白
先一个页面的主体内容有大的宽度,当屏幕超过这个宽度时这个中间的主体内容大就这么大了,不会再变大了,也就是说它固定一个大宽度,然后居中显示,如大为1080px。然后当大于1024px时,页面主体内容小宽为960px,两边自动留白;在500px到1024px之间两边保持留白40px;而当小于500px时就认为是手机,两边留白20px。所以计算一下,container的代码如下:
2.屏幕变小时,一头变窄,另一头不变
当屏幕变小或者浏览器窗口拉小时,中间内容的宽度就不能保持1080px,它得跟着变小,而在变小的过程中,往往要保持一边不变,另一边随页面变窄,如下图所示:
3.保持中间留白固定,缩小内容宽度
左栏的宽度变小应该怎么变呢?有一个原则,就是要保持中间的间距固定,而两边的内容宽度相应缩小,如下图所示:
所以就要借助CSS3的calc,如下所示:1
2
3
input{
width:calc((100%-20px)/2)
}
calc的兼容性IE10及以上支持,android 4及以下不支持,所以考虑到不支持的设备,可以简单做个兼容,如下代码所示:
1
2
3
4
input{
width:48%;
width:calc((100%-20px)/2);
}
如果不支持calc就用48%,这样差别其实不是很大,就是不是很精确。真的需要的话,你可以多写几个媒体查询变得更精确。
4.左右布局变成上下布局
当屏幕拉得很小的时候,左栏已经缩得很小了,再变小就不协调了,所以这个时候要把左右布局改成上下布局,把右边的内容往下面放。因为右栏在大屏的时候是float:right,所以在中屏的时候覆盖掉这个浮动的属性,变成float:none就可以了。原本右栏的内容有四行,都比较短,可以考虑把它下面的三行排成一行,即让它们浮动。如下面代码所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.cal-result{
float:right;
width:330px;
}
1
media(max-width:800px){
.cal-result{
float:none;
width:100%;
}
.cal-result.result{
float:left;
width:33%;
}
}
让每一个result占1/3,然后浮动,效果如下:
5.宽度太小时,自动换行
特别是当内容是列表ul形式的时候,排不下的li应当自动换到下一行。当然也可以手动控制,如下:
1
2
3
4
5
6
7
8
9
10
11
media(max-width:800px){
.result{
width:33%;
}
}
1
media(max-width:400px){
.result{
width:50%;
}
}
在屏幕宽度小于400的时候,每个结果就占50%,这样就排成两行了。这也是一种常用的办法,但是在我们这个例子,如果数字比较小,在iPhone6 375px的屏幕上还是排得下的,如果能保持在一行相对比较美观。而且固定50%,如果当数字比较大时也有可能会有重叠的危险,这个也有办法,就是别写死宽度,而是写死min-width为50%,这样当内容比较长时,float的元素同一行排不下就会自动换行。但是知名还是要个办法让它能根据内容长度自动换行,当然可以用JS计算,但是有点麻烦。
这个时候flex就派上用场了,很简单,只要设置两个属性:
1
2
3
4
5
.result-container{
display:flex;
justify-content:space-between;
flex-wrap:wrap;
}
space-between让子元素挨着容器的两边等间距排列,而wrap属性让子元素自动换行,当容器宽度不够的时候,就有了以下的效果:
这样还有一个小问题,就是当内容如果刚刚好占满时,两个项之间就没有间距了,如下图所示:
1
2
3
.result:not(:last-child){
margin-right:10px;
}
效果如下:
还有从大屏变成成小屏的时候有些字号主要是标题的字号和间距要相应调小,这种变小是阶梯变化的,而不是像rem一样连续变化,而且这种阶梯一般只要有两个就够了,一个大屏的,一个小屏的。如果你需要做很多阶梯的话,那你的排版很可能有问题。
6.使用响应式图片
如相同的头图,在电脑上需要使用大图,但是手机上面使用小图就好了,不然会造成手机上加载慢浪费流量等问题,一个办法是使用backgound-image结合媒体查询,如下所示:
1
2
3
4
5
6
7
.banner{
background-image:url(/static/large.jpg);
}
media(max-width:500px){
background-image:url(/static/small.jpg);
}
这种方法的缺点是对SEO不太友好,因为如果使用img标签还可以写个alt属性。
第二种常用办法是使用img的srcset或者picture标签做响应式图片,这个我在《Effective前端7:加快页面打开速度》已经提到,这里不再重复。
这种响应式图片除了大小屏之外,还可以兼顾视网屏即dpr为2及以上的和普通屏dpr为1的屏幕,即在高dpr的屏幕使用2倍图,而普通屏幕使用1倍图。
7.其它问题处理
有些地方大小屏的排版差异比较大,例如有些内容大屏的时候是挨在一起,而小屏离得比较远,这个时候你可能得重复html,写两份的标签,大屏的时候隐藏掉小屏的html标签,小屏的时候隐藏掉大屏的html标签。并且这种情况不应该是常例,如果你经常要写两套,那说明你这个页面可能不太适合写响应式,还不如直接写两套呢。
还有个问题,有时候你可能要借助rem/transform:scale做大小缩放,但这一定是下策,我们的原则还是要保持字号和间距不变,当屏幕的跨度不是很大的时候。使用transform的后果是屏幕拉小的时候,内容跟着变小了,但是由于transform不会造成重排,它占据的高度还是那么大,下面的内容不会跟上来。这样就得手动计算内容的高度。另外如果使用rem,就和响应式的思想冲突了。如果页面的一部分字号使用了rem,另一部分字号使用了px,这样就不协调了,如果你全部写rem那就不需要使用响应式开发了。这个时候你可能要想一想,是不是UI出得有问题。让UI重新调整。
还有,有时候可能会用到高度的媒体查询,例如在高度小于多少的时候,不能让弹框超出页面的高度;在高度大于多少的时候,让footer的定位fixed在底部,不然footer的下面可能会留白。