2015年4月19日

[教學] jQuery學習筆記 第八堂-1(單頁式網頁設計:浮動導覽列垂直置中)


前陣子可以看到許多單頁式網頁的設計,單頁式網頁就是在一頁裡面把所有要傳達的訊息都傳達完,而網友可以透過捲動捲軸的方式,讓網頁產生類似輪撥的效果。

本堂課完成的結果就像這樣。
註:但也許是因為會控制網友右側捲軸的關係,使網友在使用上的控制感較差,所以近年來這種設計開始變少,取而代之的是視差滾動的效果,也就是再不控制滑鼠捲軸的情況下,當網友滑動捲軸時背後圖層可以產生連帶動畫,例如這個網站的設計fannabee

本次會學習到的函數共包含:

CSS
border-radius邊框變弧形,可用來製作圓形。
box-shadow製作邊框的陰影效果。
scrollTop:從頁面最上方到某捲軸位置的距離。

jQuery
$( ).css({"標籤" :"值","標籤" :"值"})針對某個標籤(包含class、id)修改css樣式。
$( ).height( ):擷取某個屬性的高度。
$( ).resize( ):當某個屬性(例如:視窗)被縮放時。
$( ).offset( ).top:距離最上面的距離,如果是要做左邊的距離,把top改成left。
$( ).scroll( ):當我轉動捲軸時產生效果。
console.log( ):可以透過console(chrome瀏覽器按F12)視窗產生訊息

基本版型設計


找圖

首先,我到Unsplash抓了五張好看的圖片,作為等一下輪撥式網頁的背景,我把這5張圖片都存到images這個資料夾當中。

由於這些下載下來的圖片都是原檔,檔案的容量很大,這會影響到我們之後網頁讀取的速度和順暢度,因此我先都用JPEGmini把檔案容量縮小(參考:縮小圖片檔案大小卻又不失品質的好方法─JPEGmini)。


把圖片放入網頁中

我的想法是利用背景圖片的方式來呈現這些圖片,而且我希望每一張圖片呈現的大小都會隨著瀏覽器的高度而變,占滿整個頁面,所以我把它分成5個區塊,分別取不同的class。


 然後在css的地方,我用background-image的方式放入背景圖片,background-size: cover設成cover,希望它能夠盡可能的放滿整個背景頁面。height:100vh的地方,我們用之前說明的vh來使用,意思是它的高度要是整個瀏覽器視窗的高度,而且這個高度可以隨著視窗的縮放而改變(參考:好用的css 3新單位vh vw ─ 讓你的圖片可以隨著螢幕大小而不同)。


透過上述這兩段程式碼,背景圖片就可以完整的呈現在整個頁面上了,而且既使視窗有縮放, 背景圖片還是會佈滿整個頁面,如下圖:


加上導覽標籤

導覽標籤的用意就是讓網友知道現在是在第幾頁,一般來說用個圓點就可以了。寫法如下:

我利用表單標籤<ul></ul>來建立表單,因為我們有5張圖,所以一共會有5組的標籤。


接下來,我想直接用CSS的方法來建立標籤(圓點)



這裡分了兩個部分:

在nav這個class中,我把位置設成fixed,位置則是設在右方,高度大約50%的地方(這樣做雖然可以隨著螢幕縮放而改變nav的位置,但是它並不是真的垂直置中,後面會在說明垂直置中的方式)。

在li這個標籤中,我們則是希望設定一個高和寬為10px的圓點,要變成圓點我們只需要使用border-radius這個指令就可以了,當border-radius的大小超過長和寬的一半時,它就變變成圓形box-shadow這個指令則是用來製造陰影的,因為我希望的我圓點更有立體感。list-style-type: none則是希望把原本ul標籤會有的標點拿掉。

完成後的結果會長的像這樣子:


透過jQuery讓導覽列垂直置中


在之前整理的CSS垂直置中的方法這篇文章中,我們已經說明了許多不同的垂直置中方法,這裡因為我們的position是設定成fixed的關係,所以並不適合使用margin+position或inline-block這些方法。那我們到底該怎麼辦呢?這時候只好用jQuery來解決這個問題了!

寫法是這樣的

匯入jQuery

一開始一樣要記得匯入jQuery

計算垂直置中的高度

接著我們的概念是這樣的,我們希望把nav這個class的高度設成是瀏覽器高度的一半,這時候top就應該設成是二分之一的視窗高度(window height)減掉二分之一的導覽列高度(nav height),如下圖:


接著,我們就把這樣的概念用jQuery的語法寫出來:


這裡我們設定一個變數,叫做pos,它的高度會是window(瀏覽器可視範圍)高度的一半,減掉導覽列高度的一半(註:為什麼是用window而不是body可參考這篇計算網頁底部位置),要擷取某個屬性的高度時,我們可以用$( ).height( )然後我們要用之前學過的$().css()這個語法,讓導覽列的高度會在導覽器的位置垂直置中。


但這麼做還不夠,因為當我們縮放視窗的時候,這個導覽列並不會隨著縮放而垂直置中,那麼該怎麼做呢?

當網頁縮放時仍能垂直置中

這時候我們會用到一個新的語法,叫做$( ).resize ( ),我們希望當我們的瀏覽器視窗被縮放時,會幫我們重新計算pos的高度。



這時候,不管你的視窗怎麼縮放,右邊的導覽列都會一直垂直置中了。

我們也可以讓我們的程式碼更簡潔一點,乾脆寫成一個叫做center的函數


點選導覽列移動到指定頁面


scrollTop

為了讓導覽列移動到指定的頁面,我們會用到上一堂課所學到的$( ).animate( )指令(參考: animate使用:製作輪撥式廣告),搭配上scrollTop這個語法。

那我們來看看要怎麼寫:


scrollTop基本的用法,就是告訴它你要到距離網頁頂端幾像素的。這段語法的意思是,當我點第一個按鈕時,移動到距離網頁頂端0像素的地方(也就是頁頂。至於我們要對誰移動到頁頂這個很重要,我們同時填了html和body,之所以要填兩個的主要原因在於,整個網頁在IE和Firefox中被稱做html,但在chomre中稱做body,所以如果你只填其中一個,都可能會讓其他瀏覽器失效。兩個中間則用「,」加以區隔就可以了。

這時候,雖然點選導覽列時,就會產生捲動旁邊捲軸的動畫,但是這麼做並沒有辦法讓我們到達想要的位置,因為我們背景圖片的高度在最上面的地方是設定vh,也就是會隨著視窗大小而變,這使我們沒辦法給定一個固定的px值,請它轉到那個位置。那麼我們可以怎麼解決這樣的問題呢?

.offset( )

為了解決這個問題,我們會用到$( ).offset( ).top,寫法是這樣:


我們可以把offset想成是「距離」,後面多了.top,也就是「到最上面的距離」,而前面有一個selector,分別是p01、p02、p03、...,這是我們div取的class名稱,所以第一段的語法的意思就會是p01這個div到最上面的距離,第二段的語法就是p02這個div到最上面的距離。

因為我們前面有加scrollTop,所以會分別移動到該div的位置。

寫成回圈

寫法如下:


首先,要知道導覽列中有幾個按鈕,可以用$( ).length這個語法,並且把它取名為變數num_li。

接著,我們用for( ){ }撰寫回圈,要再記得一次,for裡面是要用「;」間隔,接著我們要把i這個變數引入click當中,所以function的地方要寫成{id:i},funtion(e){},同樣的,這裡的id、e都是可以自己取的變數,data則是關鍵字。因為我們的圖片是從1開始編號,而不是從0開始編號,所以我把page這個引進i的變數都加上1,最後在把它放入scollTop後。

為了讓我們的動畫更完美,我們也可以加入一段$("html,body").stop()再for迴圈裡面的最上面,避免白木的網友一直點我們的游標,導致網站動畫秀斗。

根據哪個游標被點擊,來改變該游標的顏色


點選那個游標,哪個游標就變色

我們希望導覽列能夠根據我們目前所在的頁面來顯示不同的顏色,我們可以透過$( ).css( )來達到這樣的效果,寫法如下:


這裡分成兩個部分,我們先看下面那行,這行的意思是,當某個li被點到的時候,這個li的背景顏色要改變(這裡是變成綠色))。上面那行則是會了讓所有的圓點都變回白色(除了被點到的那個),沒有這行的話,最後會變成全都是綠色點。

第一個游標預設成被點擊的狀態

因為網友一進來就是看到第一頁,所以第一個游標照理來說一開始就是綠色的,因此我們要把第一個游標加上綠色:


根據捲軸位置,來改變該游標的顏色


再來這個部分就比較特別一些,我們希望導覽列的游標,除了被點擊時會變色外,也要能根據我滑鼠移到的圖片而改變,為了達到這個效果,我們會用到$( ).scroll( )這個指令,這個指令是當我轉動捲軸時能夠產生相對應的效果。同時,我們還要統整上面所學到的語法。

我們可以利用如下的語法達到我們想要的效果:

因為程式碼有點長,我們先分開來看,首先這一段
 if($(window).scrollTop()>=$(".p01").offset().top && $(window).scrollTop()<$(".p02").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(0)").css("background-color","#46dd46")
這段的意思指的是說,如果我目前視窗範圍的頂端($(window).scrollTop())大於等於第一頁的頂端($(".p01").offset().top)),且小於第二頁的頂端($(".p02").offset().top))時,則把第一個導覽列的點(".nav li:eq(0)")改成綠色。

接著第二段是
}else if($(window).scrollTop()>=$(".p02").offset().top && $(window).scrollTop()<$(".p03").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(1)").css("background-color","#46dd46")
意思指的是,如果我目前視窗範圍的頂端($(window).scrollTop())大於等於第二頁的頂端($(".p02").offset().top)),而且小於第三頁的頂端($(".p03").offset().top))時,則把第二個導覽列的點(".nav li:eq(1)")改成綠色。

其他的就是繼續這樣else if 下去,完整的程式碼長的像這樣:

$(window).scroll(function(){
                 if($(window).scrollTop()>=$(".p01").offset().top && $(window).scrollTop()<$(".p02").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(0)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p02").offset().top && $(window).scrollTop()<$(".p03").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(1)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p03").offset().top && $(window).scrollTop()<$(".p04").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(2)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p04").offset().top && $(window).scrollTop()<$(".p05").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(3)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p05").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(4)").css("background-color","#46dd46")
                }
            })
最上面的scroll則使指當瀏覽器在捲動時,幫我執行...的功能。

利用console.log( )

在進行這段語法的時候,我們可以利用console.log( )進行偵錯,之前雖然都是用alert( )來回報一些數值,但這裡是一捲動捲軸就要回報,如果用alert大概會沒完沒了。

console.log可以這樣用,例如我想知道p04這個div到網頁頂端的距離是多少($(".p04").offset( ).top),同時我也想知道,我的捲軸目前到最頂端的距離有多少($(window).scrollTop( )),就可以輸入下面那段console.log

                console.log("offset(p01):"+$(".p01").offset().top)
                console.log("offset(p02):"+$(".p02").offset().top)
                console.log("offset(p03):"+$(".p03").offset().top)
                console.log("offset(p04):"+$(".p04").offset().top)
                console.log("offset(p05):"+$(".p05").offset().top)
                console.log("scrollTop:"+$(window).scrollTop())


這時候用chrome預覽畫面時,按下F12,當我捲動捲軸時就會出現記錄了,這樣我就可以進行偵錯,也可以知道我想知道的訊息。


至於這種寫法要怎麼讓程式碼更精簡我目前還沒想到,若有人知道也歡迎留言告知!

結果


這次完整的程式碼如下:
<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <title>單頁輪撥式網站設計</title>
<!--    匯入jQuery    -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <script>
        $(document).ready(function(){
            
            //根據捲軸的位置改變右方導覽列游標的顏色
            $(window).scroll(function(){
                 if($(window).scrollTop()>=$(".p01").offset().top && $(window).scrollTop()<$(".p02").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(0)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p02").offset().top && $(window).scrollTop()<$(".p03").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(1)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p03").offset().top && $(window).scrollTop()<$(".p04").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(2)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p04").offset().top && $(window).scrollTop()<$(".p05").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(3)").css("background-color","#46dd46")
                }else if($(window).scrollTop()>=$(".p05").offset().top){
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    $(".nav li:eq(4)").css("background-color","#46dd46")
                }
            })
                     
            //點選右方導覽列時會到指定圖片
            var num_li=$("li").length
            for(i=0;i<=num_li;i++){
                $(".nav li:eq("+i+")").click({id:i},function(e){
                    $("html,body").stop()
                    $(".nav li").css("background-color","white")//除了被點擊到的游標,其他都恢復成原來的顏色
                    page=e.data.id+1
                    $("html,body").animate({"scrollTop":$(".p0"+page).offset().top})
                    $(".nav li:eq("+e.data.id+")").css("background-color","#46dd46")//被點擊到的游標變色,前面的selector用this也可以
                })
            }
            //一進入網頁時,將導覽列垂直置中計算導覽列置中的位置
           center()
            
           //縮放網頁時,將導覽列垂直置中
            $(window).resize(function(){
                center()
            })
            
            //計算導覽列垂直置中的高度
            function center(){
                pos=$(window).height()/2-$(".nav").height()/2
                $(".nav").css("top",pos)
            }
            
        })
    </script>
<style>
    *{
        padding:0;
        margin:0;
    }
/*    插入背景圖片    */
    .p01{
        background-image:url(images/p01.jpg);
        background-size:cover;
        background-repeat: no-repeat;
        height:100vh;
    }
    .p02{
        background-image:url(images/p02.jpg);
        background-size:cover;
        background-repeat: no-repeat;
        height:100vh;
    }
    .p03{
        background-image:url(images/p03.jpg);
        background-size:cover;
        background-repeat: no-repeat;
        height:100vh;
    }
    .p04{
        background-image:url(images/p04.jpg);
        background-size:cover;
        background-repeat: no-repeat;
        height:100vh;
    }
    .p05{
        background-image:url(images/p05.jpg);
        background-size:cover;
        background-repeat: no-repeat;
        height:100vh;
    }
/*    插入導覽列     */
    .nav{
        position: fixed;
        top:50%;
        right:0px;
        cursor:pointer;
    }
    li{
        width:10px;
        height:10px; 
        margin: 10px;
        background-color: white;
        border-radius: 5px;
        box-shadow: 1px 1px 1px rgba(0,0,0,0.5) inset,-1px -1px 1px rgba(0,0,0,0.5) inset;
        list-style-type: none;
    }
    h1{
        font-size:60px;
        color:ghostwhite;
        text-shadow:0px 0px 15px black;
    }
</style>
</head>
<body>
<div class="container">
    <div class="nav">
        <ul>
            <li style="background-color:#46dd46"></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>
    </div>
    <div class="p01"><h1>Page 1</h1></div>
    <div class="p02"><h1>Page 2</h1></div>
    <div class="p03"><h1>Page 3</h1></div>
    <div class="p04"><h1>Page 4</h1></div>
    <div class="p05"><h1>Page 5</h1></div>
</div>

</body>
</html>
寫的好辛苦阿!讓我們趕快來看一下結果吧!

以上內容均為本人在馬老師雲端研究室學習所整理之筆記

0 意見:

張貼留言