1. 程式人生 > >如何使用js把網頁適配為一屏的幾個技巧

如何使用js把網頁適配為一屏的幾個技巧

最近看到一篇關於網頁適配的經典文章,有興趣的可以點開看看。其主要介紹的是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天然居中;反之,螢幕下邊會有很大的空白處

參考資料: