2017-1-15更新:原生JS實現全屏切換以及導航欄滑動隱藏及顯示——修改,這篇文章中的代碼解決了bug。
思路分析:
- 向后滾動鼠標滾輪,頁面向下全屏切換;向前滾動滾輪,頁面向上全屏切換。切換過程為動畫效果。
- 第一屏時,導航欄固定在頁面頂部,切換到第二屏時,導航條向左滑動隱藏。切換回第一屏時,導航欄向右滑動顯示。
- 頁面顯示的不是第一平時,當鼠標指針滑動到頁面的頭部區域,導航欄向右滑出;鼠標指針移出頭部區域時,導航欄向左滑動隱藏。
- 當視口尺寸小於768px時,切換頁面不隱藏導航條,但是導航條的項目要隱藏,通過點擊按鈕來顯示和隱藏項目。
本篇代碼是重構前的代碼。
HTML代碼:

1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>全屏滾動</title> 8 9 <style type="text/css"> 10 </style> 11 </head> 12 <body> 13 <header id="nav-head"> 14 <nav id="nav"> 15 <div id="navbar-header"> 16 <div id="logo-box"> 17 <!--<img id="logo-brand" src="喬巴.jpg" />--> 18 Fogwind 19 </div> 20 <button id="navbar-toggle" type="button"> 21 <span class="icon-bar"></span> 22 <span class="icon-bar"></span> 23 <span class="icon-bar"></span> 24 </button> 25 </div> 26 <!-- 27 <button id="navbar-slip" type="button"> 28 < 29 </button> 30 --> 31 <ul id="navbar-item" class="navbar-item-block navbar-item-none"> 32 <hr id="navbar-item-border" /> 33 <li class="navbar-list"> 34 <a href="http://www.battlenet.com.cn/zh/">戰網1</a> 35 </li> 36 <li class="navbar-list"> 37 <a href="http://www.battlenet.com.cn/zh/">戰網2</a> 38 </li> 39 <li class="navbar-list"> 40 <a href="http://www.battlenet.com.cn/zh/">戰網3</a> 41 </li> 42 <li class="navbar-list"> 43 <a href="http://www.battlenet.com.cn/zh/">戰網4</a> 44 </li> 45 </ul> 46 47 48 </nav> 49 </header> 50 51 <!--全屏滾動--> 52 <div id="full-page"> 53 <div id="page-box"> 54 <div id="page-one" class="page"></div> 55 <div id="page-two" class="page"></div> 56 <div id="page-three" class="page"></div> 57 <div id="page-four" class="page"></div> 58 </div> 59 </div> 60 <script> 61 </script> 62 </body> 63 </html>
CSS代碼:

1 *{ 2 margin: 0; 3 padding: 0; 4 } 5 html,body{ 6 height: 100%; 7 } 8 a{ 9 text-decoration: none; 10 } 11 /********************導航欄樣式**********************/ 12 #nav-head{ 13 height: 50px; 14 width: 100%; 15 position: absolute; 16 background: transparent; 17 z-index: 900; 18 } 19 #nav{ 20 background-color: #222; 21 height: auto; 22 position: fixed; 23 width: 100%; 24 top: 0; 25 z-index: 1000; 26 } 27 28 #logo-box{ 29 float: left; 30 height: 50px; 31 padding: 15px 15px; 32 margin-left: -15px; 33 font-size: 18px; 34 line-height: 20px; 35 color: #9d9d9d; 36 } 37 /*當log-brand是圖片時*/ 38 #logo-brand{ 39 display: block; 40 max-height: 35px; 41 } 42 /*導航欄右邊按鈕中的三道杠*/ 43 .icon-bar{ 44 display: block; 45 width: 22px; 46 height: 2px; 47 border-radius: 1px; 48 background-color: #fff; 49 } 50 #navbar-toggle .icon-bar + .icon-bar{ 51 margin-top: 4px; 52 } 53 /*導航欄列表ul*/ 54 #navbar-item{ 55 list-style: none; 56 } 57 #navbar-item-border{/*ul上部分割線,大屏時不顯示*/ 58 border: 0; 59 height: 1px; 60 background-color: #333; 61 } 62 .navbar-item-block{ 63 display: block; 64 overflow: hidden; 65 } 66 67 .navbar-list a{ 68 display: block; 69 color: #9d9d9d; 70 padding: 15px 15px; 71 line-height: 20px; 72 } 73 .navbar-list a:hover{ 74 color: #fff; 75 } 76 /*向左隱藏導航欄按鈕 77 #navbar-slip { 78 float: right; 79 }*/ 80 /*大屏時*/ 81 @media (min-width: 768px) { 82 #navbar-header{ 83 float: left; 84 height: 50px; 85 padding: 0 15px; 86 } 87 #navbar-item-border{ 88 display: none; 89 } 90 .navbar-list{ 91 float: left; 92 } 93 94 #navbar-toggle{ 95 display: none; 96 } 97 } 98 /*中小屏時*/ 99 @media (max-width: 767px) { 100 #navbar-header{ 101 display: block; 102 overflow: hidden; 103 height: 50px; 104 padding: 0 15px; 105 } 106 #navbar-toggle{ 107 float: right; 108 background-color: transparent; 109 border: 1px solid #333; 110 border-radius: 4px; 111 padding: 9px 10px; 112 margin-top: 8px; 113 margin-bottom: 8px; 114 } 115 #navbar-toggle:hover{ 116 background-color: #333; 117 } 118 119 .navbar-item-none{ 120 display: none; 121 } 122 123 /*#navbar-slip{ 124 display: none; 125 }*/ 126 } 127 128 /*************************全屏滾動樣式**************************/ 129 #full-page{ 130 height: 100%; 131 position: relative; 132 overflow: hidden; 133 } 134 #page-box{ 135 height: 100%; 136 width: 100%; 137 position: absolute; 138 } 139 .page{ 140 height: 100%; 141 } 142 #page-one{ 143 background-color: #6495ED; 144 } 145 #page-two{ 146 background-color: #B8860B; 147 } 148 #page-three{ 149 background-color: #8470FF; 150 } 151 #page-four{ 152 background-color: #D87093; 153 }
CSS代碼參考了Bootstrap的代碼。
這其中最關鍵的是html,body{height: 100%},這條樣式可以初始化body的高度為視口高度,即使它里面沒有內容。媒體查詢規定了小屏幕下的樣式。導航欄用了固定定位,全屏切換的每個頁面用div包裹,這個div絕對定位,通過控制其top屬性實現全屏切換。
JS代碼:

1 var bool = true;//存儲導航欄的狀態,顯示時為true,隱藏時為false 2 3 //跨瀏覽器的添加事件的函數 4 function addHandler(element, type, handler) { 5 if(element.addEventListener) { 6 element.addEventListener(type, handler, false); 7 } else if(element.attachEvent) { 8 element.attachEvent('on' + type, handler); 9 } else { 10 element['on' + type] = handler; 11 } 12 } 13 14 //跨瀏覽器的添加mousewheel事件的函數 15 function addMouseWheelEvent(element,func) { 16 17 if(typeof element.onmousewheel == "object") { 18 19 addHandler(element,"mousewheel",func); 20 } 21 if(typeof element.onmousewheel == "undefined") { 22 //alert(1); 23 //兼容Firefox 24 addHandler(element,"DOMMouseScroll",func); 25 } 26 } 27 /**********中小屏顯示/隱藏導航欄中項目的代碼***********/ 28 var navbarbtn = document.getElementById("navbar-toggle"); 29 //保存navbarbtn被點擊了幾次 30 navbarbtn.count = 0; 31 navbarbtn.onclick = function() { 32 var navbaritem = document.getElementById("navbar-item"); 33 if(navbarbtn.count === 0) { 34 //第一次點擊時顯示項目 35 navbaritem.className = "navbar-item-block"; 36 navbarbtn.count++; 37 } else { 38 //第二次點擊時隱藏項目,並重置navbarbtn.count 39 navbaritem.className = "navbar-item-none navbar-item-block"; 40 navbarbtn.count = 0; 41 } 42 43 }; 44 45 /*************向左隱藏導航條,向右顯示導航條****************/ 46 var nav = document.getElementById('nav'); 47 //分別用來保存導航欄開始滑動和結束滑動的時間 48 //利用兩者差值來判斷動畫效果是否完成 49 nav.startDate = 0; 50 nav.stopDate = 0; 51 //動畫效果完成所需的時間 52 nav.t = 300; 53 54 //向左隱藏 55 function navSlideLeft() { 56 if(nav.navmove) { 57 clearInterval(nav.navmove); 58 } 59 60 //獲取nav的計算樣式表 61 var computedStyle; 62 if(document.defaultView.getComputedStyle) { //DOM 2標准方法 63 computedStyle = document.defaultView.getComputedStyle(nav,null); 64 } else { 65 computedStyle = nav.currentStyle;//兼容IE方法 66 } 67 68 var width = parseInt(computedStyle.width), speed = width/(nav.t/10), left = parseInt(computedStyle.left); 69 //IE中computedStyle.left為auto 70 //下面的if語句用來兼容IE 71 if(!Boolean(left)) { 72 left = 0; 73 } 74 //如果nav沒有向左隱藏,執行向左隱藏代碼 75 //alert(width); 76 if(left > -width) { 77 78 nav.startDate = new Date(); 79 nav.navmove = setInterval(function() { 80 nav.stopDate = new Date(); 81 if(nav.stopDate - nav.startDate < nav.t) { 82 left += -speed; 83 //nav.style.left += left + 'px'; 84 } else { 85 left = -width; 86 //nav.style.left = left + 'px'; 87 clearInterval(nav.navmove); 88 } 89 nav.style.left = left + 'px'; 90 },10); 91 } else { 92 return; 93 } 94 } 95 96 function navSlideRight() { 97 if(nav.navmove) { 98 clearInterval(nav.navmove); 99 } 100 //獲取nav的計算樣式表 101 var computedStyle; 102 if(document.defaultView.getComputedStyle) { //DOM 2標准方法 103 computedStyle = document.defaultView.getComputedStyle(nav,null); 104 } else { 105 computedStyle = nav.currentStyle;//兼容IE方法 106 } 107 108 var width = parseInt(computedStyle.width), speed = width/(nav.t/10), left = parseInt(computedStyle.left); 109 110 //如果nav沒有向左隱藏,執行向左隱藏代碼 111 if(left < 0) { 112 113 nav.startDate = new Date(); 114 nav.navmove = setInterval(function() { 115 nav.stopDate = new Date(); 116 if(nav.stopDate - nav.startDate < nav.t) { 117 left += speed; 118 //nav.style.left += left + 'px'; 119 } else { 120 left = 0; 121 //nav.style.left = left + 'px'; 122 clearInterval(nav.navmove); 123 } 124 nav.style.left = left + 'px'; 125 },10); 126 } else { 127 return; 128 } 129 } 130 131 /*全屏滾動代碼*/ 132 var pageBox = document.getElementById('page-box'); 133 if(document.defaultView.getComputedStyle) { //DOM 2標准方法 134 pageBox.computedStyle = document.defaultView.getComputedStyle(pageBox,null); 135 } else { 136 pageBox.computedStyle = pageBox.currentStyle;//兼容IE方法 137 } 138 pageBox.startDate = 0; 139 pageBox.stopDate = 0; 140 pageBox.t = 300; 141 142 //獲取有幾屏 143 pageBox.pageChildren = pageBox.getElementsByTagName('div').length; 144 145 //切換計數 146 pageBox.num = 1; 147 148 //超時調用ID,優化mousewheel事件,防止連續觸發 149 pageBox.mousewheelTimer = null; 150 151 function pageSlideUp(num) { 152 if(pageBox.pageScroll) { 153 clearInterval(pageBox.pageScroll); 154 } 155 var height = parseInt(pageBox.computedStyle.height); 156 var top = parseInt(pageBox.computedStyle.top); 157 var speed = height/(pageBox.t/10); 158 pageBox.startDate = new Date(); 159 pageBox.pageScroll = setInterval(function() { 160 pageBox.stopDate = new Date(); 161 if(pageBox.stopDate - pageBox.startDate < pageBox.t) { 162 top += -speed; 163 } else { 164 top = -height*num; 165 clearInterval(pageBox.pageScroll); 166 } 167 pageBox.style.top = top + "px"; 168 },10); 169 } 170 171 function pageSlideDown(num) { 172 if(pageBox.pageScroll) { 173 clearInterval(pageBox.pageScroll); 174 } 175 var height = parseInt(pageBox.computedStyle.height); 176 var top = parseInt(pageBox.computedStyle.top); 177 var speed = height/(pageBox.t/10); 178 pageBox.startDate = new Date(); 179 pageBox.pageScroll = setInterval(function() { 180 pageBox.stopDate = new Date(); 181 if(pageBox.stopDate - pageBox.startDate < pageBox.t) { 182 top += speed; 183 } else { 184 top = -height*num; 185 clearInterval(pageBox.pageScroll); 186 } 187 pageBox.style.top = top + "px"; 188 },10); 189 } 190 191 192 function mouseWheelListener(event) { 193 194 event = event || window.event; 195 //獲取滾動方向 196 var wheelDelta; 197 if(event.wheelDelta) { 198 wheelDelta = event.wheelDelta; 199 } else { 200 201 wheelDelta = -event.detail;//兼容Firefox 202 } 203 //alert(wheelDelta); 204 //通過超時調用優化滾動事件 205 if(pageBox.mousewheelTimer) { 206 clearTimeout(pageBox.mousewheelTimer); 207 } 208 //當連續兩次滾動鼠標滾輪的時間間隔小於pageBox.t時,不觸發滾動效果 209 if((pageBox.stopDate - pageBox.startDate > 0) && (pageBox.stopDate - pageBox.startDate < pageBox.t)) { 210 //console.log(pageBox.stopDate - pageBox.startDate); 211 return; 212 } 213 //mousewheel事件與mouseover事件的時間間隔小於nav.t時,不觸發事件,防止事件沖突。 214 var nowtime = new Date(); 215 if(nowtime - navhead.leaveDate<nav.t) { 216 return; 217 218 } 219 220 //當滾輪向后滾動時 221 if(wheelDelta < 0) { 222 if(pageBox.num <= pageBox.pageChildren - 1) { 223 pageBox.mousewheelTimer = setTimeout(pageSlideUp(pageBox.num),20); 224 pageBox.num++;//最后等於pageBox.pageChildren,這里為4 225 //console.log(pageBox.num); 226 } else { 227 return; 228 } 229 } else {//當滾輪向前滾動時 230 if(pageBox.num <= pageBox.pageChildren && pageBox.num > 1) { 231 pageBox.num--; 232 pageBox.mousewheelTimer = setTimeout(pageSlideDown(pageBox.num-1),20); 233 //console.log(pageBox.num); 234 } else { 235 pageBox.num = 1; 236 return; 237 } 238 } 239 240 //隱藏導航條 241 /* 242 if(parseInt(pageBox.computedStyle.width) > 768 && event.clientY > 50) { 243 if(pageBox.num != 1 && bool) 244 245 navSlideLeft(); 246 bool = false; 247 } 248 if(pageBox.num == 1 && !bool) { 249 navSlideRight(); 250 bool = true; 251 } */ 252 253 //解決導航條進出切換bug,主要是兩次事件觸發的時間間隔小於動畫時間所致 254 //因為動畫效果由三個事件觸發:mousewheel,navhead的mouseover和pageBox的mouseover,事件之間有沖突 255 //包括代碼中的所有時間間隔的判斷都是為了解決此bug 256 //導航欄高度固定為50px 257 if(parseInt(pageBox.computedStyle.width) > 768 && event.clientY > 50) { 258 if(pageBox.num == 2 && bool) { 259 navSlideLeft(); 260 bool = false; 261 } 262 if(pageBox.num == 1 && !bool) { 263 navSlideRight(); 264 bool = true; 265 } 266 } 267 } 268 269 //給document添加鼠標滾動事件 270 addMouseWheelEvent(document,mouseWheelListener); 271 272 //保存超時調用ID,優化resize事件,防止連續觸發 273 var resizeTimer = null; 274 //視口寬度小於768時,導航條不隱藏,大於768時才隱藏 275 //同時保證全屏切換時,每一屏的高度始終等於視口高度 276 window.onresize = function() { 277 if (resizeTimer) { 278 clearTimeout(resizeTimer) 279 } 280 resizeTimer = setTimeout(function() { 281 pageBox.style.top = (-parseInt(pageBox.computedStyle.height)*(pageBox.num-1)) + "px"; 282 283 if(parseInt(pageBox.computedStyle.width) < 768) { 284 nav.style.left = '0px'; 285 } 286 if(parseInt(pageBox.computedStyle.width) >= 768 && pageBox.num != 1) { 287 //這里有點小bug,最小化再最大化,鼠標滑過頭部區域后導航條不消失 288 nav.style.left = (-parseInt(pageBox.computedStyle.width)) + 'px'; 289 bool = false; 290 } 291 292 293 },20); 294 }; 295 296 var navhead = document.getElementById('nav-head'); 297 navhead.overDate = 0; 298 navhead.leaveDate = 0; 299 navhead.onmouseover = function(event) { 300 event = event || window.event; 301 event.target = event.srcElement || event.target; 302 303 //防止navhead的子元素觸發事件(也可以阻止事件冒泡) 304 if(event.target.nodeName != this.nodeName) {//換成判斷id 305 return; 306 } 307 if(pageBox.num == 1 || parseInt(pageBox.computedStyle.width) < 768 ) { 308 return; 309 } 310 311 navhead.overDate = new Date(); 312 //console.log(navhead.overDate - navhead.leaveDate); 313 //這里的時間間隔判斷會產生一個bug 314 //當切換到下一屏時,如果指針足夠快划入頭部區域,導航條不出現,要滑出來等至少0.3s,才行 315 //如果你足夠快,讓指針在頭部與頁面之間來回切換,導航條始終不出現。 316 //下面pageBox的mouseover事件同理 317 if((navhead.overDate - navhead.leaveDate) > pageBox.t) { 318 if(!bool) { 319 navSlideRight(); 320 bool = true; 321 } 322 } 323 /** 324 325 326 327 //console.log(navhead.overDate - navhead.leaveDate); 328 if((navhead.overDate - navhead.leaveDate) > pageBox.t) { 329 if(parseInt(pageBox.computedStyle.width) > 768 && pageBox.num != 1) { 330 if(parseInt(nav.style.left) < 0) { 331 navSlideRight(); 332 } 333 334 335 } 336 }**/ 337 }; 338 339 pageBox.onmouseover = function(event) { 340 if(pageBox.num == 1 || parseInt(pageBox.computedStyle.width) < 768 ) { 341 return; 342 } 343 //console.log(123); 344 event = event || window.event; 345 navhead.leaveDate = new Date(); 346 //console.log(navhead.leaveDate.getTime()); 347 if((navhead.leaveDate - navhead.overDate) > pageBox.t) { 348 if(parseInt(pageBox.computedStyle.width) > 768 && pageBox.num != 1) { 349 if(bool) { 350 navSlideLeft(); 351 bool = false; 352 } 353 } 354 if(parseInt(pageBox.computedStyle.width) > 768 && pageBox.num == 1) { 355 if(!bool) { 356 navSlideRight(); 357 bool = true; 358 } 359 } 360 } 361 };
JS代碼比較亂,后期還要用面向對象思想重構。這里主要說說寫的過程中遇到的一些問題及bug。
1、以前寫輪播圖時,首先想到的都是用位置判斷動畫效果有沒有完成,這容易導致輪播效果不穩定。比如走着走着速度越來越快、圖片不是整張顯示而是跨張顯示或者圖片不見了等等,這些bug的產生與Javascript的定時機制有關。所以這次直接以前一次動畫函數與當前動畫函數的調用時間差來判斷動畫效果是否完成。
2、鼠標滾動事件在不同瀏覽器之間的兼容性。參考我以前的文章:mousewheel事件的兼容方法。
3、獲取元素計算樣式表的方法,主要是跨瀏覽器的兼容。這里碰到一個問題,一個元素的CSS屬性有很多,獲取到的計算樣式表中各個屬性的值在不同瀏覽器中差別很大,比如我沒有明確設置絕對定位的<nav>元素的left屬性,在獲取到的計算樣式表中,Chrome瀏覽器中left屬性為0px,Firefox和IE中則為auto,需要初始化為0px。所以在以后的項目中,如果要獲取元素的計算樣式表,一定要注意瀏覽器之間的差異。
4、通過超時調用優化mousewheel事件和resize事件,防止連續觸發。
5、如果使用了超時調用或間歇調用一定注意記得清除,特別是實現動畫效果的時候。
6、在這個項目中導航條的顯示/隱藏有三個事件可以觸發,為了解決事件之間的沖突,使用了各個事件之間的觸發時間差與動畫完成所需時間做判斷條件,等動畫效果完成才能觸發事件,否則不觸發。這雖然解決了事件沖突的bug,也帶來了一些其他的小bug。比如,先最小化再最大化視口,鼠標滑過頭部區域后導航條不消失;當切換到下一屏時,如果指針足夠快划入頭部區域,導航條不出現,要滑出來等至少0.3s,才可以(其實這兩個bug都是同一個原因導致的,就是兩次事件的觸發時間差小於動畫完成所需的時間)。這些bug我暫時還不知道怎么解決,不過與事件沖突比起來,還不是特別嚴重,也不影響使用。
暫時就想到這么多,接下來對代碼進行重構,以便后期好維護。