CSS 3D Panorama(全景)之淘寶造物節(jié)技術(shù)剖析
前言
3D 全景并不是什么新鮮事物了,但以前我們在 Web 上看到的 3D 全景一般是通過 Flash 實現(xiàn)的。若我們能將 CSS3 Transform 的相關(guān)知識運用得當,也是能實現(xiàn)類似的效果。換句話說,3D 全景其實就是 CSS3 3D 的應用場景之一。
準備
在實現(xiàn) CSS3 3D 全景之前,我們先理清部分 CSS3 Transform 屬性:
- transform-origin:元素變形的原點(默認值為 50% 50% 0,該數(shù)值和后續(xù)提及的百分比均默認基于元素自身的寬高算出具體數(shù)值);
- perspective:指定了觀察者與 z=0 平面的距離,使具有三維位置變換的元素產(chǎn)生透視效果。(默認值:none,值只能是絕對長度,即負數(shù)是非法值);
- transform-style:用于指定其為子元素提供 2D 還是 3D 的場景。另外,該屬性是非繼承的;
- transform:該屬性能讓你修改 CSS 可視化模型的坐標空間,其中包括 平移(translate)、旋轉(zhuǎn)(rotate)、縮放(scale) 和扭曲(skew)。
下面對上述的一些點進行更深入的分析:
對于 perspective,該屬性指定了“眼睛”與元素的 perspective-origin (默認值是 50% 50%)點的距離。那么問題來了:“當我們應用 px 作為衡量單位時,那它的實際距離該如何量化呢(即如何得到我們熟悉且易于表達的距離)?”
答:當我們的屏幕的分辨率是 1080P(1920*1080px),且該元素或祖先元素的 perspective 值是 1920px 時,該應用了 CSS3 3D Transform 的元素的立體效果就相當于我們在距離一個屏幕寬度(1920px)的屏幕前觀看該元素的真實效果。盡管如此,目前我也不清楚如何準確地為元素設(shè)置一個合適的 perspective 值,只能猜測個大概值,然后再動態(tài)修改值,以達到滿意的呈現(xiàn)效果。
另外,關(guān)于 perspective 還有另外一個重要的點。因為,perspective-origin 的默認值是 50% 50%,因此,對哪個元素應用perspective 屬性,就決定了“眼睛”的位置(即我們的“眼睛”是在哪個角度看物體)。一般來說,當我們需要正視物體時,就會將該屬性設(shè)置在與該元素中心重合的某一祖先元素上。
再另外,如果說:“如何讓一個元素(的背面)不可見?”,你可能會說 backface-visibility:hidden;。其實,對于在“眼睛”背后的元素(以元素的 transform-origin 為參考點),即元素的z軸坐標值大于 perspective 的值,瀏覽器是不會將其渲染出來的。
對于 transform-style,該屬性指定了其子元素是處于 3D 場景還是 2D 場景。對于 2D 場景,元素的前后位置是按照平時的渲染方式(即若在普通文檔流中,是按照代碼中元素的先后順序,后面的元素會遮住在其前面的元素);對于 3D 場景,元素的前后位置則按照真實世界的規(guī)則排序(即越靠近“眼睛”的,會遮住離“眼睛”更遠的元素)。
另外,由于 transform-style 屬性是非繼承的,對于中間節(jié)點需要顯式設(shè)定。
對于 transform 屬性:下圖整理了 rotate3d、translate3d 的變換方向:
transform 中的變換屬性的順序是有關(guān)系的,如 translateX(10px) rotate(30deg) 與 rotate(30deg) translateX(10px) 是不等價的。
另外,需要注意的是 scale 中如果有負數(shù)值,則該方向會產(chǎn)生 180 度的翻轉(zhuǎn);再另外,部分 transform 效果會導致元素(字體)模糊,如 translate 的數(shù)值存在小數(shù)、通過 translateZ 或 scale 放大元素等等。每個瀏覽器都有其不同的表現(xiàn)。
實現(xiàn)
上面理清了一些 CSS Transform 的知識點,下面就講講如何實現(xiàn) CSS 3D 全景 :
想象一下,當我們站在十字路口中間,身體不動,頭部旋轉(zhuǎn) 360°,這個過程中所看到的畫面就形成了一幅以你為中心的全景圖了。其實,當焦距不變時,我們就等同于站在一個圓柱體的中心。
但是,虛擬世界與現(xiàn)實的最大不同是:沒有東西是連續(xù)的,即所有東西都是離散的。例如,你無法在屏幕上顯示一個完美的圓。你只能以一個正多邊形表示圓:邊越多,圓就越“完美”。
同理,在三維空間,每個 3D 模型都等同于一個多面體(即 3D 模型只能由不彎曲的平面組成)。當我們討論一個本身就是多面體(如立方體)的模型時并不足以為奇,但當我們想展示其它模型時,如球體時,就需要記住這個原理了。
淘寶造物節(jié)的活動頁 就是 CSS 3D 全景的一個很贊的頁面,它將全景圖分隔成 20 等份,相鄰的元素間差 18°(360/20)。需要注意的是:我們要確保每個元素的正面是指向圓柱中心的,所以要計算好每等份的旋轉(zhuǎn)角度值。然后再將元素向外(即 Z 軸方向)平移 rpx。對于立方體 r 就是 邊長/2,而對于其它更復雜的正多面體呢?
舉例:對于正九面體,每個元素的寬為 210px,對應的角度為 40°,即如下圖(圖片來自:https://desandro.github.io/3d...)
正九面體的俯視圖
計算過程
由此得到一個公用函數(shù),只需傳入含有元素的寬度和元素數(shù)量的對象,即可得到 r 值:
- function calTranslateZ(opts) {
- return Math.round(opts.width / (2 * Math.tan(Math.PI / opts.number)))
- }
- calTranlateZ({
- width: 210,
- number: 9
- }); // 288
俯視時所看到的元素外移動畫
另外,為了讓下文易于理解,我們約定 html 結(jié)構(gòu)是:
- #view(perspective:1000px)
- #stage(transform-style:preserve-3d)
- #cube(transform-style:preserve-3d)
- .div(width:600px;height:600px;) /*組成立方體的元素*/
正多面體構(gòu)建完成后,就需要將我們的“眼睛”放置在正多面體內(nèi)。由于在“眼睛”后的元素是不會被瀏覽器所渲染的(與 .div元素 是否設(shè)置 backface-visibility:hidden; 無關(guān)),且我們保證了 .div元素 的正面都指向了正多面體的中心,這就形成 360° 被環(huán)繞的效果了。
那“眼睛”具體是被放置在哪個位置呢?
答:通過設(shè)置 #stage 元素的 translateZ 的值,讓不能被看到的 .div元素 的 Z 軸坐標值大于 perspective 的值即可。如:立方體的正面的 translateZ 是 -300px(為了保證立方體的正面是指向立方體中心,正面元素需要設(shè)置 rotateY(-180deg) translateZ(-300px),即正面元素向“眼球”方向平移了 300px),而 #view 的 perspective 的值為 1000px,那么 #stage 的 translateZ 的值應該大于 700px 且小于 1300px,具體數(shù)值則取決于你想要呈現(xiàn)的效果了。
根據(jù)上述知識,我粗略地模仿了“造物節(jié)”的效果:http://jdc.jd.com/lab/zaowu/i...
其實,只需 6 幅圖就可以實現(xiàn)一張常見的無死角全景圖。我自己又試驗了下:http://jdc.jd.com/lab/zaowu/i...
可由下圖看出,將水平的 4 張圖片合成后就是一張全景圖:
理解上述知識后,就可以通過為元素設(shè)置合適的 CSS3 Transform 屬性值,即可實現(xiàn)一張可交互的全景圖了。當然,交互的效果可以是拖拽,也可以是重力感應等。
正如在上文提到的:“沒有東西是連續(xù)的,即所有東西都是離散的...”。將《造物節(jié)》與后續(xù)全景圖的水平方向上的圖片分別合成一張圖后,可以發(fā)現(xiàn):圖片數(shù)量越多,圖片的要求也越低。你覺得呢?
造物節(jié)全景圖
全景圖素材的制作
將全景圖制作分為設(shè)計類與實景類:
設(shè)計類
要制作類似 《淘寶造物節(jié)》 的全景頁面,設(shè)計稿需要有以下這些要求。
注:下面提及的具體數(shù)據(jù)均基于《造物節(jié)》,可根據(jù)自身要求進行調(diào)整(若發(fā)現(xiàn)欠缺,歡迎作出補充)。
整體背景設(shè)計圖如下(2580*1170px,被分成 20 等份):
基本要求:
- 水平方向上需要首尾相連;
- 因為效果圖最終需要切成 N 等份,所以盡可能讓 設(shè)計圖的寬度能被 N 整除;
- 圖片尺寸不僅要考慮正視圖的大小,還要考慮元素在旋轉(zhuǎn)時依然能覆蓋視野(可選)。
當然,上圖只是背景圖,還可以添加一些小物體素材(通過運動速度的差異形成視差,增強立體效果),如:
小物體元素(虛線是用于參考的,造物節(jié)中共有 21 個小物體)
如上圖所示,每個圖片也是被等分成 M 等份。當然,M 取決于物體在背景上的具體位置和本身大小。 另外,M 的寬度是與 N 的寬度相等的。盡管部分物體(M>1)的兩側(cè)等份的圖案占比小,但建議保留同樣的寬度。
注:如果小物體有特殊的變形效果,應該備注具體變形參數(shù)。
對于頂部和底圖圖片,則無特殊要求。
實景類
如果想制作實景的全景,可以看看 Google 街景。
Google 街景 推薦的設(shè)備如下:
如上圖,最實惠的就是最后一個選項—— Google 街景 APP,該應用內(nèi)部提供了全景相機功能,但正如圖片介紹所說,這是需要練習的,因此對操作要求比較高。
補充:上周六(2016.8.20)參加了 TGDC 的分享會,嘉賓分享了他們處理全景的方式:
- 利用 RICOH THETA S 等專業(yè)設(shè)備拍出全景圖
- 導出靜態(tài)圖像
- 利用設(shè)備專門提供的 APP 或 krpamo tools、pano2vr、Glsky box 等工具將靜態(tài)圖像轉(zhuǎn)為6張圖
- 利用 Web 技術(shù)制作可交互的全景圖
其中 Web 技術(shù)有以下3種可選方式(當然,還有其它):
- CSS3(本文所提及的方式)
- Three.js
- krpano(為全景而生,低級瀏覽器則回退到 Flash),查看教程
當時,嘉賓現(xiàn)場快速制作的 會議現(xiàn)場全景。
可見,優(yōu)秀硬件設(shè)備的出現(xiàn),大大減少了后期處理的時間,而 Web 則提供了一個很好的展現(xiàn)平臺。
最后
隨著終端設(shè)備的軟硬件不斷完善和提高,Web 在 3D 領(lǐng)域也不甘落后,如果你玩膩了 2D 的 H5 或者想為用戶提供更加新穎優(yōu)秀的體驗,全景也許是一種選擇。
最后,如有不清晰或不明白的地方,可以聯(lián)系我,我會盡可能解決的。謝謝謝~