本文轉自凹凸實驗室:https://aotu.io/notes/2016/08... css
3D 全景並非什麼新鮮事物了,但之前咱們在 Web 上看到的 3D 全景通常是經過 Flash 實現的。若咱們能將 CSS3 Transform
的相關知識運用得當,也是能實現相似的效果。換句話說,3D 全景其實就是 CSS3 3D 的應用場景之一。html
在實現 CSS3 3D 全景以前,咱們先理清部分 CSS3 Transform 相關的屬性:git
下面咱們對上述的一些點進行更深刻的分析:github
perspective
,該屬性指定了「眼睛」與元素的 perspective-origin
(默認值是 50% 50% 0
)點的距離。那麼問題來了:「當咱們以 px
做爲衡量單位時,它的實際距離該如何量化呢?」 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 中的變換屬性的順序是有關係的,如 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),而 #view
的 perspective
值爲 1000px
,那麼 #stage
的 translateZ
值應該大於 700px
且小於 1300px
便可,具體數值則取決於你想要的呈現效果。
根據上述知識,筆者粗略地模仿了「造物節」的效果:http://jdc.jd.com/lab/zaowu/i...
另外,只需 6 幅圖就能夠實現一張常見的無死角全景圖。
筆者本身又試驗了下:http://jdc.jd.com/lab/zaowu/i...
可由下圖看出,將水平的 4 張圖片合成後就是一張全景圖:
所以,理解上述知識後,經過 CSS3 Transform 相關屬性就能夠實現可交互的全景效果了。固然,交互的效果能夠是拖拽,也能夠是重力感應等。
正如在上文提到的:「沒有東西是連續的,即全部東西都是離散的...」。經過兩個案例的對比能夠發現:圖片數量越多,對圖片的要求也越低。你以爲呢?
造物節全景圖
將全景圖製做分爲設計類與實景類:
要製做相似 《淘寶造物節》 的全景頁面,設計稿須要有如下這些要求。
注:下面說起的具體數據均基於《造物節》,可根據自身要求進行調整(若發現欠缺,歡迎做出補充)。
總體背景設計圖以下(2580*1170px,被分紅 20 等份):
基本要求:
固然,上圖只是做爲背景,咱們還能夠添加一些小物體素材(與背景圖的運動速度不一樣時,可造成視差效果,加強立體感),如:
小物體元素(虛線用於參考,造物節中共有 21 個小物體)
如上圖所示,每一個圖片也被等分紅 M 等份,並且 M 的寬度應該與 N(背景元素)的寬度相等(具體緣由,請看文章評論)。
對於頂部和底圖圖片,則無特殊要求。
若是想製做實景的全景效果,能夠看看 Google 街景:
Google 街景 推薦的設備以下:
如上圖,最實惠的方式就是最後一個選項——Google 街景 APP,該應用提供了全景相機功能,但正如圖片介紹所說,這是須要練習的,所以對操做要求比較高。
補充:
上週六(2016.8.20)參加了 TGDC 的分享會,嘉賓分享了他們處理全景的方式:
其中 Web 技術有如下 3 種可選方式(固然,還有其它):
當時,嘉賓現場快速製做的 會議現場全景。
可見,優秀硬件設備的出現,大大減小了後期處理的時間,而 Web 則提供了一個很好的展示平臺。
隨着終端設備的軟硬件不斷完善和提升,Web 在 3D 領域也不甘落後,若是你玩膩了 2D 的 H5 或者想爲用戶提供更加新穎優秀的體驗,全景也許是一種選擇。
最後,若有不清晰或不明白的地方,能夠留言,我會盡量解決的。謝謝謝~