canvas實現3D魔方
摘要:使用canvas實現可交互的3D魔方
一、簡單分析
魔方物理性質:
1.中心塊(6個):中心塊與中心軸連接在一起,但可以順著軸的方向自由的轉動。
2.棱塊(12個):棱塊的表面是兩個正方形,結構類似一個長方體從立方體的一個邊凸出來。
3.角塊(8個):角塊的表面是三個正方形,結構類似一個小立方體從立方體的一個邊凸出來,這樣的結構可以讓角塊嵌在三個棱塊之間。
4.可見方塊(26個):魔方對於我們可見的方塊有26塊。
5.魔方六個面,每個面九個方塊,轉動時每個面繞著自己面的中心塊旋轉。
6.在觀察魔方時,我們將整個魔方繞著三維空間中的某個點旋轉
7.在人的視野範圍內,魔方可見的部分只有三個面,19個方塊。
二、實現方式
1)完成方塊的旋轉
通過簡略的類圖,先了解一下實現的思路。在一個三維空間中,最基礎的就是點(Vertex),點組成了面(Face),面組成了空間立方體(Cube)。魔方(MagicCube)由26個Cube組成,同時將26個Cube單組不重復的分為6組(MagicFace)。當旋轉觀察魔方時,26個Cube繞著魔方的中心旋轉。當轉動魔方的某一面時,MagicFace上所有的Cube圍繞以原點為起點,面的中心為終點的矢量旋轉。只需要掌握點在空間中的旋轉,即可完成整個魔方的旋轉。三維空間中點的旋轉主要用到了數學上的旋轉公式。關於如何將一個三維空間的立方體投影到平面並實現旋轉請看我的上一篇隨筆。
Canvas實現3D效果-可旋轉的立方體
2)完成方塊的上色
第一步只是完成了一個透明魔方旋轉。我們需要給魔方上色。初始化時為每個cube的face上色,我們不可見的面設置為白色。就像我們在現實中繪畫一樣,在canvas上繪制圖像時後渲染的圖像會把之前的覆蓋掉,因此魔方的渲染順序我們需要從後往前渲染。
我們通過面的中心距離原點的Y值(本例以Y軸垂直與屏幕作為參考軸),來確定渲染順序。單個方塊的渲染如上圖,6個面(*代表看不見的面),渲染順序為1*,2*,3*,4,5,6,後渲染的面將前面渲染的面蓋住。
整個魔方的渲染,舉例標了數字的三個方塊,也是通過方塊中點距離原點的Y值來確定渲染順序。渲染順序先是方塊1,然後方塊2把1的上半部分蓋住,然後方塊3把方塊二的上半部分蓋住。
3)完成鼠標的點擊事件
交互是本例中比較難的一個點,canvas 沒有提供為其內部元素添加事件監聽的方法,因此如果要使 canvas 內的元素能夠響應事件,需要自己動手實現。通過獲得鼠標在 canvas 上的坐標,計算當前坐標在哪些元素內部,然後對元素進行相應的操作。配合自定義事件,我們就可以實現為 canvas 內的元素添加事件監聽的效果。
在上圖中,可旋轉的面投影到二維平面的區域共有6個,圖中只標識了三個。如何確定投影的區域呢?想了很多方法,最終采用了建立參考點的方法。
1.獲取到3個可視面MagicFace(F1F2F3)---6個面中中心Y值最大的3個
2.獲取到3個可視面無窮遠的參考點(V1V2V3)--6個參考點中Y值最大的3個
4.我們循環6個面,例如循環到圖中的F2面,我們過濾離F2中心最近的參考點(過濾掉與F2中心空間距離最小的點),剩下V1V3
5.獲取到F2面中離V1最最近的三個方塊(即上方紅色區域的三個方塊) -- 離參考點的空間距離
6.獲取三個方塊中裏F2這個面中點最遠的4個點(即上方紅色區域的4個頂點)
通過3層循環,即可獲取到所有可視面的投影。當我們鼠標點擊並滑動一段距離時(圖中黑點),我們就可以判斷兩個點所在的投影的位置是哪一個面的,獲取到面之後就可以旋轉那個面。
PS:我們通過建立參考點,6個無窮遠的點(分別在X,Y,Z軸的正負方向上,魔方整體旋轉時參考點也會旋轉)。使用他與各個元素中點的距離用於判斷一些特殊位置的點和面的方法。滑動部分的投影的8個點就是用參考點輔助求出的。
判斷點是否在面中使用的是pnpoly算法 https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html
三、最終效果
PS:面積投影越小事件效果就不靈敏了,有待優化
源碼下載:https://github.com/sincw/sinwProject/blob/master/webContent/src/main/webapp/work/magiccube/index.html
canvas實現3D魔方