iOS 3D變換 -- CALayer的transform

UIView的顯示設(shè)置都是對(duì)CALayer屬性的封裝,但是這層封裝掩蓋了CALayer提供的3D顯示功能姑廉。所以我們想讓UIView顯示3D的效果的話,需要直接操作CALayer。

demo地址:https://github.com/smalldu/IOS-Animations
中的3DTransformDemo

看一個(gè)效果圖(一個(gè)靜態(tài) 一個(gè)動(dòng)態(tài))

立方體
動(dòng)態(tài)立方體

在CALayer中堡纬,我們使用CATransform3D 對(duì)其進(jìn)行3D變換,其中變換比2d變換多一個(gè)Z軸 均函。 在3D變換中三個(gè)坐標(biāo)軸的旋轉(zhuǎn)方向如圖

旋轉(zhuǎn)圖

發(fā)現(xiàn)沒 繞x軸和y軸轉(zhuǎn)都是垂直于屏幕的升熊。

這里我們創(chuàng)建一個(gè)single view application 。拖一個(gè)view上去涩咖。如圖

初始

然后對(duì)它的layer執(zhí)行3d旋轉(zhuǎn)x軸

@IBOutlet weak var v1:UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        var trans = CATransform3DIdentity
        
        let angel = CGFloat(M_PI/4)
        trans = CATransform3DMakeRotation(angel, 1, 0, 0)
        v1.layer.transform = trans
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

效果圖

效果

然而并沒有3D效果 海诲, 只是好像變扁了。怎么回事呢檩互?

這是因?yàn)樘蒯#贑ALayer的顯示系統(tǒng)中,默認(rèn)的相機(jī)使用正交投影闸昨,正交投影沒有遠(yuǎn)小近大效果蚯斯,所以在本例中薄风,只能造成相應(yīng)軸上的縮放。所以不管你旋轉(zhuǎn)多少度都沒有效果

這時(shí)候我們需要用到透視投影拍嵌,CALayer默認(rèn)使用正交投影遭赂,因此沒有遠(yuǎn)小近大效果,而且沒有明確的API可以使用透視投影矩陣横辆。所幸可以通過矩陣連乘自己構(gòu)造透視投影矩陣撇他。

3d變換其實(shí)是一個(gè)矩陣變換

矩陣

??好暈 , 矩陣太復(fù)雜了狈蚤∧娲猓看不懂怎么辦 。幸運(yùn)的是我們不用研究所有點(diǎn) 炫惩。有一個(gè)很特殊的點(diǎn)m34 僻弹。我們只需要設(shè)置這個(gè)點(diǎn)就可以了

CATransform3D的透視效果通過一個(gè)矩陣中一個(gè)很簡(jiǎn)單的元素來控制:m34
。m34用于按比例縮放X和Y的值來計(jì)算到底要離視角多遠(yuǎn)他嚷。

m34的默認(rèn)值是0蹋绽,我們可以通過設(shè)置m34為-1.0 / d來應(yīng)用透視效果,d代表了想象中視角相機(jī)和屏幕之間的距離筋蓖,以像素為單位卸耘,那應(yīng)該如何計(jì)算這個(gè)距離呢?實(shí)際上并不需要粘咖,大概估算一個(gè)就好了蚣抗。

d的大小大家可以試試

       var trans = CATransform3DIdentity
        trans.m34 = -1 / 500
        let angel = CGFloat(M_PI/4)
        trans = CATransform3DMakeRotation(angel, 1, 0, 0)
        v1.layer.transform = trans

效果


t5.png
trans.m34 = -1 / 20

效果

近距離
trans.m34 = -1/3000

效果


遠(yuǎn)距離

d代表了相機(jī)離它的距離,這個(gè)度自己感覺瓮下。一般500-1000效果挺好

下面我們就利用這個(gè)3d變換做一個(gè)立方體 翰铡。這個(gè)要一點(diǎn)空間想象力

首先創(chuàng)建一個(gè)項(xiàng)目拖6個(gè)view都居中,放到一個(gè)容器中

t9.png

然后再利用3d變換畫六個(gè)面讽坏,Container view決定視野,然后根據(jù)手勢(shì)決定視野角度

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var containerV:UIView!
    @IBOutlet weak var v1:UIView!
    @IBOutlet weak var v2:UIView!
    @IBOutlet weak var v3:UIView!
    @IBOutlet weak var v4:UIView!
    @IBOutlet weak var v5:UIView!
    @IBOutlet weak var v6:UIView!
    
    var angel = CGFloat(-M_PI/4)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        var trans = CATransform3DIdentity
        trans.m34 = -1 / 500
   
        
        var trans2 = CATransform3DTranslate(trans, 0, 0, 50)
        v1.layer.transform = trans2
        
        trans2 = CATransform3DTranslate(trans, 50, 0, 0)
        trans2 = CATransform3DRotate(trans2, CGFloat(M_PI_2) , 0 , 1 , 0);
        v2.layer.transform = trans2
        
        trans2 = CATransform3DTranslate(trans, 0, -50, 0)
        trans2 = CATransform3DRotate(trans2, CGFloat(M_PI_2) , 1 , 0 , 0);
        v3.layer.transform = trans2
        
        trans2 = CATransform3DTranslate(trans, 0, 50, 0)
        trans2 = CATransform3DRotate(trans2, CGFloat(-M_PI_2) , -1 , 0 , 0);
        v4.layer.transform = trans2
        
        trans2 = CATransform3DTranslate(trans, -50, 0, 0)
        trans2 = CATransform3DRotate(trans2, CGFloat(-M_PI_2) , 0 , 1 , 0);
        v5.layer.transform = trans2
        
        trans2 = CATransform3DTranslate(trans, 0, 0, -50)
        trans2 = CATransform3DRotate(trans2, CGFloat(M_PI) , 0 , 1 , 0);
        v6.layer.transform = trans2
        
        
        trans = CATransform3DRotate(trans, angel , 0, 1 , 0);
        trans = CATransform3DRotate(trans, angel , 1, 0 , 0);
        containerV.layer.sublayerTransform = trans
        
        let pan = UIPanGestureRecognizer(target: self, action: "handlePan:")
        containerV.addGestureRecognizer(pan)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    
    func handlePan(sender:UIPanGestureRecognizer){
        let p = sender.translationInView(containerV)
        let angel1 = angel+(p.x/30)
        let angel2 = angel-(p.y/30)
        
        var trans = CATransform3DIdentity
        trans.m34 = -1 / 500
        trans = CATransform3DRotate(trans, angel1 , 0, 1 , 0);
        trans = CATransform3DRotate(trans, angel2 , 1, 0 , 0);
        containerV.layer.sublayerTransform = trans
        
        print("拖動(dòng)\(p)")
    }

}

最終效果圖就是上面的gif 锭魔。 大家可以去git上下載源碼運(yùn)行下。效果還不錯(cuò)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末路呜,一起剝皮案震驚了整個(gè)濱河市迷捧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌胀葱,老刑警劉巖漠秋,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異抵屿,居然都是意外死亡庆锦,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門晌该,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肥荔,“玉大人,你說我怎么就攤上這事朝群⊙喙ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵姜胖,是天一觀的道長誉帅。 經(jīng)常有香客問我,道長右莱,這世上最難降的妖魔是什么蚜锨? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮慢蜓,結(jié)果婚禮上亚再,老公的妹妹穿的比我還像新娘。我一直安慰自己晨抡,他們只是感情好氛悬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耘柱,像睡著了一般如捅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上调煎,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天镜遣,我揣著相機(jī)與錄音,去河邊找鬼士袄。 笑死悲关,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娄柳。 我是一名探鬼主播坚洽,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼西土!你這毒婦竟也來了讶舰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤需了,失蹤者是張志新(化名)和其女友劉穎跳昼,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肋乍,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鹅颊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了墓造。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片堪伍。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锚烦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出帝雇,到底是詐尸還是另有隱情涮俄,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布尸闸,位于F島的核電站彻亲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏吮廉。R本人自食惡果不足惜苞尝,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宦芦。 院中可真熱鬧宙址,春花似錦、人聲如沸调卑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽令野。三九已至舀患,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間气破,已是汗流浹背聊浅。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留现使,地道東北人低匙。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像碳锈,于是被迫代替她去往敵國和親顽冶。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容