優(yōu)秀網(wǎng)站看前端:小米Note介紹頁(yè)面代碼分析
剛開(kāi)始經(jīng)營(yíng)博客的時(shí)候,我寫(xiě)過(guò)不少“扒皮”系列的文章,主要介紹一些知名站點(diǎn)上有趣的交互效果,然后試著實(shí)現(xiàn)它們。后來(lái)開(kāi)始把注意力挪到一些新穎的前端技術(shù)上,“扒皮”系列便因此封筆多時(shí)。今天打算重開(kāi)“扒皮”的坑,不過(guò)咱掛個(gè)優(yōu)雅點(diǎn)的名字——“優(yōu)秀網(wǎng)站看前端”,顧名思義的,也是尋覓一些值得玩味的趣味網(wǎng)站,來(lái)學(xué)習(xí)它們的前端技術(shù)和交互理念。
作為本系列的開(kāi)篇,我們拿“買(mǎi)手機(jī)送國(guó)土”的小米來(lái)打頭陣,緣由是鄙人有著更換手機(jī)的打算又剛好看上小米note高配版,于是逛了下小米note的介紹頁(yè)面,感覺(jué)交互做的挺不錯(cuò),特別是CSS3動(dòng)畫(huà)部分,不妨就直接寫(xiě)篇文章和大家一同來(lái)學(xué)習(xí)和分享。
小米note的介紹頁(yè)面地址如下,大家可以先去領(lǐng)略它的交互效果:
字體
該網(wǎng)站首先吸引我的是其在標(biāo)題頭等地方使用的字體,由于本身也喜歡搞設(shè)計(jì),所以一眼就注意到這兩行細(xì)體字絕非宋體黑體和雅黑,check了一下,字體名為FZLTXHK(也就是方正蘭亭纖黑體):
會(huì)不會(huì)有點(diǎn)小詫異,常規(guī)我們是很不推薦在網(wǎng)頁(yè)上通過(guò)font-face來(lái)引入第三方中文字體的,畢竟一個(gè)完整的中文字體包常規(guī)都是好幾M的大小,一來(lái)得讓客戶(hù)端花時(shí)間請(qǐng)求、浪費(fèi)用戶(hù)流量,二來(lái)會(huì)造成頁(yè)面字體效果切換的閃動(dòng)(FOUT——flash of unstyled text)現(xiàn)象。
小米作為一個(gè)注重用戶(hù)體驗(yàn)的公司,相信也不會(huì)做這么不合常理的事情(喂,沒(méi)打算聊國(guó)土啊喂)。那么字體這塊,小米自然也是動(dòng)了些手腳——只封裝了頁(yè)面上需要使用到的文字。不信?你可以試著把用到第三方字體的標(biāo)題內(nèi)容更改為其它內(nèi)容,你會(huì)發(fā)現(xiàn)很多文字是不被該字體所支持的:
那么小米的射雞獅真是辛苦,每次修改頁(yè)面都要手動(dòng)打包新的字體,真是兢兢業(yè)業(yè)可歌可泣。。。。其實(shí)未然,畢竟現(xiàn)在不是刀耕火種的原始社會(huì),我們可以直接請(qǐng)node包加持~
于是 “字蛛(FontSpider)” 這款工具可以粉墨登場(chǎng)啦(別亂用成語(yǔ)啊親~)—— 字蛛通過(guò)分析本地 CSS 與 HTML 文件獲取 WebFont 中沒(méi)有使用的字符,并將這些字符數(shù)據(jù)從字體中刪除以實(shí)現(xiàn)壓縮,同時(shí)生成跨瀏覽器使用的格式。
字蛛的使用方式在其官網(wǎng)上已經(jīng)解釋的很清楚了,本文不贅述,但先聊聊@font-face的匹配格式,也就是聊一聊WEB上常用的字體格式。
@font-face中引入的字體文件可以通過(guò)format方法來(lái)幫助瀏覽器匹配到對(duì)應(yīng)的字體格式,常規(guī)各瀏覽器所支持的字體格式有如下幾種:
一、TureTpe(.ttf)格式:
.ttf字體是Windows和Mac的最常見(jiàn)的字體,是一種RAW格式,因此他不為網(wǎng)站優(yōu)化,支持這種字體的瀏覽器有(IE9+,Firefox3.5+,Chrome4+,Safari3+,Opera10+,iOS Mobile Safari4.2+);
二、OpenType(.otf)格式:
.otf字體被認(rèn)為是一種原始的字體格式,其內(nèi)置在TureType的基礎(chǔ)上,所以也提供了更多的功能,支持這種字體的瀏覽器有(Firefox3.5+,Chrome4.0+,Safari3.1+,Opera10.0+,iOS Mobile Safari4.2+);
三、Web Open Font Format(.woff)格式:
.woff字體是Web字體中***格式,他是一個(gè)開(kāi)放的TrueType/OpenType的壓縮版本,同時(shí)也支持元數(shù)據(jù)包的分離,支持這種字體的瀏覽器有(IE9+,Firefox3.5+,Chrome6+,Safari3.6+,Opera11.1+);
四、Embedded Open Type(.eot)格式:
.eot字體是IE專(zhuān)用字體,可以從TrueType創(chuàng)建此格式字體,支持這種字體的瀏覽器有(IE4+);
五、SVG(.svg)格式:
.svg字體是基于SVG字體渲染的一種格式,支持這種字體的瀏覽器有(Chrome4+,Safari3.1+,Opera10.0+,iOS Mobile Safari3.2+)。
那么綜上所述,一個(gè)@font-face只要匹配了.eot 和 其它某種字體(***是.woff),就基本能在大多數(shù)瀏覽器上正常顯示了。不過(guò)查看小米的樣式(點(diǎn)我查看),它只匹配了.woff。該字體文件采用了base64編碼形式,這樣有個(gè)好處——減少了一次文件請(qǐng)求,也能有效防止上文提過(guò)的FOUT現(xiàn)象。
不過(guò)這個(gè)base64怎么折騰出來(lái)的?或許小米用字蛛之類(lèi)的工具獲得字體壓縮文件后,再通過(guò)某種方式(比如在這里轉(zhuǎn)換)將其轉(zhuǎn)為base64編碼形式即可。
另外小米還使用了一個(gè)CSS3樣式:
-webkit-font-smoothing:antialiased
該屬性可以使頁(yè)面上的字體抗鋸齒,使用后字體看起來(lái)會(huì)更清晰舒服(特別適用于字號(hào)較小的文本內(nèi)容)。
有三種可選值:
none | subpixel-antialiased | antialiased
它們的區(qū)別見(jiàn)下圖:
transition動(dòng)畫(huà)
小米的頁(yè)面到處都充滿(mǎn)了視覺(jué)差滾動(dòng)效果,有種隨時(shí)給你小驚喜的感覺(jué):
如上圖的動(dòng)畫(huà),是由transition實(shí)現(xiàn)的,大致步驟如下:
⑴ 給所有要?jiǎng)赢?huà)的元素設(shè)置transition屬性,比如 transition:transform 1s ;
⑵ 給所有動(dòng)畫(huà)元素添加一個(gè)class="visible" ,該class中定義了動(dòng)畫(huà)的最終狀態(tài);
⑶ 頁(yè)面DOMReady的時(shí)候遍歷所有動(dòng)畫(huà)元素($(".visible")),檢查它們是否還沒(méi)被滾動(dòng)條滾上來(lái),如果還在窗口可視區(qū)域下方,則移除它們"visible"的class。該步驟主要是用于處理用戶(hù)下拉頁(yè)面到某個(gè)位置然后刷新頁(yè)面,這時(shí)要求窗口可視區(qū)域及其上方的元素都應(yīng)跳過(guò)動(dòng)畫(huà)的狀態(tài),直接到達(dá)動(dòng)畫(huà)最終狀態(tài);
⑷ 監(jiān)聽(tīng)onscroll事件,移動(dòng)到某個(gè)動(dòng)畫(huà)元素的位置時(shí),則移除該元素名為"visible"的class。
我們可以粗略地寫(xiě)個(gè)場(chǎng)景和腳本來(lái)實(shí)現(xiàn):
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>動(dòng)畫(huà)模擬</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) { //滾動(dòng)頁(yè)面時(shí)的判斷,并添加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{ //初始化頁(yè)面時(shí)的判斷,并刪除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動(dòng)畫(huà)效果默認(rèn)是線(xiàn)性展示的(linear),我們通過(guò)設(shè)置其timing-function屬性可以讓效果變得更靈活,比如這個(gè)效果:
此處的transition-timing-function屬性被設(shè)置為 cubic-bezier(.15, .73, .37, 1.2) ,表示按照該貝塞爾曲線(xiàn)函數(shù)來(lái)執(zhí)行動(dòng)畫(huà)(了解更多請(qǐng)戳我)。
你可以試著把我們上方例子中的 transition:XXX 修改為:
transition: transform 1.5s cubic-bezier(.15, .73, .37, 1.2),opacity 1s;
然后查看其效果:
如果在transition的***加上一個(gè)時(shí)間單位,可以延遲觸發(fā)動(dòng)畫(huà)效果。比如上面五個(gè)手機(jī)(5個(gè)<li>標(biāo)簽)是按順序依次出來(lái)的,那么我們可以給第2個(gè)<li>設(shè)定0.2秒的延遲,給第3個(gè)<li>設(shè)定0.4秒的延遲,給第4個(gè)<li>設(shè)定0.6秒的延遲。。。
我們拿第2個(gè)<li>的transition來(lái)示例:
transition:transform 1s cubic-bezier(.15, .73, .37, 1.2) .2s;
由于在末位寫(xiě)上了0.2s的transition-delay值,故第二個(gè)手機(jī)會(huì)相較***個(gè)手機(jī)完0.2秒執(zhí)行動(dòng)畫(huà)。
#p#
animate動(dòng)畫(huà)
animate動(dòng)畫(huà)很適用于那些需要分段展示不同動(dòng)畫(huà)的場(chǎng)景,比如(該效果頁(yè)面地址):
該卡槽是由一個(gè)div(卡槽本身)內(nèi)嵌一個(gè)span(***淡入顯示的鏤空處文本)組成的,div觸發(fā)動(dòng)畫(huà)時(shí)(跟transition一樣加個(gè)觸發(fā)class)直接從下往上顯示(2s),而span雖然是同時(shí)被觸發(fā)動(dòng)畫(huà),但它延遲2s才執(zhí)行,所以營(yíng)造了“在div動(dòng)畫(huà)結(jié)束后,span才開(kāi)始觸發(fā)動(dòng)畫(huà)”的視覺(jué)效果。
我們照樣拿前面的例子來(lái)修改:
- <!DOCTYPE html>
- <html>
- <head lang="en">
- <meta charset="UTF-8">
- <title>動(dòng)畫(huà)模擬</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) { //滾動(dòng)頁(yè)面時(shí)的判斷,并添加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{ //初始化頁(yè)面時(shí)的判斷,并刪除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>
效果如下:
其實(shí)這段代碼中涉及動(dòng)畫(huà)的最關(guān)鍵的部分不外乎這兩行:
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動(dòng)畫(huà)延遲2秒執(zhí)行,執(zhí)行過(guò)程為1s,另外兩者都使用了forwards來(lái)保持最終狀態(tài)。
另外在介紹wifi的地方還有一個(gè)有趣的循環(huán)動(dòng)畫(huà):
這是在animation中將其動(dòng)畫(huà)執(zhí)行次數(shù)設(shè)置為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的知識(shí)點(diǎn)可點(diǎn)此查閱。
其它
在相機(jī)介紹頁(yè)面,小米還使用了video來(lái)展示一個(gè)小動(dòng)畫(huà)(有些復(fù)雜點(diǎn)的展示效果直接使用小尺寸的視頻來(lái)展示也是個(gè)不錯(cuò)的選擇):
此處代碼為:
- <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>
兩個(gè)source分別匹配了mp4和webm格式的視頻文件,其中mp4是為了兼容IE9+和Safari,webm是為了兼容舊版的Firefox和Opera。除了mp4和webm,其實(shí)還有ogg格式可選,各瀏覽器對(duì)視頻格式的支持度可查看維基百科(非常詳盡)。
***的那張img圖片是用來(lái)優(yōu)雅降級(jí)的,也就是不支持<video>標(biāo)簽的瀏覽器會(huì)直接顯示為這張圖片。
小米在其基礎(chǔ)樣式中還對(duì)全體img使用了 -ms-interpolation-mode:bicubic 屬性,它可以讓IE下被縮小的圖片保持較高質(zhì)量,而不是變得模糊、帶鋸齒。不過(guò)該樣式其實(shí)只對(duì)IE7有作用,因?yàn)镮E8+的缺省值已設(shè)定為bicubic(IE7-下為nearest-neighbor ,圖片被縮放后質(zhì)量會(huì)很差)。
另外小米對(duì)其樣式和腳本均做了混淆和壓縮處理,不過(guò)這已不是什么新奇的東西,只是使用了 grunt 或 gulp 等前端輔助工具罷了。
本篇就介紹到這里,實(shí)際上小米官網(wǎng)上還有很多本章未提及的有趣交互,有興趣的朋友可以去仔細(xì)摸索一番。
話(huà)說(shuō)最近也是蠻拼的,博客越寫(xiě)越長(zhǎng)、越寫(xiě)越晚,醉了。。。
共勉~