剛開始經營博客的時候,我寫過很多「扒皮」系列的文章,主要介紹一些知名站點上有趣的交互效果,而後試着實現它們。後來開始把注意力挪到一些新穎的前端技術上,「扒皮」系列便所以封筆多時。今天打算重開「扒皮」的坑,不過咱掛個優雅點的名字——「優秀網站看前端」,顧名思義的,也是尋覓一些值得玩味的趣味網站,來學習它們的前端技術和交互理念。css
做爲本系列的開篇,咱們拿「買手機送國土」的小米來打頭陣,原因是鄙人有着更換手機的打算又恰好看上小米note高配版,因而逛了下小米note的介紹頁面,感受交互作的挺不錯,特別是CSS3動畫部分,不妨就直接寫篇文章和你們一同來學習和分享。html
小米note的介紹頁面地址以下,你們能夠先去領略它的交互效果:前端
字體css3
該網站首先吸引個人是其在標題頭等地方使用的字體,因爲自己也喜歡搞設計,因此一眼就注意到這兩行細體字絕非宋體黑體和雅黑,check了一下,字體名爲FZLTXHK(也就是方正蘭亭纖黑體):web
會不會有點小詫異,常規咱們是很不推薦在網頁上經過font-face來引入第三方中文字體的,畢竟一個完整的中文字體包常規都是好幾M的大小,一來得讓客戶端花時間請求、浪費用戶流量,二來會形成頁面字體效果切換的閃動(FOUT——flash of unstyled text)現象。gulp
小米做爲一個注重用戶體驗的公司,相信也不會作這麼不合常理的事情(喂,沒打算聊國土啊喂)。那麼字體這塊,小米天然也是動了些手腳——只封裝了頁面上須要使用到的文字。不信?你能夠試着把用到第三方字體的標題內容更改成其它內容,你會發現不少文字是不被該字體所支持的:瀏覽器
那麼小米的射雞獅真是辛苦,每次修改頁面都要手動打包新的字體,真是兢兢業業可歌可泣。。。。其實未然,畢竟如今不是刀耕火種的原始社會,咱們能夠直接請node包加持~ide
因而 「字蛛(FontSpider)」 這款工具能夠粉墨登場啦(別亂用成語啊親~)—— 字蛛經過分析本地 CSS 與 HTML 文件獲取 WebFont 中沒有使用的字符,並將這些字符數據從字體中刪除以實現壓縮,同時生成跨瀏覽器使用的格式。svg
字蛛的使用方式在其官網上已經解釋的很清楚了,本文不贅述,但先聊聊@font-face的匹配格式,也就是聊一聊WEB上經常使用的字體格式。
@font-face中引入的字體文件能夠經過format方法來幫助瀏覽器匹配到對應的字體格式,常規各瀏覽器所支持的字體格式有以下幾種:
1、TureTpe(.ttf)格式:
.ttf字體是Windows和Mac的最多見的字體,是一種RAW格式,所以他不爲網站優化,支持這種字體的瀏覽器有(IE9+,Firefox3.5+,Chrome4+,Safari3+,Opera10+,iOS Mobile Safari4.2+);
2、OpenType(.otf)格式:
.otf字體被認爲是一種原始的字體格式,其內置在TureType的基礎上,因此也提供了更多的功能,支持這種字體的瀏覽器有(Firefox3.5+,Chrome4.0+,Safari3.1+,Opera10.0+,iOS Mobile Safari4.2+);
3、Web Open Font Format(.woff)格式:
.woff字體是Web字體中最佳格式,他是一個開放的TrueType/OpenType的壓縮版本,同時也支持元數據包的分離,支持這種字體的瀏覽器有(IE9+,Firefox3.5+,Chrome6+,Safari3.6+,Opera11.1+);
4、Embedded Open Type(.eot)格式:
.eot字體是IE專用字體,能夠從TrueType建立此格式字體,支持這種字體的瀏覽器有(IE4+);
5、SVG(.svg)格式:
.svg字體是基於SVG字體渲染的一種格式,支持這種字體的瀏覽器有(Chrome4+,Safari3.1+,Opera10.0+,iOS Mobile Safari3.2+)。
那麼綜上所述,一個@font-face只要匹配了.eot 和 其它某種字體(最好是.woff),就基本能在大多數瀏覽器上正常顯示了。不過查看小米的樣式(點我查看),它只匹配了.woff(也就是IE8-將降級爲常規字體)。另外,小米將該字體文件以base64編碼形式直接inline在css上,此舉有個好處——減小了一次文件請求,也能有效防止上文提過的FOUT現象。
不過這個base64怎麼折騰出來的?或許小米用字蛛之類的工具得到字體壓縮文件後,再經過某種方式(好比在這裏轉換)將其轉爲base64編碼形式便可。
另外小米還使用了一個CSS3樣式:
-webkit-font-smoothing:antialiased
該屬性可使頁面上的字體抗鋸齒,使用後字體看起來會更清晰舒服(特別適用於字號較小的文本內容)。
有三種可選值:
none | subpixel-antialiased | antialiased
它們的區別見下圖:
transition動畫
小米的頁面處處都充滿了視覺差滾動效果,有種隨時給你小驚喜的感受:
如上圖的動畫,是由transition實現的,大體步驟以下:
⑴ 給全部要動畫的元素設置transition屬性,好比 transition:transform 1s ;
⑵ 給全部動畫元素添加一個class="visible" ,該class中定義了動畫的最終狀態;
⑶ 頁面DOMReady的時候遍歷全部動畫元素($(".visible")),檢查它們是否還沒被滾動條滾上來,若是還在窗口可視區域下方,則移除它們"visible"的class。該步驟主要是用於處理用戶下拉頁面到某個位置而後刷新頁面,這時要求窗口可視區域及其上方的元素都應跳過動畫的狀態,直接到達動畫最終狀態;
⑷ 監聽onscroll事件,移動到某個動畫元素的位置時,則移除該元素名爲"visible"的class。
咱們能夠粗略地寫個場景和腳原本實現:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>動畫模擬</title> <script src="jq.js"></script> <style> article,div{margin: 380px 0;border: solid 1px gray;} article > section{width:50px;height:50px;background:red;transform: translate3d(-100px, -60px, 0);opacity: 0;transition: all .8s;} article > section.visible {transform: translate3d(0, 0, 0);opacity: 1;} div > span{background:blue;transform: scale(.2);opacity: 0;transition: all 2s;} div > span.visible {transform: scale(1);opacity: 1;} div > p {width:50px;height:50px;background:yellow;transform: translate3d(90px, 100px, 0);opacity: 0;transition: all 1.5s;} div > p.visible {transform: translate3d(0, 0, 0);opacity: 1;} </style> <script> $(function(){ var elmArr = [], $win = $(window); $(".visible").each(function(i,elm){ $(elm).data("ot",$(elm).offset().top); elmArr.push(elm); }); dealClass(1); $win.on("scroll",dealClass); function dealClass(isRemove){ var top = $win.height() + $win.scrollTop(); if(isRemove!=1) { //滾動頁面時的判斷,並添加class="visible" for (var i = 0,$elem; i < elmArr.length; i++) { $elem = $(elmArr[i]); if ($elem.data("ot") <= top) { $elem.addClass("visible"); elmArr.splice(i, 1); --i; } } }else{ //初始化頁面時的判斷,並刪除class="visible" for (var i = 0,$elem; i < elmArr.length; i++) { $elem = $(elmArr[i]); if ($elem.data("ot") >= top) { $elem.removeClass("visible"); } } } } }) </script> </head> <body> <article> <section class="visible"></section> </article> <div> <span class="visible">hello</span> </div> <div> <p class="visible"></p> </div> </body> </html>
效果以下:
transition動畫效果默認是線性展現的(linear),咱們經過設置其timing-function屬性可讓效果變得更靈活,好比這個效果:
此處的transition-timing-function屬性被設置爲 cubic-bezier(.15, .73, .37, 1.2) ,表示按照該貝塞爾曲線函數來執行動畫(瞭解更多請戳我)。
你能夠試着把咱們上方例子中的 transition:XXX 修改成:
transition: transform 1.5s cubic-bezier(.15, .73, .37, 1.2),opacity 1s;
而後查看其效果:
若是在transition的最後加上一個時間單位,能夠延遲觸發動畫效果。好比上面五個手機(5個<li>標籤)是按順序依次出來的,那麼咱們能夠給第2個<li>設定0.2秒的延遲,給第3個<li>設定0.4秒的延遲,給第4個<li>設定0.6秒的延遲。。。
咱們拿第2個<li>的transition來示例:
transition:transform 1s cubic-bezier(.15, .73, .37, 1.2) .2s;
因爲在末尾寫上了0.2s的transition-delay值,故第二個手機會相較第一個手機晚0.2秒執行動畫。
animate動畫
animate很適用於那些須要分段展現的,或者有周期性的非過渡性動畫,好比雙4G的介紹區域就使用了animate(該效果頁面地址):
該卡槽是由一個div(卡槽自己)內嵌一個span(最後淡入顯示的鏤空處文本)組成的,div觸發動畫時(跟transition同樣加個觸發class)直接從下往上顯示(2s),而span雖然是同時被觸發動畫,但它延遲2s才執行,因此營造了「在div動畫結束後,span纔開始觸發動畫」的視覺效果。
咱們照樣拿前面的例子來修改:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>動畫模擬</title> <script src="jq.js"></script> <style> body,html{height: 100%;} @-webkit-keyframes ani-fade-in { 0% { opacity: 0 } 100% { opacity: 1 } } @keyframes ani-fade-in { 0% { opacity: 0 } 100% { opacity: 1 } } @-webkit-keyframes ani-fade-in-up { 0% { -webkit-transform: translateY(100px); opacity: 0 } 100% { -webkit-transform: translateY(0); opacity: 1 } } @keyframes ani-fade-in-up { 0% { -webkit-transform: translateY(100px); opacity: 0 } 100% { -webkit-transform: translateY(0); opacity: 1 } } article{margin: 500px 0;} div{width:50px;height:50px;background:green;opacity: 0;} div.visible{-webkit-animation:ani-fade-in-up 2s ease forwards;animation:ani-fade-in-up 2s ease forwards;} div > span{background:blue;opacity: 0;} div.visible > span {-webkit-animation:ani-fade-in 1s 2s ease forwards;animation:ani-fade-in 1s 2s ease forwards;} </style> <script> $(function(){ var elmArr = [], $win = $(window); $(".visible").each(function(i,elm){ $(elm).data("ot",$(elm).offset().top); elmArr.push(elm); }); dealClass(1); $win.on("scroll",dealClass); function dealClass(isRemove){ var top = $win.height() + $win.scrollTop(); if(isRemove!=1) { //滾動頁面時的判斷,並添加class="visible" for (var i = 0,$elem; i < elmArr.length; i++) { $elem = $(elmArr[i]); if ($elem.data("ot") <= top) { $elem.addClass("visible"); elmArr.splice(i, 1); --i; if(i<0) $win.off("scroll",dealClass); } } }else{ //初始化頁面時的判斷,並刪除class="visible" for (var i = 0,$elem; i < elmArr.length; i++) { $elem = $(elmArr[i]); if ($elem.data("ot") >= top) { $elem.removeClass("visible"); } } } } }) </script> </head> <body> <article> <section>123</section> </article> <div class="visible"> <span>hello</span> </div> </body> </html>
效果以下:
其實這段代碼中涉及動畫的最關鍵的部分不外乎這兩行:
div.visible{-webkit-animation:ani-fade-in-up 2s ease forwards;animation:ani-fade-in-up 2s ease forwards;}
div.visible > span {-webkit-animation:ani-fade-in 1s 2s ease forwards;animation:ani-fade-in 1s 2s ease forwards;}
其中span動畫延遲2秒執行,執行過程爲1s,另外二者都使用了forwards來保持最終狀態。
另外在介紹wifi的地方還有一個有趣的循環動畫:
這是在animation中將其動畫執行次數設置爲infinite :
@-webkit-keyframes ani-circle-scale { 0% { -webkit-transform: scale(0); margin-left: 0 } 45% { -webkit-transform: scale(1); margin-left: -999px; opacity: 1 } 80% { opacity: 1 } 100% { opacity: 0 } } div{-webkit-animation:ani-circle-scale 8s ease-out forwards infinite;}
更多animation的知識點可點此查閱。
其它
在相機介紹頁面,小米還使用了video來展現一個小動畫(有些複雜點的展現效果直接使用小尺寸的視頻來展現也是個不錯的選擇):
此處代碼爲:
<video id="exporevideo" poster="http://img03.mifile.cn/webfile/images/2014/cn/goods/minote/camera/ca-49.png"> <source src="http://img03.mifile.cn/webfile/images/2014/cn/goods/minote/camera/jingtou.mp4" type="video/mp4"> <source src="http://img03.mifile.cn/webfile/images/2014/cn/goods/minote/camera/jingtou.webm" type="video/webm"> <img src="http://img03.mifile.cn/webfile/images/2014/cn/goods/minote/camera/ca-49.png" alt=""> </video>
兩個source分別匹配了mp4和webm格式的視頻文件,其中mp4是爲了兼容IE9+和Safari,webm是爲了兼容舊版的Firefox和Opera。除了mp4和webm,其實還有ogg格式可選,各瀏覽器對視頻格式的支持度可查看維基百科(很是詳盡)。
最後的那張img圖片是用來優雅降級的,也就是不支持<video>標籤的瀏覽器會直接顯示爲這張圖片。
小米在其基礎樣式中還對全體img使用了 -ms-interpolation-mode:bicubic 屬性,它可讓IE下被縮小的圖片保持較高質量,而不是變得模糊、帶鋸齒。不過該樣式其實只對IE7有做用,由於IE8+的缺省值已設定爲bicubic(IE7-下爲nearest-neighbor ,圖片被縮放後質量會不好)。
另外小米對其樣式和腳本均作了混淆和壓縮處理,不過這已不是什麼新奇的東西,只是使用了 grunt 或 gulp 等前端輔助工具罷了。
本篇就介紹到這裏,實際上小米官網上還有不少本章未說起的有趣交互,有興趣的朋友能夠去仔細摸索一番。
話說最近也是蠻拼的,博客越寫越長、越寫越晚,醉了。。。
共勉~