有選擇性的重複造一些輪子,未必是件壞事。Aaron的博客上加了一個懸浮菜單,貌似顯得很高大上了。雖然這類小把戲也不是頭一次見了,可是從未本身寫過。今天就選擇性的拿這個功能寫一寫。下面是這個輪子的開發過程,也能夠看成是一篇需求文檔的分析和實現過程。css
演示地址:http://sandbox.runjs.cn/show/to8wdmuyhtml
源碼下載:https://github.com/bjtqti/study/tree/master/floatmenuhtml5
第一步建立dom節構:node
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>AppCarrier</title> <link rel="stylesheet" href="menu.css"> </head> <body> <div id="content"> <h1 id="test1">test1</h1> <p>The past can hurt. But you can either run from it or learn from it</p> <p>過去是痛楚的,但你要麼逃避,要麼從中成長</p> <p>One meets his destiny on the road he takes to avoid it</p> <p>每每在逃避命運的路上,卻與之不期而遇</p> <p>Rules are meant to be broken</p> <p>規則就該被打破。</p> <p>Years may wrinkle the skin, but to give up enthusiasm wrinkles the soul.</p> <p>歲月流逝只令容顏蒼老,激情再也不卻使心靈枯萎。</p> <h1 id="test2">test2</h1> <p>只有不斷地練習學到的知識,你才能真正掌握它。</p> <p>Live every day to the fullest.</p> <p>盡享每日。</p> <p>Keep your eyes on the stars, and your feet on the ground.</p> <p>志存高遠,腳踏實地。</p> <p>Always be up for an unexpected adventure.</p> <p>隨時準備開始一場意外冒險吧。</p> <p>Life is full of disappointment. You can't dwell on things. You have to move on.</p> <p>生活常不如意,別沉溺往事,要一往無前。</p> <p>I'm a free spirit. I can't be caged.</p> <p>個人靈魂是自由的,不應被束縛。</p> <p>Sometimes the heart sees what is invisible to the eye.</p> <p>目不見者,心可感之</p> <p>The simple things are also the most extraordinary things, and only the wise can see them.</p> <p>最平凡的事也是最非凡的事,只有智者才明白。</p> <h1 id="test3">test3</h1> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <h1 id="test4">test4</h1> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> <p>how many xxxxxx</p> </div> <div class="menu" id="menubar"> <p class="static">隱藏</p> <ul> <li><a href="#test1">test1</a></li> <li><a href="#test2">test2</a></li> <li><a href="#test3">test3</a></li> <li><a href="#test4">test4</a></li> </ul> </div> </body> <script src="menu.js"></script> </html>
第二步準備css文件:jquery
ul { list-style-type: none; } a { text-decoration: none; } /*文章內容區*/ #content { width:400px; margin: 0 auto; font-size: 2em; } /*懸浮菜單*/ .menu { position: fixed; top:20%; right: 0; width:200px; border: 1px solid gray; border-radius: 5px; } .menu li { height: 2em; line-height: 2em; } .red { color : red; } .hide { display: none; } /*隱藏懸浮菜單*/ .slideIn { transform : translate3d(202px, 0, 0); transition-duration : .5s; } /*顯示懸浮菜單*/ .slideOut { transform : translate3d(0, 0, 0); transition-duration : .5s; } .static { color:#007aff; text-align: center; } /*顯示懸浮球*/ .toShow { display: block; width: 4.8em; height: 2em; line-height: 2em; border-radius: .5em; border:1px solid gray; transform : translate3d(-5em, 0, 0); transition-duration : 1s; }
第三步開始編寫js代碼:css3
(function(doc){ //收集各章節的連接位置 var pos = [], //收集菜單上的項目 links = doc.getElementsByTagName('a'), //收集章節的標題 titles = doc.getElementsByTagName('h1'), //懸浮菜單 menu = doc.getElementById('menubar'), //當前選擇項 currentItem=null; //添加紅色樣式 var addClass = function (element){ currentItem && currentItem.removeAttribute('class'); element.setAttribute('class','red'); currentItem = element; }, //網頁被捲去的高: getScrollTop = function (){ return Math.ceil(document.body.scrollTop)+1; }, //計算滾動位置 startScroll = function (){ var scrollTop = getScrollTop(), len = titles.length, i = 0; //第一條 if(scrollTop>=0 && scrollTop<pos[0]){ addClass(links[0]); return; } //最後一條 if(scrollTop>=pos[len-1]){ addClass(links[len-1]); return; } //中間 for(;i<len;i++){ if(scrollTop > pos[i] && scrollTop < pos[i+1]){ addClass(links[i]); break; } } }; //點擊列表中的連接變色 menu.onclick=function(e){ var target = e.target || e.srcElement; if(target.nodeName.toLowerCase() === 'a'){ //列表項狀態指示 addClass(target); return; } if(target.nodeName.toLowerCase() === 'p'){ if(target.className == 'static'){ //隱藏菜單 this.className = 'menu slideIn'; setTimeout(function(){ target.className = 'static toShow'; target.innerHTML = '顯示'; },1000); }else{ //顯示菜單 target.className = 'static'; target.innerHTML = '隱藏'; this.className = 'menu slideOut'; } } } //計算各章節的初始位置 var ln = titles.length; while(--ln>-1){ //titles[len].offsetParent.offsetTop = 0; pos.unshift(titles[ln].offsetTop); } startScroll(); //監聽滾動 window.onscroll = function(){ startScroll() } })(document);
分析:git
1. 實現自動跳轉到指定節github
這一步能夠利用<a>標籤的錨功能來作,因爲html5之後不支持name 屬性(HTML5 不支持。規定錨的名稱。),因此考慮用ID來跳轉。數組
2. 標識懸浮菜單中的項屬於左邊內容中的哪一個章節。瀏覽器
這一步是難點,先簡單分析一下:
2.1 第一種狀況,就是人爲點擊菜單項。這個很容易,只要標識點擊的元素就能夠了。
2.2 第二種狀況,經過鼠標中鍵滾動或拖動滾動條,這個時候要關聯左邊內容和右邊菜單項,這是最難的地方。考慮分步實施,先易後難,各各擊破的策略。
2.2.1 先收集標題元素的座標高度。也就是全部的h1標籤的垂直高度。存入數組1.
2.2.2 收集菜單項中的a元素,存入數組2.
2.2.3 監聽滾動事件,判斷當前內容屬於哪一個菜單項。
作一步的時候,建議在稿紙上畫一個圖:
A1
****************
* A2
*
****************
* A3
*
****************
*
* A4
*
每滾動一次,就判斷當前滾動的距離是在哪個區間,若是是0到A1則是第1章,A1到A2則是第2章,以此類推。
關於元素的位置高度,我這裏簡單地用element.offsetTop來獲取,可能會存在兼容性問題,若是用jquery來作的話,應當是$('element').offset().top,
一樣的,滾動條的高度,我也是簡單的用了document.body.scrollTop來獲取,若是換成jquery的話,應當是$(window).scrollTop();
畫圖的做用是把抽象的問題具體化,幫助咱們思考,找出規律。也許稱爲「建模」會顯得高大上一些吧。
須要強調的是數組1和數組2中的關係應當是一一對應的。如<a href="#h1">對應的是<h1 id="h1">。
2.3 第三種狀況,直接進入頁面時的菜單狀態指示。好比經過index.html#h3這樣的地址進來,菜單中的h3應當要突出顯示。
3. 實現懸浮菜單的顯示和隱藏動畫。
3.1 這一步應當是比較簡單的,能夠考慮先作。利用css3的tramsform屬性就能夠了,簡單高效,跨瀏覽器的話,注意兼容。
注意transform : translate3d(x軸, y軸, z軸); 用3d是能夠利用硬件加速,增長動畫效果,可是功耗會增長,善用!第一個參數是控制左右方向,若是爲正,則表示向右移動,若是爲負則向左移動。這麼說實際上是不嚴謹的,實際上應當根據參考點來肯定,好比元素的靜止時的x座標是0,那麼增長x的值向右,減小爲向左,0爲復位。
分析完以後,就是編寫代碼了。這沒有什麼好說的。盡情享受敲擊鍵盤產生的樂感吧。
寫完以後,預覽一下,點擊菜單,跳入指定章節,同時點擊項變紅色,刷新當前頁面,依賴顯示正確。滑動一下滾輪,菜單項隨着內容的變化而相應的變化,拖動一下滾動條,也是這樣,最後點擊一下隱藏,菜單縮回去,點擊顯示,菜單滑出來。這樣懸浮功能就作完了。