前言:javascript
如今移動互聯網發展火熱,手機上網的用戶愈來愈多,甚至大有超過pc訪問的趨勢。因此,用web程序作出仿原生效果的移動應用,也變得愈來愈流行了。這種程序也就是咱們常說的單頁應用程序,它也有一個英文縮寫,叫SPA; 它最大的特色就是能夠利用前端技術作出跨平臺的移動應用。技術難點在於理解虛擬頁面與物理頁面之間的變換關係。一個偶然的機會,我由php程序員轉爲web前端開發,主攻javascript編程,不知不覺,已經快兩年了。一直有一種想寫一個webapp應用框架的衝動,可是各類緣由,終究沒有付出實踐。因而打算從作一個簡單的webapp應用開始,萬事開頭難,今天就搭一個簡單的界面。php
HTML代碼:css
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>單頁應用</title> <link rel="stylesheet" href="css/common.css" type="text/css"/> </head> <body> <div class="container"> <header> <h3>sameple test </h3> </header> <ul class="root"> <li class="page">1</li> <li class="page">2</li> <li class="page">3</li> <li class="page">4</li> <li class="page">5</li> <li class="page">6</li> <li class="page">7</li> <li class="page">8</li> <li class="page">9</li> <li class="page">10</li> </ul> <div class="left">prev</div> <div class="right">next</div> <footer> <h4>(c)2015 by ouyangli</h4> </footer> </div> </body> <script type="text/javascript" src="lib/core.js"></script> </html>
css:html
1 ul , li { 2 margin: 0; 3 padding: 0; 4 list-style: none; 5 } 6 h3,h4,p { 7 margin:0; 8 padding: 0; 9 } 10 header { 11 position: absolute; 12 width:100%; 13 top:0; 14 left: 0; 15 z-index: 9; 16 } 17 18 header h3 { 19 text-align: center; 20 height: 3em; 21 line-height: 3em; 22 border-bottom: 1px solid green; 23 } 24 25 .container { 26 position: absolute; 27 width :320px; 28 height: 480px; 29 left:320px; 30 top:2em; 31 } 32 33 .root { 34 position: absolute; 35 width :100%; 36 height: 100%; 37 top :0; 38 left:0; 39 overflow:hidden; 40 -webkit-perspective:1000; 41 -webkit-user-select: none; 42 -webkit-transform-style:preserve-3d; 43 } 44 45 .page { 46 position: absolute; 47 width: 318px; 48 height: 100%; 49 overflow: hidden; 50 border:1px solid green; 51 } 52 53 .left { 54 left :2px; 55 } 56 .right { 57 right:2px; 58 } 59 60 .left,.right { 61 position: absolute; 62 top:45%; 63 width:3em; 64 height: 3em; 65 line-height: 3em; 66 text-align: center; 67 border-radius: 15%; 68 border:1px dashed blue; 69 } 70 71 .left:hover,.right:hover { 72 background-color: #33ff44; 73 cursor:pointer; 74 } 75 76 footer { 77 position: absolute; 78 width: 100%; 79 bottom: 0; 80 } 81 82 h4 { 83 height: 3em; 84 line-height: 3em; 85 text-align: center; 86 border-top: 1px solid green; 87 }
以上源碼將會在頁底提供打包下載,這裏貼出中間過程,只是想讓你們能明白,我是怎麼一步一步把這個程序寫出來的。若是有疑問的地方就給我留言好了,我會盡可能回覆。前端
演示地址:http://runjs.cn/detail/o4ql6f6ajava
細心的話,你會發現左上角有一個「亂碼」,其實那是由於全部的頁面都堆疊在一塊兒,形成頁數看不清了。這正是咱們接下來要解決的問題之一。git
js:程序員
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d('+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在右邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }());
好了,如今看起來僅管仍是很醜,可是至少已經符合我預期的樣子了。在這裏我把第一頁已外的頁面所有擺到了屏幕的右邊,你看不到它們。這樣作的目的是要模擬手機上的翻頁效果。接下來,就要實現這個很是使人期待的滑動翻頁效果。如今該javascript發揮威力的時候了。這是一個簡單的應用,我儘可能把全部的js寫在core.js中,並採用最普通的函數式編程。github
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d('+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在右邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }()); 15 16 //控制邏輯 17 ;(function(){ 18 //這裏直接使用了新的api,由於移動應用能夠這樣任性。 19 var pages = document.querySelectorAll('li'); 20 //左右翻頁按鈕 21 var left = document.querySelector('.left'); 22 var right = document.querySelector('.right'); 23 //取得全部的子頁面 24 var pagesLen = pages.length-1; 25 26 //標記當前頁面 27 var currIndex = 0; 28 29 //移動頁面 30 var move = function(index,pos){ 31 var width = 320 * pos; 32 var page = pages[index]; 33 page.style.transform = 'translate3d('+width+'px, 0, 0)'; 34 } 35 36 //向左翻頁 37 var toLeft = function(){ 38 39 if(currIndex!==pagesLen){ 40 move(currIndex,-1); 41 move(++currIndex,0); 42 } 43 } 44 45 //向右翻頁 46 var toRight = function(){ 47 if(currIndex!==0){ 48 move(currIndex,1); 49 move(--currIndex,0); 50 } 51 } 52 53 //監聽動做 54 left.onclick = function(){ 55 toLeft(); 56 } 57 58 right.onclick = function(){ 59 toRight(); 60 } 61 62 }())
如今咱們的程序能夠左右翻頁了,不過除了頁碼發生了變化以外,用戶好像感受不到有翻頁的效果。左右翻頁按鈕的做用與咱們指望的效果不一致,因此初始化時,把頁面堆在右邊實際上是不太好的,改成左邊會更好一些。嗯,還要繼續完善。web
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d(-'+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在左邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }()); 15 16 //控制邏輯 17 ;(function(){ 18 //這裏直接使用了新的api,由於移動應用能夠這樣任性。 19 var pages = document.querySelectorAll('li'); 20 //左右翻頁按鈕 21 var left = document.querySelector('.left'); 22 var right = document.querySelector('.right'); 23 //取得全部的子頁面 24 var pagesLen = pages.length-1; 25 26 //標記當前頁面 27 var currIndex = 0; 28 29 //移動頁面 30 var move = function(index,pos){ 31 var width = 320 * pos; 32 var page = pages[index]; 33 page.style.transform = 'translate3d('+width+'px, 0, 0)'; 34 page.style.transitionDuration = '300ms'; 35 } 36 37 //上一頁 38 var toLeft = function(){ 39 if(currIndex >0){ 40 move(currIndex,-1); 41 move(--currIndex,0); 42 } 43 } 44 45 //下一頁 46 var toRight = function(){ 47 if(currIndex < pagesLen){ 48 move(currIndex,1); 49 move(++currIndex,0); 50 } 51 } 52 53 //監聽動做 54 left.onclick = function(){ 55 toLeft(); 56 } 57 58 right.onclick = function(){ 59 toRight(); 60 } 61 62 }())
如今終於看起來像是在翻頁的樣子了,不過呢,咱們在手機上操做的時候,是能夠滑動翻頁的,這個效果天然也要支持才行。咱們先在pc上用鼠標拖動來模擬這一個過程,等邏輯跑通後,再加入觸摸事件便可。
繼續下一步以前,咱們先小結一下:
首先在初始化代碼時,順道把全部不可見的頁面所有堆疊到左邊,放在左邊的緣由是由於咱們習慣右邊的按鈕做爲下一頁,因此這樣擺放,實現起來最簡單,我固然要選擇最有利於我編碼的方式來擺放啦。其次呢,每一次翻頁,其實都要移動兩個頁面。拿點擊下一頁按鈕來講,首先要把當前頁移動到不可見的屏幕右邊,而後把上一頁移到屏幕中間來。上一頁的過程正好與之相反。咱們發現,點擊翻頁和滑動翻頁,其實都會調用相同的功能。因此在這裏可先抽出公共方法,方便代碼複用。
接下來,咱們實現鼠標拖動翻頁的效果。
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d(-'+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在左邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }()); 15 16 //控制邏輯 17 ;(function(){ 18 //這裏直接使用了新的api,由於移動應用能夠這樣任性。 19 var pages = document.querySelectorAll('li'); 20 //左右翻頁按鈕 21 var left = document.querySelector('.left'); 22 var right = document.querySelector('.right'); 23 //取得全部的子頁面 24 var pagesLen = pages.length-1; 25 //屏寬 26 var screenWidth = 320; 27 28 //標記當前頁面 29 var currIndex = 0; 30 31 //標記是否觸發滑動 32 var isTouch = false; 33 //記錄當前位置 34 var axis = { 35 x:0, 36 y:0 37 } 38 39 //移動頁面 40 var move = function(index,width,time){ 41 var page = pages[index]; 42 page.style.transform = 'translate3d('+width+'px, 0, 0)'; 43 page.style.transitionDuration = time+'ms'; 44 } 45 46 //上一頁 47 var toLeft = function(){ 48 if(currIndex >0){ 49 move(currIndex,-screenWidth,300); 50 move(--currIndex,0,300); 51 } 52 } 53 54 //下一頁 55 var toRight = function(){ 56 if(currIndex < pagesLen){ 57 move(currIndex,screenWidth,300); 58 move(++currIndex,0,300); 59 } 60 } 61 62 //監聽動做 63 left.onclick = function(){ 64 toLeft(); 65 } 66 67 right.onclick = function(){ 68 toRight(); 69 } 70 71 document.addEventListener('mousedown',function(e){ 72 isTouch = true; 73 axis.x = e.clientX; 74 axis.y = e.clientY; 75 }); 76 77 document.addEventListener('mousemove',function(e){ 78 if(isTouch){ 79 var distance = e.clientX - axis.x; 80 if(distance>0){ 81 //next 82 //此時須要看到實時移動效果,因此時間爲0 83 move(currIndex,distance,0); 84 //當前頁與上一頁之間老是相差一個屏寬 85 move(currIndex+1,distance-screenWidth,0); 86 }else{ 87 //prev 88 move(currIndex,distance,0); 89 move(currIndex-1,screenWidth+distance,0); 90 91 } 92 } 93 }); 94 95 document.addEventListener('mouseup',function(e){ 96 isTouch = false; 97 }); 98 99 }())
這一步咱們已經實現了拖動滑頁效果,可是感受怪怪的,對比一下手機上的滑動翻頁效果發現,真機上只要咱們滑動必定距離以後,頁面就自動翻過去了,而不是要咱們從一邊一直滑到另外一邊,這樣也太不實際了,並且若是咱們只滑了一點距離,那麼頁面會自動歸位,也就是常說的反彈效果。要實現這些也不難,咱們繼續完善代碼。
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d(-'+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在左邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }()); 15 16 //控制邏輯 17 ;(function(){ 18 //這裏直接使用了新的api,由於移動應用能夠這樣任性。 19 var pages = document.querySelectorAll('li'); 20 //左右翻頁按鈕 21 var left = document.querySelector('.left'); 22 var right = document.querySelector('.right'); 23 //取得全部的子頁面 24 var pagesLen = pages.length-1; 25 //屏寬 26 var screenWidth = 320; 27 //反彈時間 28 var time = 300; 29 //滑動距離 30 var distance=0; 31 //標記當前頁面 32 var currIndex = 0; 33 34 //標記是否觸發滑動 35 var isTouch = false; 36 //記錄當前位置 37 var axis = { 38 x:0, 39 y:0 40 } 41 42 //移動頁面 43 var move = function(index,width,time){ 44 var page = pages[index]; 45 page.style.transform = 'translate3d('+width+'px, 0, 0)'; 46 page.style.transitionDuration = time+'ms'; 47 } 48 49 //上一頁 50 var toLeft = function(){ 51 if(currIndex >0){ 52 move(currIndex,-screenWidth,time); 53 move(--currIndex,0,time); 54 } 55 } 56 57 //下一頁 58 var toRight = function(){ 59 if(currIndex < pagesLen){ 60 move(currIndex,screenWidth,time); 61 move(++currIndex,0,time); 62 } 63 } 64 65 //監聽動做 66 //prev 67 left.onclick = function(){ 68 toLeft(); 69 } 70 //next 71 right.onclick = function(){ 72 toRight(); 73 } 74 75 document.addEventListener('mousedown',function(e){ 76 isTouch = true; 77 axis.x = e.clientX; 78 axis.y = e.clientY; 79 }); 80 81 document.addEventListener('mousemove',function(e){ 82 if(isTouch){ 83 distance = e.clientX - axis.x; 84 if(distance>0){ 85 //next 86 if(currIndex<pagesLen){ 87 //此時須要看到實時移動效果,因此時間爲0 88 move(currIndex,distance,0); 89 //當前頁與上一頁之間老是相差一個屏寬 90 move(currIndex+1,distance-screenWidth,0); 91 } 92 }else{ 93 if(currIndex>0){ 94 //prev 95 move(currIndex,distance,0); 96 move(currIndex-1,screenWidth+distance,0); 97 } 98 } 99 } 100 }); 101 102 document.addEventListener('mouseup',function(e){ 103 isTouch = false; 104 //反彈條件 105 var band = Math.ceil(screenWidth * 0.3); 106 //next 107 if(distance >0 && currIndex < pagesLen){ 108 if(distance > band){ 109 toRight(); 110 }else{ 111 //滑動距離過小,頁面反彈 112 move(currIndex,0,time); 113 move(currIndex+1,-screenWidth,time); 114 } 115 return; 116 } 117 //prev 118 if(distance < 0 && currIndex > 0){ 119 if(-distance > band){ 120 toLeft(); 121 }else{ 122 //反彈 123 move(currIndex,0,time); 124 move(currIndex-1,screenWidth,time); 125 } 126 } 127 }); 128 129 }())
最後發現有一點小問題,滑動以後再點翻頁按鈕,亂套了。仔細分析以後,找出了問題所在,鬆手時的移動距離不該該用滑動時的最後距離,因此修復很容易。
1 //初始化 2 ;(function(){ 3 var pages = document.querySelectorAll('li'); 4 var width = 320; 5 var len = pages.length; 6 var setpost = function(element){ 7 element.style.transform = 'translate3d(-'+width+'px, 0, 0)'; 8 } 9 10 //把除1之外的頁堆在左邊 11 while(--len){ 12 setpost(pages[len]); 13 } 14 }()); 15 16 //控制邏輯 17 ;(function(){ 18 //獲取全部頁面 19 var pages = document.querySelectorAll('li'); 20 //左右翻頁按鈕 21 var left = document.querySelector('.left'); 22 var right = document.querySelector('.right'); 23 //取得全部的子頁面 24 var pagesLen = pages.length-1; 25 //屏寬 26 var screenWidth = 320; 27 //反彈時間 28 var time = 300; 29 30 //標記當前頁面 31 var currIndex = 0; 32 33 //標記是否觸發滑動 34 var isTouch = false; 35 //記錄當前位置 36 var axis = { 37 x:0, 38 y:0 39 } 40 41 //移動頁面 42 var move = function(index,width,time){ 43 var page = pages[index]; 44 page.style.transform = 'translate3d('+width+'px, 0, 0)'; 45 page.style.transitionDuration = time+'ms'; 46 } 47 48 //上一頁 49 var toLeft = function(){ 50 if(currIndex >0){ 51 move(currIndex,-screenWidth,time); 52 move(--currIndex,0,time); 53 } 54 } 55 56 //下一頁 57 var toRight = function(){ 58 if(currIndex < pagesLen){ 59 move(currIndex,screenWidth,time); 60 move(++currIndex,0,time); 61 } 62 } 63 64 //監聽動做 65 //prev 66 left.onclick = function(){ 67 toLeft(); 68 } 69 //next 70 right.onclick = function(){ 71 toRight(); 72 } 73 74 document.addEventListener('mousedown',function(e){ 75 isTouch = true; 76 axis.x = e.clientX; 77 axis.y = e.clientY; 78 }); 79 80 document.addEventListener('mousemove',function(e){ 81 if(isTouch){ 82 //滑動距離 83 var distance = e.clientX - axis.x; 84 if(distance>0){ 85 //next 86 if(currIndex<pagesLen){ 87 //此時須要看到實時移動效果,因此時間爲0 88 move(currIndex,distance,0); 89 //當前頁與上一頁之間老是相差一個屏寬 90 move(currIndex+1,distance-screenWidth,0); 91 } 92 }else{ 93 if(currIndex>0){ 94 //prev 95 move(currIndex,distance,0); 96 move(currIndex-1,screenWidth+distance,0); 97 } 98 } 99 } 100 }); 101 102 document.addEventListener('mouseup',function(e){ 103 //鬆手時的移動距離 104 var distance = e.clientX - axis.x; 105 //反彈條件 106 var band = Math.ceil(screenWidth * 0.3); 107 isTouch = false; 108 //next 109 if(distance >0 && currIndex < pagesLen){ 110 if(distance > band){ 111 toRight(); 112 }else{ 113 //滑動距離過小,頁面反彈 114 move(currIndex,0,time); 115 move(currIndex+1,-screenWidth,time); 116 } 117 return; 118 } 119 //prev 120 if(distance < 0 && currIndex > 0){ 121 if(-distance > band){ 122 toLeft(); 123 }else{ 124 //反彈 125 move(currIndex,0,time); 126 move(currIndex-1,screenWidth,time); 127 } 128 } 129 }); 130 131 }())
到此,這個簡易的移動應用就建好了。發現這其實只是搭好了一個架子,裏邊還有不少內容能夠填。好比,給每頁加點背景和文字,讓頁面看起來更加豐滿,給標題欄加點工具圖標,讓它看起來更像是一個原生的apk程序。當咱們雙擊或長按頁面空白區域的時候,自動隱藏或顯示翻頁按鈕,增長對更多瀏覽器的支持,增長對移動設備的支持........
能夠發揮的地方太多太多了,錦上添花的事就只好交給各位了。
一步一步作下來,收穫了些什麼呢?總結一下:
首先要有一個清晰的思路,先肯定要實現什麼功能,在腦海裏有一個整的印象。把UI先建起來,這一步很容易實現,在此基礎上,咱們就能夠看到,有哪些功能須要js去作,哪些效果須要css去作。先易後難。先總體後局部。在作的過程當中,一步一步的完善,擴展,優化。一開始就想着要多完美,多優化,結果只是不斷的否認本身的想法。不要怕出錯,前面的演示中,咱們發現,每一步都有些小錯誤,可是隻要咱們的總體思路是符合預期的,修正起來就會很快,這個修錯的過程,也是一次學習的機地,總結的多了,之後就能夠少出錯,少被坑。
效果圖:
加點內容,是否是立馬顯得高大上了呀!
源碼打包下載:https://github.com/bjtqti/swipe.git
本文系原創,若是喜歡就砸個贊吧!