原生JS实现全屏切换以及导航栏滑动隐藏及显示——重构前

2017-1-15更新:原生JS实现全屏切换以及导航栏滑动隐藏及显示——修改,这篇文章中的代码解决了bug。

思路分析:

  1. 向后滚动鼠标滚轮,页面向下全屏切换;向前滚动滚轮,页面向上全屏切换。切换过程为动画效果。
  2. 第一屏时,导航栏固定在页面顶部,切换到第二屏时,导航条向左滑动隐藏。切换回第一屏时,导航栏向右滑动显示。
  3. 页面显示的不是第一平时,当鼠标指针滑动到页面的头部区域,导航栏向右滑出;鼠标指针移出头部区域时,导航栏向左滑动隐藏。
  4. 当视口尺寸小于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             &lt;
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>
View Code

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    }
View Code

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 };
View Code

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我暂时还不知道怎么解决,不过与事件冲突比起来,还不是特别严重,也不影响使用。

暂时就想到这么多,接下来对代码进行重构,以便后期好维护。

posted @ 2016-11-09 11:16  Fogwind  阅读(7477)  评论(0编辑  收藏  举报