如何做一個讓人聞風喪膽的 H5?(内有(yǒu)幹貨)
前言
最近火熱的有(yǒu)聲娛樂平台 APP,企鵝 FM,在8月28日鬼節前夕,聯合《盜墓筆(bǐ)記》推出了“勇敢者的遊戲”活動。作(zuò)為(wèi)一個 UI 工(gōng)程師,在這個移動互聯網叱咤風雲的時代,每次看到朋友圈中(zhōng)被分(fēn)享的各種花(huā)樣 H5 頁(yè)面,總是心癢難耐,也想做有(yǒu)着酷炫動畫和帶感聲效的 H5 呢(ne)。回想到做鬼節活動頁(yè)的時候,接近午夜零點還在調整頁(yè)面效果,看着頁(yè)面上漸隐漸現的可(kě)怕畫面,活生生吓到了自己,也是蠻難忘的。作(zuò)為(wèi)剛剛來到活動頁(yè)新(xīn)手村的朋友,踩到了一些坑,所以讓我進入正題吧。哦,等一下,請先掃一下二維碼啦~
與設計師的溝通
在拿(ná)到視覺稿和需求單之後,我們需要了解整個活動的流程。有(yǒu)的時候,設計師并不會把每個頁(yè)面的動畫效果做成視頻,而是用(yòng)口述的方式和工(gōng)程師進行溝通。這樣就需要工(gōng)程師結合活動頁(yè)流程和設計稿之後,自己先構思一些動效再去同産(chǎn)品以及設計溝通,這樣交流的效率才會比較高。
動畫新(xīn)手有(yǒu)的時候會天馬行空想到一些奇怪的效果,可(kě)能(néng)會不符合整體(tǐ)設計風格,可(kě)能(néng)會違反現實物(wù)理(lǐ)規律,有(yǒu)自己的想法,還要和産(chǎn)品設計确認。比如這個頁(yè)面:
一開始是做成了先出現手電(diàn)筒,再出現光,在我的設想中(zhōng)剛開始手電(diàn)筒上不會有(yǒu)那層黃綠色的光。後來經設計師提醒:如果完全沒有(yǒu)光源,也應該看不到手電(diàn)筒。才改成了現在的方案:燈光閃兩下:物(wù)理(lǐ)定律什麽的,我才記不清了呢(ne)T T。
P.S. 做動畫的時候銘記動效原則,基本上動畫的效果不會跑偏。
具(jù)體(tǐ)實現
仔細想想,這個活動頁(yè)面并沒有(yǒu)用(yòng)到什麽高深技(jì )巧,基本上是用(yòng) position 定位和 CSS3 動畫完成的。不過在寫頁(yè)面之前,還是有(yǒu)不少擔心的:
-
頁(yè)面兼容怎麽做按照 iPhone6 的尺寸确定元素的位置,然後用(yòng)
zoom
或者transform: scale(x)
拉伸頁(yè)面或者拉伸元素。在實際開發的時候,我一度對這兩個屬性的用(yòng)法産(chǎn)生混淆,所以作(zuò)些了研究,稍後會詳細說說這兩個屬性。 -
3D 變化效果怎麽實現大家應該早就聽說過或者使用(yòng)過
perspective
和perspective-origin
這兩個屬性了,雖然教程和分(fēn)享都看過不少,但真正寫起來還是有(yǒu)一些摸不着頭腦,各種搜索關于 3D 動畫的解釋之後我認為(wèi)原理(lǐ)大概是這樣的:簡單粗暴地說,請想象你是站在上圖中(zhōng)的紅點位置,你與物(wù)體(tǐ)間的距離是 perspective,眼睛的位置是 perspective-origin。好了,現在站定位置,去看這個物(wù)體(tǐ),想象物(wù)體(tǐ)投影在某個平面上的效果(這裏的平面就是我們的顯示屏),這就是 3D 投影的結果。關于perspective-origin
,可(kě)以看看這個demo。
說了這麽多(duō),在實際運用(yòng)中(zhōng),我還是找了一個生成器。
得到的效果大概是這樣的:想要重點說一下磁帶的實現,主要牽涉到的圖片資源是下面這幾個:
磁帶被分(fēn)成了3個部分(fēn),磁帶底部,磁帶封面和磁帶。其中(zhōng)封面和磁帶是正視圖,需要使用(yòng) 3D 旋轉,讓這兩個元素“躺下去”,而且為(wèi)了保證這三個元素之間不會因為(wèi)屏幕的縮放,出現錯位的問題,需要保證磁帶封面和磁帶的定位是基于磁帶底部的。其實磁帶上還有(yǒu)兩片蓋子,不知道大家有(yǒu)沒有(yǒu)注意到。其實這個蓋子是左右對稱且中(zhōng)心對稱的,所以完全可(kě)以隻用(yòng)一張圖片完成這樣的效果,用(yòng)
transform: scale(-1, 1);
和transform: scale(1, -1);
實現。 - 動畫如何才有(yǒu)代入感這個活動頁(yè)面我個人最喜歡的地方就是星星砸下去的動畫,感覺所有(yǒu)的戲份都在它身上。開始的設想隻是星星砸下去,感謝(xiè)在制作(zuò)過程中(zhōng),經驗豐富的同事所提的建議:星星砸下去的時候要有(yǒu)灰塵濺起或者火星四濺的效果。後來和設計商(shāng)量,最後用(yòng)了灰塵濺起的效果。不過本來還想做,星星背後的鐵闆應該要有(yǒu)震動的效果。由于時間關系,震動的效果有(yǒu)點不好添加,所以放棄了,這一點感覺有(yǒu)一些遺憾。可(kě)能(néng)頁(yè)面新(xīn)手的經驗不夠,很(hěn)多(duō)動畫的效果因為(wèi)有(yǒu)限的想象力所以不太完善。這個時候和身邊經驗豐富的同事請教是很(hěn)有(yǒu)效的方法。同時,一定要多(duō)看 B 站拓寬視野才行哦。
踩到了一些坑
頁(yè)面布局并不複雜。前文(wén)也提到,基本上使用(yòng) position: absolute; 來實現的,不過遇到了一些問題就有(yǒu)了以下總結:
- 殺雞就不要用(yòng)牛刀(dāo),能(néng)又(yòu)快又(yòu)好解決問題才最重要上圖是首頁(yè)的截圖,頁(yè)面加載之後應該可(kě)以看到“膽量測試”下面的藍色線(xiàn)條有(yǒu)一個動畫。故事是這樣的,自從 SVG 帝王小(xiǎo)啪帶着神器 svgartisan 降臨(對 SVG 有(yǒu)興趣的同學(xué)歡迎加群 426886128 一同讨論 SVG 技(jì )術),做頁(yè)面的時候總想加上一點 SVG。然而這個藍色線(xiàn)條上有(yǒu)特效,所以用(yòng) PS 導出 SVG 後還要做二次處理(lǐ)才能(néng)還原設計稿上的效果,而且 SVG 的代碼你懂得,它總是有(yǒu)點長(cháng)。其實有(yǒu)個簡單粗暴的方法:改變藍色線(xiàn)條的圖片寬度,雖然這個方法從性能(néng)上看并不好,但考慮到這個頁(yè)面也隻有(yǒu)這裏發生了重繪,所以還是用(yòng)圖片+寬度控制來實現了。不知道大家會不會為(wèi)了某一種技(jì )術而去做某一件事情,但其實完成那件事情才是真正的目的,卻因為(wèi)那個技(jì )術而耽誤了不少進度,這樣就有(yǒu)點進入了炫技(jì )的誤區(qū)。
-
答(dá)應我,僞元素上就不要做動畫了
僞元素真的是神一樣的存在,一個标簽自帶兩個兒子,不知道為(wèi)什麽就有(yǒu)種金閃閃的感覺。但是僞元素上的動畫真的很(hěn)坑,年少無知,頁(yè)面都寫完了,發現在 iOS 上美如畫的動畫效果,到了小(xiǎo)米和魅族上就……總之看到屏幕那一刻我是這樣的:Android 上坑多(duō),不要一次應用(yòng)太多(duō)新(xīn)技(jì )術。比如在魅族上用(yòng) flexbox 布局,就要加上 display: -webkit-box。還有(yǒu)一個教訓就是,頁(yè)面果然應該做一個測試一個啊QAQ。因為(wèi)這個項目是重構和前端并行開發的……把僞元素改成實際 DOM 元素的時候,是懷着一顆對不起前台的心的。 - 請寫好注釋與UI工(gōng)程師對接的前台同學(xué)需要看注釋才知道什麽時候要加什麽class,想到剛剛開始接需求的時候,從來不寫注釋…真是對不起前台同學(xué)T T現在我個人的注釋如上圖所示。也看過組裏不同同事的注釋風格,大同小(xiǎo)異,主體(tǐ)思想就是“在XX時候添加XX class”這樣,和對接的同學(xué)約定好就可(kě)以~
zoom 和 transform: scale(x); 的使用(yòng)
我将會在接下來詳細談到前文(wén)提到的 zoom 和 transform: scale(x); 問題。
為(wèi)什麽要使用(yòng)縮放
現在不管是活動頁(yè)的設計稿還是産(chǎn)品頁(yè)的設計稿,逐漸以 375×667 的 iPhone6 為(wèi)基礎。但是實際生活裏,這些頁(yè)面是會出現在細細長(cháng)長(cháng)的 iPhone5、480px 高度的 iPhone4 還有(yǒu)大屏幕的 iPhone 6+,更不要說在三星小(xiǎo)米魅族一加等等等等尺寸都不知道怎麽辦(bàn)才好的 Android 系列上打開會是什麽gui。
拿(ná)這次的活動頁(yè)面設計稿來說,與用(yòng)戶産(chǎn)生交互的按鈕在頁(yè)面篇底部的位置。有(yǒu)一個前提,為(wèi)了兼容不同寬度的屏幕,所以頁(yè)面是基于 iPhone 6 的 375px 用(yòng) zoom 屬性進行縮放,可(kě)以看出iPhone 5 和 iPhone 4 的寬度一樣,而且看設計稿,頁(yè)面元素是從上到下分(fēn)布的:
也就是說,使用(yòng)相同的 zoom 值,滿足了 iPhone5 的頁(yè)面效果,但是在 iPhone4 上,按鈕就會偏底,頁(yè)面整體(tǐ)感覺也偏底。但是 zoom 值不能(néng)随便更改,否則紅框中(zhōng)的錄音機圖片的左右邊界就會顯示出來。所以要針對 iPhone 4 調整元素的之間的間距,最終的效果大概是這樣的:
可(kě)以看得出效果并不是很(hěn)好,整個頁(yè)面非常的擁擠,所以在這個情況下,我覺得用(yòng)統一 zoom 值來調整有(yǒu)點一棍子打倒了,如果一個個元素微調,那麽最好效果會好得多(duō)。加上 zoom 會有(yǒu)一定的性能(néng)問題,組裏的同事有(yǒu)些是 zoom 調整,也有(yǒu)給每個元素加 class 通過 transform: scale() 調整的。重構最神奇的就是,條條大路通羅馬,怎麽樣都能(néng)達到自己想要的視覺效果,但是中(zhōng)間會因為(wèi)各種原因,實現的方式不太一樣。
拿(ná)到設計稿一開始就先看看這個設計稿的布局,有(yǒu)一些是從頁(yè)面頂部到底部都有(yǒu)效果的,這個時候就要考慮在 iPhone4 這樣屏幕不夠高的設備上如何保證頁(yè)面完整呈現;或者在不影響交互的情況下,隐藏哪些元素。有(yǒu)的時候頁(yè)面上元素比較集中(zhōng),這個時候就要考慮在 iPhone6+ 這樣的大屏幕設備上,要不要調整間距使得頁(yè)面不會太空曠。
要知曉設計稿背後的含義,不是一拿(ná)到就開始做了,有(yǒu)些元素其實是要整體(tǐ)考慮的。有(yǒu)些乍一看好像是用(yòng) position 定位,分(fēn)别寫 top 值就好。殊不知,設計師真正要表達的是,作(zuò)為(wèi)一個整體(tǐ),它們在頁(yè)面上要絕對居中(zhōng)。沒有(yǒu) get 到這個 point,兼容的時候就要哭了。
zoom 和 transform:scale 的概念
先來看一下 zoom
和 transform:scale
的說明:
Specifies the initial zoom factor for the window or viewing area. This is a magnifying glass type of zoom. Interactively changing the zoom factor from the initial zoom factor does not affect the size of the initial or the actual viewport.
從定義上看 zoom 縮放的是被 zoom 的容器的視口,可(kě)以把它想象成放大鏡的效果,這個屬性是可(kě)被繼承的,所以我們做設備屏幕兼容的時候,可(kě)以在 body 标簽下加一個 div 包裹住頁(yè)面上的其他(tā)元素,然後在這個 div 上加 zoom,達到的視覺效果就是頁(yè)面上其他(tā)元素也被縮放了。但是有(yǒu)些元素并不支持 zoom。
A two-dimensional transformation is applied to the coordinate system an element renders in through the ‘transform’ property. This property contains a list of transform functions. The final transformation value for a coordinate system is obtained by converting each function in the list to its corresponding matrix (either defined in this specification or by reference to the SVG specification), then multiplying the matrices.
在說 scale 應該要先看看 transform。transform 屬性應用(yòng)到元素的過程其實是矩陣變換的過程,在渲染的時候,元素的坐(zuò)标就會被确定下來,然後和 transform 的屬性值進行矩陣運算得到最終的坐(zuò)标,不過你會發現,一個絕對定位的元素通過 transform 改變顯示位置後,這個元素的 tbrl 值并不會被更新(xīn),且 transform 屬性不可(kě)繼承的。
The value of the transform property is a list of applied in the order provided. … specifies a scale operation using the [sx,1] scaling vector, where sx is given as the parameter. … specifies a scale operation using the [1,sy] scaling vector, where sy is given as the parameter.
scale 是 transform 的一個屬性值,這是一個縮放矩陣。當一個元素被定義了 transfrom: scale(x); 後,還是再結合它的 transfrom-origin,才能(néng)确定最後的縮放效果。依然是兼容屏幕分(fēn)辨率的問題,要想 transfrom: scale(x) 達到和 zoom 相似的效果,要記得把 transfrom-origin 設置成 0 0。這麽設置的原因是,在文(wén)檔流中(zhōng)的元素,是以它的左上角為(wèi)中(zhōng)心進行 zoom 的,而當元素脫離文(wén)檔流時,要使 transform: scale(x) 和 zoom 達到相同的效果,還要具(jù)體(tǐ)分(fēn)析 transform-origin 要如何設置。
大概你也注意到了,在前一句中(zhōng),我說的是“相似的效果”而不用(yòng)“一樣的效果”,這是因為(wèi)使用(yòng) scale 的時候可(kě)能(néng)遇到下面這樣的問題。下圖中(zhōng)左邊為(wèi) transform:scale(.85),右邊為(wèi) zoom: .85:
下面這段是外層容器的樣式,背景是定義在 switch-wh 動畫中(zhōng),通過絕對定位讓浏覽器自行計算,保證容器大小(xiǎo)占滿整個屏幕:
因為(wèi) zoom 是作(zuò)用(yòng)在 body 下面這個占滿了屏幕空間的容器,所以根據定義以及 zoom 的繼承性,我們可(kě)以說在這個頁(yè)面上使用(yòng) zoom 其實是縮放了整個屏幕(也就是視口),可(kě)以想象成在浏覽器中(zhōng)打開了頁(yè)面,然後放大這個頁(yè)面的效果:
為(wèi)什麽 scale 會留下右部和底部的迷の白色呢(ne)?讓我們回到 transform 的定義中(zhōng),“applied to the coordinate system an element renders in through the ‘transform’ property”。當元素都進行渲染了,坐(zuò)标已經确定了,再進行縮放,也就是在原來元素基礎上改變大小(xiǎo)。所以 .sf-index 雖然在渲染時四個角的位置分(fēn)别是(0,0)、(100%,0)、(0,100%)、(100%, 100%),經過以 (0,0) 為(wèi)變換中(zhōng)心的 scale,就變成了(0,0)、(85%,0)、(0,85%)、(85%,85%)。我們就會看到頁(yè)面右邊出現寬度為(wèi)15%的一條白邊,以及頁(yè)面下方高度為(wèi)15%的白邊。
好像 zoom 無敵了呢(ne)
看起來,好像兼容的時候應該用(yòng) zoom 呢(ne)。嗯看到 scale 之後的結果我就是這麽想的。接着就發現 zoom 之後的頁(yè)面,文(wén)字的顯示不太正常。下圖左邊是被 zoom 的 iPhone4,右邊是沒有(yǒu)被 zoom 的 iPhone6:
由于頁(yè)面是被整體(tǐ)縮放了,所以文(wén)字也自然出現了縮放,剛好這種好像被砍了一刀(dāo)的文(wén)字效果還蠻适合鬼節的活動頁(yè)面,所以我并沒有(yǒu)做處理(lǐ)。正常來說,如果需要做處理(lǐ)就是調整文(wén)字的 line-height 和容器的 height,使其不出現遮擋。
正如頁(yè)面元素經過 zoom 後,實際的大小(xiǎo)會發生改變,圖片的大小(xiǎo)也發生了改變,使用(yòng)雪(xuě)碧圖就出現了一些問題。雪(xuě)碧圖是把各種小(xiǎo)圖拼合到一張大圖上面,通過 width、height 和 background-position 定位到圖片,看下圖可(kě)以發現相鄰圖片的邊界也一起顯示出來了。
審查元素發現,用(yòng)于顯示圖片的元素尺寸也不對啊:
可(kě)以看出這個元素正确的尺寸應該是 198×52,經過 zoom: 1.10 放大後容器反而變小(xiǎo)了,後來将 zoom 值調整到 1.104(414/375),圖片的邊界問題算是解決了。
關于圖片沒有(yǒu)正常顯示的問題,我的推斷是,原因在于 zoom 值設定偏小(xiǎo),圖片經過 zoom 後沒有(yǒu)被正确地計算,而圖片的容器又(yòu)偏大,所以相鄰圖片的邊就被顯示了出來。後來 zoom 值是根據比例設定了,就不會出現這個問題。
最後,zoom 對性能(néng)不友好,下面兩個 gif 分(fēn)别是 zoom 和 transform: scale 引起的重繪:
很(hěn)明顯,在文(wén)檔流中(zhōng) zoom 加在任意一個元素上都會引起整個頁(yè)面的重新(xīn)渲染,而 transform: scale 隻是在當前的元素上重繪。
還有(yǒu)沒有(yǒu)更好的兼容方法呢(ne)
這樣說來,簡直兩個方法都不能(néng)用(yòng)了嘛…還有(yǒu)沒有(yǒu)什麽别的兼容的方法呢(ne)?
有(yǒu)的。
像需要大量圖片的頁(yè)面,做兼容的時候我們常常擔心的是什麽?當然是圖片比例出問題咯,所以也會使用(yòng)通過僞元素設置 padding-top 的方法,保證圖片比例正常地顯示出來。隻是這樣的寫法通常要結合 background-size:cover; 而我們常用(yòng)的工(gōng)具(jù) CssGaga 在生成雪(xuě)碧圖了之後會覆蓋 background-size。目前的這個方案的話……就不合成雪(xuě)碧圖了。
還有(yǒu)一個方案是使用(yòng) media query
結合 rem (或百分(fēn)比)完成這樣的布局,不過目前 gaga 不支持 background-size 的 rem,所以要采用(yòng)什麽方式合成雪(xuě)碧圖以及如何生成新(xīn)的樣式,還需要尋找新(xīn)的方向。在不需要合成雪(xuě)碧圖的時候,可(kě)以用(yòng)這兩種方法。
工(gōng)具(jù)/網站推薦
- 查看設備的屏幕參數
- 設計keyframes
- 動畫靈感哪裏找:http://codepen.io/,https://dribbble.com/
- 無聊時候陪着你的企鵝 FM 推出《鬼吹燈》大結局啦,無聊時候陪着你的企鵝 FM 推出《鬼吹燈》大結局啦,無聊時候陪着你的企鵝 FM 推出《鬼吹燈》大結局啦。聽完盜墓聽鬼吹燈,根本停不下來!
留言