最近看到一篇关于网页适配的经典文章,有兴趣的可以点开看看。其主要介绍的是pc页面的适配问题,个人觉得是前端开发者必须面临的问题。下面我仿这个例子做了自己的一个demo。注意:下面的例子,其实就是我们以前面临的一个很常见的使用场景,中间是内容部分,必须在不同的设备上能正常显示,即使在1024*768的设备上。而两边在大屏幕上可能会留白,或者使用一个背景图片来填充。
实例1:使用background-position来适配网页
首先我们看看DOM结构:
<body id="body">
<div class="lay" id="lay">
<!--这里是背景层,其用于设置背景图片,一般会通过background-position来定位背景图片-->
<div class="lay2" id="lay2"></div>
<!--这里是内容层,其和背景层在水平方向上距离为background-position的水平距离,这要理解-->
<div class="lay1" id="lay1"></div>
</div>
<div class="nav"></div>
很简单的DOM结构,其中lay相当于我们以前常用的container,而lay2是用于设置背景图的,也就是说我们的背景图可以使用这个div来设置,而lay1就是我们的内容部分:
我们在看看CSS部分:
body{
min-width: 1000px;
}
/*这是lay的元素的定位,其相对于body来定位的,定位位置为(0,0)。该图片是10*800*/
.lay{
width: 100%;
min-width: 1000px;
position: absolute;
left:0;
top:0;
background: url(http://ossweb-img.qq.com/images/game/happy/3rd/bg.jpg) 0 0;
background-repeat:repeat;
border:2px solid green;
/*margin:0 auto;可以去掉,因为这里是100%*/
overflow: hidden;
/*这里必须设置,防止多加了border后出现垂直方向的空格*/
}
/*lay1,lay2相对于lay来定位的。lay2是背景,而lay1是内容*/
.lay1,.lay2{
width:1440px;
height: 800px;
position: absolute;
top:0;
}
.lay1{
margin:0 auto;/*内容层居中*/
border:2px solid blue;
}
/*这里是背景层,虽然是给lay2设置的背景层,但是实际上它是为lay1而设计的,因为它是用于为lay1显示的
*而且这种情况就像我们的知乎一样,中间是内容部分两边是空白的。但是可以为内容部分设置背景图,同时也可以为body设置背景层
*/
.lay2{
width:7668px;
border:2px solid red;
background:url(http://ossweb-img.qq.com/images/game/happy/3rd/bg1.jpg) no-repeat 247px 205px
}
注意:从上面你可以看到,所有的元素的left/top都是没有设定的,它都是通过js来动态计算的,我们看看主要的js计算部分:
function load() {
cliWidth = document.documentElement.clientWidth || document.body.clientWidth;
ostart = Math.floor(cliWidth / 2 - 720);
var nav_n=0;
for (var i = 0; i < lays.length - 1; i++) {
lays[i].style.left = ostart + 1440 * (i - 0) + "px";
};
//更新背景的位置,背景不需要移动ostart,背景需要往左移动background-position的水平值
lays[lays.length - 1].style.left = ostart +(- 247 - 1440 * nav_n) + "px";
//控制height
heightControl();
};
上面控制了所有的lay1,lay2,也就是背景层和内容层的left值,这样元素在水平方向上就可以正常显示了,我们在看看垂直方向是如何动态计算的:
function heightControl() {
cliHeight = document.documentElement.clientHeight || document.body.clientHeight;
//把body的高度设置为document.documentElement.clientHeight值
document.getElementById("body").style.height = cliHeight + "px";
//如果body的高度大于620px,lay_box的高度为body的高度,top为0,
if (cliHeight >= 620) {
lay_box.style.height = cliHeight + "px";
lay_box.style.top = "0";
/*
(1)首先一定要弄清楚,这个背景图片的尺寸是:10*800px
(2)(cliHeight - 800) / 2表示一开始这个背景图和lay1,lay2定位在一起的
(3)然后往上移动800px,得到(cliHeight - 800) / 2 - 800,这时候只有这个图片的下面一部分被显示
(4)图片显示顺序是:图片的下半部分->整个图片800px->图片的上一部分,这不就是repeat-y的作用吗
*/
lay_box.style.backgroundPosition = "0 " + ((cliHeight - 800) / 2 - 800) + "px";
for (var i = 0; i < lays.length; i++) {
lays[i].style.top = (cliHeight - 800) / 2 + "px"
}
} else {
/*
(1)相当于设置了一个高度的临界值,这个临界值为620px,对小于这个高度的clientHeight做特殊处理
(2)把包含元素lay的高度设置为710px,然后往body内部隐藏90px,所以显示在外面的就是620px。lay_box是相对于body来说的
(3)现在pc最小的适配是1024*768,也就是高度最小也是768px
(4)问题:为什么把lay_box设置为710px,这个值是如何计算出来的?
解答:我们很容易就会想到下面的修改,也就是把lay_box的高度设置为800px
lay_box.style.height = "800px";
lay_box.style.top = "0px";
但是,仔细想想你就会发现,因为这时候cliHeight都小于600px,那么你如果把高度设置为800px就会出现滚动条了。而且你可能想试试如下的代码:
lay_box.style.height = "800px";
lay_box.style.top = "-120px";
但是,这时候你就会发现虽然lay_box设置了overflow:hidden,但是高度还是太高了
*/
lay_box.style.height = "710px";
lay_box.style.top = "-90px";
//lay_box被body覆盖了90px,只有620px在外面
for (var i = 0; i < lays.length; i++) {
//所有的lay1,lay2的top为0。也就是紧紧跟着lay来定位的
lays[i].style.top = "0"
}
}
};
很显然,我们在页面的大小发生变化的时候依然需要动态计算元素的位置:
function Resizing(n) {
cliHeight = document.documentElement.clientHeight || document.body.clientHeight;
cliWidth = document.documentElement.clientWidth || document.body.clientWidth;
var onow = (cliWidth / 2 - 720);
//当前的位置
var re_n = Math.floor(onow - ostart);
//计算变化的位置
var ostart = Math.floor(onow);
//把当前的位置设定为ostart,而不是onload的时候的值
for (var i = 0; i < lays.length; i++) {
lays[i].style.left = lays[i].offsetLeft + re_n + "px"
};
//更新所有的layer的位置和颜色
heightControl();
};
当然,如果我们在内容部分继续添加其他的定位元素都是需要动态计算位置的,这一点要弄清楚:
<div class="lay1" id="lay1" style="border:2px solid blue;">
<!--左上角的‘腾讯游戏’图标-->
<a href="http://game.qq.com/" target="_blank" class="logo" title="腾讯游戏官方网站" id="logo">
<strong>腾讯游戏官方网站</strong>
</a>
<!--右下角的两个button按钮-->
<div class="m_btns" id="m_btns">
<a href="index.htm" class="replay" title="重新播放">
<strong>重新播放</strong>
</a>
<a href="movies.htm" class="morev" title="欣赏高清版本">
<strong>欣赏高清版本</strong>
</a>
</div>
</div>
<a href="javascript:void(0)" id="fd_icon" class="fd_icon" title="点亮快乐图标">
<strong>点亮快乐图标</strong>
</a>
比如logo就是相对于lay1进行定位的,我们看看其元素的CSS:
.logo {
position:absolute;
display:block;
width:97px;
height:96px;
top:0;
left:31px;
background-position:0 -37px;
}
其默认是定位在lay1的顶部的,但是在水平方向上和lay1的距离是31px,我们看看如何计算他的初始位置和resize后的位置:
logo.style.left = -ostart + 60 + "px";
我们可以清楚的看到,logo虽然相对于lay1来定位,但是其最后会定位到包含元素lay的左顶角向右60px处!我们再来看看其在垂直方向上的定位是什么?
如果页面高度大于620px,那么为了保证logo在垂直方向上处于顶部,可以使用下面的js代码:
logo.style.top = -(cliHeight - 800) / 2 + "px";
如果页面高度小于620px,这时候因为logo是相对于lay1来定位的,lay1是相对于lay来定位的,而且lay被body覆盖了90px,所以为了让logo显示出来,只要设置为如下的代码就可以了:
logo.style.top="90px";//这时候就能处于视口中了
我们再看看nav如何定位的:
.nav{
height:50px;
position: absolute;
width:100%;
left: 0;/*top是动态计算的*/
background-color: blue;
}
因为我们nav是相对于body来定位的,所以其top也是需要动态计算的:
如果页面高度大于620px,我们如下来定位:
nav_box.style.top=cliHeight-50+"px";//nav_box的高度是50px
如果body高度小于620px:
nav_box.style.top="570px";
至于为什么是570px可以这么理解,因为lay已经隐藏了90px,所以只有620px的可视区域,然而nav的高度为50px,所以可以计算得到其top为:620-50=570px
我们再来看看my_btns是如何定位的:
.m_btns {
position:absolute;
width:147px
}
a.replay,a.morev
{
display:block;
width:67px;
height:67px;
background-position:-550px -97px;
margin-right:13px;
float:left;
background-image: url(http://ossweb-img.qq.com/images/game/happy/3rd/png.png?d=20110914);
}
a.replay:hover {
background-position:-617px -97px
}
a.morev {
background-position:-684px -97px;
margin-right:0
}
a.morev:hover {
background-position:-751px -97px
}
my_btns的位置也是动态计算的,其水平方向的位置为:
m_btns.style.left = -ostart + cliWidth - 170 + "px";//也就是说靠右显示为170px,那么其实在resize的时候也要重新计算的,并保持相同
在垂直方向上,我们看看如何定位:
如果文档高度大于620px:
m_btns.style.top = cliHeight - 120 - (cliHeight - 800) / 2 + "px";//应该很容易理解,减去120是为了能够出现在可视区域内
如果文档高度小于620px:
m_btns.style.top = "590px";
m_btns是相对于lay1定位的,而且lay1有90已经被隐藏了,可是区域为620px。所以:(1)首先向下移动90px,这时候刚好显示(2)然后向下移动620可视区域,那么就和大于620可视区域一样的效果了。(3)然后大于620的时候减去了120,所以这里也减去120。所以结果就是90+620-120=590px
我们再来看看fd_icon的位置:
/*”点亮快乐图标“这个右上角的按钮*/
.fd_icon {
position:absolute;
display:block;
width:139px;
height:139px;
background-position:0 -164px;
top:0;
left:0
}
我们看看js如何计算left值:
fd_icon.style.left = cliWidth - 139 + "px";//也就是距离右边为139px,在resize中也必须是同样的距离
对于top的值的计算:
如果高度大于620px:
fd_icon.style.top = "0";//top一直是0
如果高度小于620:
fd_icon.style.top = "90px";//因为lay已经隐藏了90px,所以这里要设置为90px
下面给出这个DOM的一个图,通过该图你就会明白上面的计算公式了:
实例2:使用zoom和scale来适配PC页面
DOM结构如下:
<div class="warpper" id="warpper" style="border:1px solid red;">
<!-- day1 -->
<!--我们发现我们的dayBox的宽度是wrapper的两倍-->
<div class="dayBox" id='dayBox1' style="z-index:3;border:1px solid blue;">
<!--daySingle的宽度是dayBox的一半,所以其宽度和wrapper是一样的-->
<div class="daySingle s1_1">
<!--下面是day元素,其尺寸是1600*835px,也就是说其尺寸是固定不变的,而且你会发现其上有一个zoom的内联属性-->
<div class="day day1_1" id="day1_1">
<!--下面每一个元素都有一个class="em",同时下面每一个p元素都是一个Element-->
<p class="em" id="em1_1"></p>
<p class="em" id="em1_5"></p>
<p class="em" id="em1_2"><em class="em1_1_24_jpg"></em></p>
<p class="em" id="em1_3"></p>
<p class="em" id="em1_4"></p>
</div>
</div>
</div>
</div>
<!--下面是显示页码的DOM,背景图是http://ossweb-img.qq.com/images/up/2014/life/page.png-->
<div class="pageNo" id="pageNo">1</div>
<!--下面是显示切换按钮的元素,每一个a标签就是一个圆点-->
<div class="dayChangeBtns" id='dayChangeBtns'>
<a href="javascript:oo.dayCtl(0);"></a>
<a href="javascript:oo.dayCtl(1);"></a>
<a href="javascript:oo.dayCtl(2);"></a>
<a href="javascript:oo.dayCtl(3);"></a>
</div>
其中主要CSS如下:
/*这里为body设置了padding,可以用于设置导航栏等特殊用处*/
body {
position:relative;
padding-top:42px;
overflow:hidden
}
/*wrapper设计的宽度为100%;同时height也是100%,而且overflow:hidden*/
.warpper {
width:100%;
position:relative;
z-index:1;
height:100%;
overflow:hidden
}
/*pageNo是相对于body来定位的*/
.pageNo {
display:block;
position:absolute;
left:0;
top:60px;
width:87px;
padding-left:5px;
height:150px;
line-height:140px;
font-weight:bold;
color:#343835;
font-size:36px;
z-index:80
}
/*这是屏幕左边的竖向原点,相对于body进行定位的*/
.dayChangeBtns {
position:absolute;
top:300px;
left:20px;
z-index:80
}
/*每一个a标签就是一个圆点*/
.dayChangeBtns a {
background-position:-25px -151px;
display:block;
width:25px;
height:24px;
margin-bottom:20px
}
/*这里的width:200%,这一点一定要弄清楚,也就是他是视口宽度的两倍,同时absolute定位是相对于wrapper的。left/top都是0*/
.dayBox {
position:absolute;
left:0;
top:0;
height:100%;
width:200%;
z-index:1;
transition: all 0.3s linear;
-webkit-transform-style: preserve-3d;
-webkit-backface-visibility: hidden
}
/*daySingle是dayBox的50%,所以其宽度和wrapper是一样的*/
.daySingle {
width:50%;
height:100%;
float:left;
overflow:hidden;
border: 1px solid pink;
}
/*这里的width是定宽的,而且是1600px,高度也是固定的。*/
.day {
width:1600px;
height:835px;
margin:0 auto;
position:relative;
border: 2px solid blue;
}
/*第一天*/
.s1_1 { background:#d6dde7}
.s1_2 { background:#ffe400}
.s2_2 { background:#379ee1}
.s3_2 { background:#ff7d27}
.s4_2 { background:#6057ca}
/*em是相对于day来定位的,因为day是内容区块,而em是内容本身*/
.em {
position:absolute;
background-position:0 0;
background-repeat:no-repeat;
display:block
}
#em1_1 {
background-image:url(http://ossweb-img.qq.com/images/up/2014/life/em1_03.jpg);
width:1486px;
height:327px;
left:30px;
top:151px
}
#em1_2 {
background-image:url(http://ossweb-img.qq.com/images/up/2014/life/em1_24.png);
width:914px;
height:684px;
left:180px;
top:151px
}
.em1_1_24_jpg {
display:block;
width:914px;
height:684px;
background:url(http://ossweb-img.qq.com/images/up/2014/life/em1_24_jpg_07.jpg) no-repeat 0 327px
}
#em1_3 {
background-image:url(http://ossweb-img.qq.com/images/up/2014/life/em1_10.png);
width:422px;
height:250px;
left:1094px;
top:385px
}
#em1_4 {
background-image:url(http://ossweb-img.qq.com/images/up/2014/life/em1_12.png);
width:407px;
height:148px;
left:1050px;
top:641px
}
#em1_5 {
background-image:url(http://ossweb-img.qq.com/images/up/2014/life/em1_logo.png);
width:134px;
height:130px;
left:1077px;
top:15px
}
其主要做法就是根据设计稿主体部分的比例和屏幕主体部分的比例来进行相应的内容缩放:
//下面是Zoom的计算
oo.sizeCtr=function()
{
var cliWidth=document.documentElement.clientWidth||document.body.clientWidth;
var cliHeight=(document.documentElement.clientHeight || document.body.clientHeight)-oo.footerH;
//宽度是100%的计算,但是高度在计算比例的时候要减去footer的高度,因为这一部分内容不是正文内容。我们只是计算用于显示主体内容部分的比例
//和设计稿的比例
if(cliWidth<oo.minW) {
cliWidth=oo.minW;
}
//else if(cliWidth>oo.contW) cliWidth=oo.contW;
if(cliHeight<oo.minH){
cliHeight=oo.minH;
}
var cliBl= cliWidth/cliHeight;
if(cliBl>=oo.bl)//页面更宽
{
oo.zoom=cliHeight/oo.contH
}else //页面更窄
{
oo.zoom=cliWidth/oo.contW
}
document.body.style.height=cliHeight+(oo.footerH-42)+'px';
//body的height高度为cliHeight+oo.footerH-42,其中因为上面的cliHeight已经减去了footerH,同时body有默认的42px的padding-top也要减去
//FF使用scale,而IE使用zoom
var day=oo.days[oo.now1][oo.now2];
if(firefox){
day.style.cssText ='transform: scale('+oo.zoom+');-moz-transform-origin:0% 0%;';
} else {
day.style.zoom=oo.zoom;
}
}
下面给一张图你就明白了:
好了,根据比例来选择zoom的方式也完成了,如果你有兴趣可以仔细阅读参考文献,我觉得是非常非常好的一篇文章。同时,关于第二种方式你需要弄清楚:
首先,如果屏幕的长宽比和设计稿的长宽比差距很大的情况下,选择了高度zoom的话,那么表示宽度变化的比例很大,这时候margin:0 auto天然居中;反之,屏幕下边会有很大的空白处
参考资料:
网页如何自适应不同的分辨率界面?
聊聊PC端页面适配
Axure 设计网站原型时,网站宽度和高度怎么设置比较好呢?