CSS 3D Panorama(全景) - 淘寶造物節技術剖析

本文轉自凹凸實驗室https://aotu.io/notes/2016/08... css

cover.jpg

前言

3D 全景並非什麼新鮮事物了,但之前咱們在 Web 上看到的 3D 全景通常是經過 Flash 實現的。若咱們能將 CSS3 Transform 的相關知識運用得當,也是能實現相似的效果。換句話說,3D 全景其實就是 CSS3 3D 的應用場景之一。html

準備

在實現 CSS3 3D 全景以前,咱們先理清部分 CSS3 Transform 相關的屬性:git

下面咱們對上述的一些點進行更深刻的分析:github

  • 對於 perspective,該屬性指定了「眼睛」與元素的 perspective-origin(默認值是 50% 50% 0)點的距離。那麼問題來了:「當咱們以 px 做爲衡量單位時,它的實際距離該如何量化呢?」
    答:當屏幕分辨率是 1080P(1920*1080px)且該元素或其祖先元素的 perspective 數值的值爲 1920px 時,應用了 CSS3 3D Transform 的子元素的立體效果就至關於咱們在距離一個屏幕寬度(1920px)的屏幕前觀看該元素時的真實效果。儘管如此,目前筆者也不知道如何準確地爲元素設置一個合適的 perspective 值,只能猜想大概值後進行調整,以達到滿意的呈現效果。

此處輸入圖片的描述
根據 類似三角形 的性質可計算出被前移的元素最終在屏幕上顯示的實際大小 瀏覽器

另外,關於 perspective 還有另一個重要的點是:由於 perspective-origin 屬性的默認值是 50% 50% 0,因此對哪一個元素應用 perspective 屬性,就決定了「眼睛」的位置(即咱們的「眼睛」是在哪一個角度看物體)。通常來講,當咱們須要正視物體時,就會將該屬性設置在與該元素中心重合的某一祖先元素上。app

再另外,若是說:「如何讓一個元素(的背面)不可見?」,你可能會回答 backface-visibility:hidden;。其實,對於在「眼睛」背後的元素(以元素的 transform-origin 爲參考點),即元素的 Z 軸座標值大於 perspective 的值時,瀏覽器是不會將其渲染出來的。函數

  • 對於 transform-style,該屬性指定了其子元素是處於 3D 場景仍是 2D 場景。對於 2D 場景,元素的先後位置是按照平時的渲染方式(即若在普通文檔流中,同層級元素是按照代碼中元素的前後編寫順序,後面的元素會遮住在其前面的元素);對於 3D 場景,元素的先後位置則按照真實世界的規則排序(即靠近「眼睛」的元素,會遮住離「眼睛」遠的元素)。

    另外,因爲 transform-style 屬性是非繼承的,對於中間節點須要顯式設定。工具

  • 對於 transform 屬性:下圖整理了 rotate3d、translate3d 的變換方向:
    transform

須要注意的是:transform 中的變換屬性的順序是有關係的,如 translateX(10px) rotate(30deg) 與 rotate(30deg) translateX(10px) 是不等價的。字體

另外,須要注意的是 scale 中若是有負值,則該方向會產生 180 度的翻轉;動畫

再另外,部分 transform 效果會致使元素(字體)模糊,如 translate 的數值存在小數、經過 translateZ 或 scale 放大元素等等。每一個瀏覽器都有其不一樣的表現

實現

上面理清了一些 CSS Transform 相關的知識點,下面就講講如何實現 CSS 3D 全景 :

想象一下,當咱們站在十字路口中間,身體旋轉 360°,這個過程當中所看到的畫面就是一幅以你爲中心的全景圖了。其實,當焦距不變時,咱們就等同於站在一個圓柱體的中心。

可是,虛擬世界與現實世界的最大不一樣是:沒有東西是連續的,即全部東西都是離散的。例如,你沒法在屏幕上顯示一個完美的圓。你只能以一個正多邊形表示圓:邊越多,圓就越「完美」。

同理,在三維空間中,每一個 3D 模型都是一個多面體(即 3D 模型由不可彎曲的平面組成)。當咱們討論一個自己就是多面體(如立方體)的模型時並不足覺得奇,但咱們想展現其它模型時,如球體,就須要記住這個原理了。
三維環境的球體

淘寶造物節的活動頁 就是 CSS 3D 全景的一個很讚的頁面,它將全景圖分割成 20 等份,相鄰的元素構成的夾角 18°(360/20,相鄰兩側面相對於棱柱中心所構成的夾角)。須要注意的是:咱們要確保每一個元素的正面是指向棱柱中心的。因此要計算好每等份的旋轉角度值後,再將元素向外(即 Z 軸方向)平移 r px。對於立方體的 r 就是 邊長/2,而對於其它更復雜的正多面體呢?

舉例:對於正九棱柱,每一個元素的寬爲 210px,對應的角度爲 40°,即以下圖:
圖片來自:https://desandro.github.io/3d...
正九棱柱的俯視圖
正九棱柱的俯視圖

計算過程
計算過程

由此可獲得一個公用函數,只需傳入含有元素的寬度元素數量的對象,便可獲得 r 值:

function calTranslateZ(opts) {
  return Math.round(opts.width / (2 * Math.tan(Math.PI / opts.number)))
}

calTranlateZ({
    width: 210,
    number: 9
});  // 288

俯視時所看到的元素外移動畫
俯視時所看到的元素外移動畫

另外,爲了讓下文易於理解,咱們約定 HTML 的結構:

#view(perspective:1000px)
    #stage(transform-style:preserve-3d)
        #cube(transform-style:preserve-3d)
            .div(width:600px;height:600px;) /*組成立方體的元素*/

正棱柱構建完成後,就須要將咱們的「眼睛」放置在正棱柱內。因爲在「眼睛」後的元素是不會被瀏覽器渲染的(與 .div元素 是否設置 backface-visibility:hidden; 無關),並且咱們保證 .div元素正面都是指向正棱柱中心,這樣就造成 360° 被環繞的效果了。

那「眼睛」具體被放置在哪一個位置呢?
答:經過設置 #stage 元素的 translateZ 值,讓不能被看到的 .div元素Z 軸上的最終座標值(即其自身 Z 座標和祖先元素 Z 座標相加)大於 #view 元素的 perspective 值便可。如:立方體的正面的 translateZ-300px(爲了保證立方體的正面是指向立方體中心,正面元素須要以自身水平方向上的中線爲軸,旋轉 180度,即 rotateY(-180deg) translateZ(-300px),即正面元素向「眼球」方向平移了 300px),而 #viewperspective 值爲 1000px,那麼 #stagetranslateZ 值應該大於 700px 且小於 1300px 便可,具體數值則取決於你想要的呈現效果。

根據上述知識,筆者粗略地模仿了「造物節」的效果:http://jdc.jd.com/lab/zaowu/i...

另外,只需 6 幅圖就能夠實現一張常見的無死角全景圖。
筆者本身又試驗了下:http://jdc.jd.com/lab/zaowu/i...

可由下圖看出,將水平的 4 張圖片合成後就是一張全景圖:
此處輸入圖片的描述

所以,理解上述知識後,經過 CSS3 Transform 相關屬性就能夠實現可交互的全景效果了。固然,交互的效果能夠是拖拽,也能夠是重力感應等。

正如在上文提到的:「沒有東西是連續的,即全部東西都是離散的...」。經過兩個案例的對比能夠發現:圖片數量越多,對圖片的要求也越低。你以爲呢?
淘寶造物節總體效果圖
造物節全景圖

全景圖素材的製做

將全景圖製做分爲設計類與實景類:

設計類

要製做相似 《淘寶造物節》 的全景頁面,設計稿須要有如下這些要求。

注:下面說起的具體數據均基於《造物節》,可根據自身要求進行調整(若發現欠缺,歡迎做出補充)。

總體背景設計圖以下(2580*1170px,被分紅 20 等份):
淘寶造物節總體效果圖

基本要求:

  1. 水平方向上須要首尾相連;
  2. 由於效果圖最終須要切成 N 等份,因此儘量讓 設計圖的寬度能被 N 整除
  3. 圖片尺寸不只要考慮正視圖的大小,還要考慮元素在上下旋轉時依然能覆蓋視野(可選)。

固然,上圖只是做爲背景,咱們還能夠添加一些小物體素材(與背景圖的運動速度不一樣時,可造成視差效果,加強立體感),如:
物體小元素1
物體小元素2
小物體元素(虛線用於參考,造物節中共有 21 個小物體)

如上圖所示,每一個圖片也被等分紅 M 等份,並且 M 的寬度應該與 N(背景元素)的寬度相等(具體緣由,請看文章評論)。

對於頂部和底圖圖片,則無特殊要求。

實景類

若是想製做實景的全景效果,能夠看看 Google 街景:

Google 街景 推薦的設備以下:
Google 街景推薦的設備

如上圖,最實惠的方式就是最後一個選項——Google 街景 APP,該應用提供了全景相機功能,但正如圖片介紹所說,這是須要練習的,所以對操做要求比較高。

補充:
上週六(2016.8.20)參加了 TGDC 的分享會,嘉賓分享了他們處理全景的方式:

  1. 利用 RICOH THETA S 等專業設備拍出全景圖
  2. 導出靜態圖像
  3. 利用設備專門提供的 APP 或 krpamo tools、pano2vr、Glsky box 等工具將靜態圖像轉爲 6 張圖
  4. 利用 Web 技術製做可交互的全景圖

其中 Web 技術有如下 3 種可選方式(固然,還有其它):

  • CSS3(本文所說起的方式)
  • Three.js
  • krpano(爲全景而生,低級瀏覽器則回退到 Flash),查看教程

當時,嘉賓現場快速製做的 會議現場全景

可見,優秀硬件設備的出現,大大減小了後期處理的時間,而 Web 則提供了一個很好的展示平臺。


最後

隨着終端設備的軟硬件不斷完善和提升,Web 在 3D 領域也不甘落後,若是你玩膩了 2D 的 H5 或者想爲用戶提供更加新穎優秀的體驗,全景也許是一種選擇。

最後,若有不清晰或不明白的地方,能夠留言,我會盡量解決的。謝謝謝~

相關文章
相關標籤/搜索