這段時間在做的東西,是北郵人論壇APP的注冊頁。這個注冊頁是内嵌的網頁,因為打算安卓和IOS平台同時使用。是以實際上就是在做移動端的web開發了。
在這過程中遇到了不少有意思的東西。
DEMO的github位址在
這裡- 移動端用下拉重新整理的方式實作上拉加載
- 移動端(手機端)頁面自适應解決方案—rem布局篇
- 一個 VUE 元件:實作子元素 scroll 父元素容器不跟随滾動(相容PC、移動端)
- 移動端rem.js使用方法
- 移動端常見bug
内容提要:
- meta标簽
- Vuejs的簡單實戰
- CSS移動端全屏背景
- CSS移動端動畫初探
這點與在PC端寫前端有着很大的差別,移動端的meta标簽簡直多。我就說說我所用到的标簽。
<!-- 1、如果支援Google Chrome Frame:GCF,則使用GCF渲染;2、如果系統安裝ie8或以上版本,則使用最高版本ie渲染;3、否則,這個設定可以忽略。 -->
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
<!-- 對視窗縮放等級進行限制,使其适應移動端螢幕大小 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- 當把這個網頁添加到主螢幕時的标題(僅限IOS) -->
<meta name="apple-mobile-web-app-title" content="北郵人論壇注冊">
<!-- 添加到主螢幕後全屏顯示 -->
<meta name="apple-touch-fullscreen" content="yes" />
尤其是第二個meta标簽,是移動端适配非常重要的一句話。
整體布局
整體的布局大緻是4-5頁橫向布局。第一頁是用來填寫注冊資訊的。後面的幾頁是用來選擇關注的版面的。
注冊資訊頁

關注版面頁
整體架構
前端采用的架構大緻是這樣:
- 滑動切換頁面的效果來自 swiper.js
- 頁面内容的渲染采用 Vue.js
- 頁面布局和樣式采用純css,部分效果采用css3
- ajax部分采用 vue-resource
後端支撐的架構來自php的
laravel,當然,這不是本文的重點,僅提及一下。
是的這次的開發中,已經看不到jquery的身影了——這也是前端以後發展後的結果——慢慢地脫離jquery的依賴。不過jquery給前端帶來的改變和發展是無人能替代的。
swiper.js的應用
引入swiper.js來進行頁面的切換效果純粹是因為這次開發的周期要求比較短,要考慮效果和相容性兼備的情況下,我就偷懶找了一個動畫庫。
不過這個動畫庫的效果我還是算比較滿意的。而整體來說使用也相當友善。尤其是,swiper.js是可以不依賴jquery的。
使用起來也比較友善。我簡要說說用法。
首先需要在頁面頂部的head标簽裡加入swiperjs的css檔案:
<link rel="stylesheet" href="css/swiper.min.css')">
然後在頁面底部可以引入和寫下相應的js:
<script src="{{ asset('js/swiper.min.js') }}"></script>
<!-- Initialize Swiper -->
<script>
var swiper = new Swiper('.swiper-container', {
pagination: '.swiper-pagination',
paginationType: 'progress',
noSwipingClass: 'swiper-no-swiping',
allowSwipeToNext: true,
allowSwipeToPrev: true,
});
</script>
解釋一下,建立一個swiper的對象,然後這對象的容器是class叫做swiper-container的一個html元素。對其的配置是:
- pagination: ‘.swiper-pagination’, 顯示頁碼
- paginationType: ‘progress’, 頁碼顯示格式為進度條,可以參見頂部藍白色的進度條
- noSwipingClass: ‘swiper-no-swiping’, 不允許進行觸摸滑動的元素的class名稱
- allowSwipeToNext: true, 允許向後滑動
- allowSwipeToPrev: true, 允許向前滑動
相應的HTML代碼可以如下:
<!-- swiper生效的容器 -->
<div class="swiper-contanier">
<div class="swiper-wrapper">
<!-- 具體滑動的頁面 -->
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
<div class="swiper-slide"></div>
</div>
</div>
<!-- 進度條 -->
<div class="swiper-pagination"></div>
有些頁面是不能直接讓使用者通過觸摸來前後滑動的,而必須通過點選按鈕觸發。比如第一頁注冊頁,這個頁面就是必須填寫完資訊然後點選下一步進行驗證後然後才可以滑動到後面的内容。是以隻要将這個頁面所在的class裡加上swiper-no-swiping這個class就可以實作無法觸摸滑動切換頁面了。
然後我們可以通過swiper.slideNext(bool,time)這個方法來進行手動控制向後翻頁的動作以及控制動畫的時長。這個内容會在vue裡說到。
Vue.js的簡單實戰
由于本次的開發隻是在原有的北郵人開放平台的項目的基礎上加入一個快速注冊的功能,是以Vue.js的引入并不是為了将整個項目重構,而隻是為了嘗試一下脫離jquery的情況下對于開發來說不同的體驗是什麼,能學到什麼。
關于Vue.js的用法、特性什麼的不是本文的重點,題外話就不說了。說說本次實作思路。因為我的Vuejs實際上也隻是算入門級别,隻能說會用但是還沒到能駕馭的程度。
首先整個頁面包在class叫做swiper-contanier的元素中。因為這個元素包括了滑動所需的所有東西,我們實際上可以簡單的把它看成是一個SPA(Single Page Application),用一個Vue的執行個體就可以接管整個頁面了(這麼說是不負責的,因為實際上Vue是元件化的思想)。
先建立一個基本的Vue的示例,并将其和swiper-container綁定起來:
var vm = new Vue({
el: ".swiper-contanier", // 将這個執行個體與html元素綁定起來
data: {}, // 所需要變動、關注的資料,也是vue的核心
ready: function(){}, // vue提供的鈎子,用于在vue渲染視圖完成後立即觸發
methods: {} // 方法,用于操作、更新、改變資料而改變視圖
})
注冊頁實作
上面所說,我們的頁面是建構在一個Vue的執行個體上的。是以不同類型的兩種頁面如何用一個執行個體來接管呢?在這裡我的實作方式是用兩類資料來分别表示。
我們分析一下注冊頁:
實際上注冊頁的中間部分是重複的元素,他們都是input标簽+顯示文字标簽(對,尤其注意這裡并不是用placeholder實作的)。效果:
是以這中間的部分實際上可以看成是一個清單,可以用Vue的v-for來渲染。清單裡所不同的隻是顯示的文字不同以及input框的類型不同(有text類型的,有password類型的),是以用資料綁定的方式我們可以将這個頁面的資料格式安排如下:
data: {
main: [
{"name":"username","info":"使用者名(以英文開頭+英文數字)","type":"text"},
{"name":"passwd","info":"設定密碼","type":"password"},
{"name":"passwd_confirm","info":"在輸入一遍密碼","type":"password"},
{"name":"gwno","info":"校園網賬戶(預設是學号)","type":"text"},
{"name":"gwpwd","info":"校園網密碼(預設是身份證後六位)","type":"password"},
],
}
同時我們建立一個便于和前端視圖進行雙向綁定的資料對象userInfo:
data: {
main: [...],
userInfo: {
username: "",
passwd: "",
passwd_confirm: "",
gwno: "",
gwpwd: ""
}
}
而在前端的話我們就可以用這個資料來進行視圖渲染:
<ul class="user-info">
<li v-for="item in main" style="position: relative;">
<!-- input輸入框 -->
<!-- 此處用了v-model将資料和視圖進行了雙向綁定 -->
<input class="effect" type="{{item.type}}" v-model="userInfo[item.name]">
<!-- 提示資訊 -->
<label>
<span>{{item.info}}</span>
</label>
<!-- input框的底下的線條 -->
<span class="focus-border cube"></span>
</li>
</ul>
<a href="#"><button>下一步</button></a>
于是一個輸入的清單就很容易做出來了。然後既然是表單,就需要驗證。而此處做的驗證明際上有這麼幾點:
- 使用者名是否合法/重複?
- 兩次輸入的密碼是否相同?
- 校園網賬戶的密碼是否正确?
其中隻有第二點兩次密碼輸入是否相同可以用前端直接判斷,而第一、三點都是需要通過ajax的方式向背景發送驗證請求的。為了能夠展現和辨識錯誤與否,我們在main下的每個條例裡加入了一個error屬性,并規定如下三種狀态:
- true,代表有錯誤,提示錯誤
- false,代表正确,提示正确
- normal,代表預設,顯示預設值
于是我們可以在method下寫一些方法來進行判斷。
checkUserId: function(msg){
if (msg !== ""){
this.$http.post('url'+msg,function(data){
if (data.success){
this.main[0].error = false;
} else{
this.main[0].error = true;
}
})
} else{
this.main[0].error = "normal";
}
},
checkUserPwd: function(){
if (this.userInfo.passwd_confirm !== ""){
this.userInfo.passwd == this.userInfo.passwd_confirm && this.userInfo.passwd_confirm != "" ? this.main[2].error = false : this.main[2].error = true;
}
},
當然這個隻是一個引入的功能,我們再聚合一下:
check: function(msg,i){
var index = i;
this.userInfo[msg] != "" ? this.main[index].effect = true : this.main[index].effect = false;
switch (msg){
case "username":
this.checkUserId(this.userInfo[msg]);
break;
case "passwd":
this.userInfo.passwd !== "" ? this.main[1].error = false : this.main[1].error = "normal";
this.checkUserPwd();
break;
case "passwd_confirm":
this.checkUserPwd();
break;
case "gwno":
this.userInfo.gwno !== "" ? this.main[3].error = false : this.main[3].error = "normal";
break;
case "gwpwd":
this.userInfo.gwno !== "" ? this.main[4].error = false : this.main[4].error = "normal";
break;
}
}
這樣通過一個check的method我們就可以将整個表單的驗證的方法容納進來了。(此處對于校園網賬号的驗證會放到送出表單的函數中)。為了能在視圖中展現正确、錯誤、正常的不同形态,我們需要對前端的一些結構進行一些修改。我們需要給main下的每個條例加入錯誤資訊,也即errorInfo。
是以至此整個main的結構是這樣:
main: [
{"name":"","info":"","type":"","error":"","errorInfo":""},
...
]
然後我們需要加入送出驗證的部分:
submitReg: function(){
var flag = 0;
// 用于判斷表單是否都是正确的
this.main.map(function(obj){
obj.error == false ? flag += 1 : flag +=0;
})
if (flag == 5){
this.$http.post('url',this.userInfo)
.then(function(res){
if (res.success){
swiper.slideNext(false,300); // 驗證正确就可以進入下一頁
} else{
this.main[4].error = true;
}
})
}
},
将視圖部分修改如下:
<ul class="user-info">
<li v-for="item in main" style="position: relative;">
<!-- 綁定blur事件 -->
<input @blur="check(item.name,$index)" class="effect" type="{{item.type}}" v-model="userInfo[item.name]">
<!-- 根據error類型切換不同的标簽顯示 -->
<label>
<!-- 此處用到了v-show的方法 -->
<span v-show="item.error == 'normal'">{{item.info}}</span>
<span style="color: red;" v-show="item.error == true">{{item.errorInfo}}</span>
<span v-show="item.error == false">{{item.info}}√</span>
</label>
<span class="focus-border cube"></span>
</li>
</ul>
<!-- 點選下一步的同時送出表單資訊 -->
<a href="#"><button @click="submitReg">下一步</button></a>
至此,整個注冊頁的布局和功能性的部分都已經做完了。不過這隻是一塊比較簡單的部分,我們用到了vuejs的的v-for進行清單渲染,用到了v-model進行資料的雙向綁定,用到了method進行一些資料的處理,用到了v-show進行條件顯示,一個基本的頁面下我們已經能嘗試這麼多vue的特性了。而相比于jquery的dom操作,我覺得vue在此處最好的地方在于,表單的送出很友善。由于雙向綁定了資料,隻需要背景把資料格式規定好給我,我按照後端的資料結構整理一下我前端的資料結構然後就可以直接送出給後端了。而且省去了很多dom操作的地方。
不過除了vue的部分,我還想來說說兩個東西,跟css有關:
- 全屏背景
- 輸入框文字提升效果,并涉及到移動端動畫效果的處理
不僅僅是簡單的background-size: cover那麼簡單了,還需要進行小小的處理。先說說我希望實作的效果吧。我希望的效果是整個背景能夠填充整個頁面,并且在頁面元素上下滾動的情況下,背景固定而不随着元素滾動。
放到往常我可能會這麼寫:
body,html{
height: 100%;
}
body{
background: url(bg.png) center 0 no-repeat;
background-size: cover;
}
但是這樣的話在移動端會出現比較嚴重的後果,那就是一旦頁面元素的高度大于整個頁面後,滾動頁面元素的時候,背景也會随之而動。而且背景會被撐開。這不是我所希望的。
這裡用到一個小技巧,用上:before的方法。
body:before {
content: "";
position: fixed;
z-index: -1;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: url(bg.png) center 0 no-repeat;
background-size: cover;
}
這個用上before的僞元素的方法是一個很有奇效的小技巧。大家不妨可以試試。這樣的話在移動端也能完美實作背景固定而且顯示全屏。
移動端動畫簡單初探
在做PC端的web的動畫效果的時候,由于PC端的性能足夠,是以在寫一些效果的時候往往沒有考慮到性能的問題。這次在開發的時候就遇到了動畫效果實作上不小的問題。大家先來看看這兩個動畫的比較:上面一個動畫和下面一個動畫:
第一個:
https://codepen.io/molunerfinn/pen/zBGrxx第二個:
https://codepen.io/molunerfinn/pen/oLXjKz會發現這兩個東西的效果相差甚大。實際上,二者實作的最終效果是一緻的,都是讓小球按一條口型路線運動。但是為何顯示上來說,第一個這麼流暢,而第二個有明顯示卡頓呢?
這裡涉及到很多東西,不光光是重繪(repaint),還有軟硬體性能上面的問題。感興趣的話可以參考這些文章:
在移動端上的效果如果沒有優化的話,實際上大緻就是第二種的效果——讓人看起來有所卡頓。簡單來說,在移動端,有些效果是由浏覽器來渲染的——那麼這些效果如果比較複雜,而移動端的浏覽器性能又不太足夠的情況下,效果就比較卡頓。有些效果可以由GPU來渲染,那麼這些效果渲染起來就相對來說比較流暢。而我們還可以人為觸發GPU硬體渲染,通過 transform的translate3d屬性就能實作硬體渲染進而俗稱硬體加速。
舉個簡單例子。如果讓一個元素從(0,0)->(100px,0),正常思路是left: 0->left: 100px,然後再用transition屬性進行過渡。不過這樣的話在移動端上效果就很感人,因為涉及到性能問題。但是如果我們用另外一種方式: transform: translate3d(100px,0,0)的話,就可以讓這個動畫效果由GPU去渲染,那麼這樣的話,在移動端的效果也是完全可以接受的流暢。
實際上,能夠觸發GPU渲染的動作有opacity,transform,transition,animation等等。但是像top,left,color,size等屬性的變化則不會觸發GPU渲染。
本文中描述的執行個體,是當焦點集中到input框的時候,文本上移并且有顔色變化。本來想實作的是文本大小還有變化。但是由于上面說的情況,涉及到size變化的時候,效果就會大打折扣。無奈之下我隻能砍掉這個效果了。而實作文本上移實際上就是采用了transform的translate3d的方式,将其往上移動,并配合transition進行了一下過渡處理罷了。
具體的CSS實作大緻如下:
input:focus ~ label,.trans {
color: #fff;
transition: 0.3s;
-webkit-transform: translate3d(0, -20px, 0);
-moz-transform: translate3d(0, -20px, 0);
-ms-transform: translate3d(0, -20px, 0);
transform: translate3d(0, -20px, 0);
}
實際上也不難不是麼?
關注版面頁的實作
實際上從注冊頁的實作中已經可以瞅見關注版面頁實作的方法了。實際上關注版面頁不過也是清單的渲染,在資料裡定義好相應的屬性就行了。然後用一個picked的屬性來看看是否被選中即可。最後完成注冊的時候,将所有選中的清單組裝成相應的數組送出到背景就行了。因為注冊頁裡這些方法已經說過了,是以就不再贅述了。
總結
簡單總結一下,這次移動端的開發中學習到的東西。
- Vuejs的簡單使用,從DOM操作->資料綁定
- 全屏背景的實作
- 流暢動畫效果的實作
實際上,相容性方面還有不少的東西需要訴說,不過限制于篇幅以及本文的主要内容并不是在糾結移動端的相容性的是以并沒有在相容性方面進行記錄。
在排版上我還是沒有用上比較流行的flex,對于Vue還沒有使用元件化開發的思路。這些都是需要改進的部分。不過這次的開發過程中經過美工的指點,将我之前寫頁面的時候注意不到的很多細節部分,比如線條尺寸,元素間隔,顔色搭配等東西給指了出來,這些東西都不是簡簡單單就實作的。總之,有時間的話,我想我可以将設計方面的知識融入到我的前端開發中,想必能讓我的作品更加地符合審美和使用者的體驗吧~
文章作者: Molunerfinn
文章連結:
https://molunerfinn.com/vuejs-1/版權聲明: 本部落格所有文章除特别聲明外,均采用 CC BY-NC-SA 4.0 許可協定。轉載請注明來自 MARKSZのBlog!