目錄
固体对象
光亮和阴影
点击事件
总结
首頁 web前端 html教學 [iOS Animation]-CALayer 变换-固体对象_html/css_WEB-ITnose

[iOS Animation]-CALayer 变换-固体对象_html/css_WEB-ITnose

Jun 21, 2016 am 09:06 AM

固体对象

现在你懂得了在3D空间的一些图层布局的基础,我们来试着创建一个固态的3D对象(实际上是一个技术上所谓的空洞对象,但它以固态呈现)。我们用六个独立的视图来构建一个立方体的各个面。

在这个例子中,我们用Interface Builder来构建立方体的面(图5.19),我们当然可以用代码来写,但是用Interface Builder的好处是可以方便的在每一个面上添加子视图。记住这些面仅仅是包含视图和控件的普通的用户界面元素,它们完全是我们界面交互的部分,并且当把它折成一个立方体之后也不会改变这个性质。

图5.19 用Interface Builder对立方体的六个面进行布局

这些面视图并没有放置在主视图当中,而是松散地排列在根nib文件里面。我们并不关心在这个容器中如何摆放它们的位置,因为后续将会用图层的transform对它们进行重新布局,并且用Interface Builder在容器视图之外摆放他们可以让我们容易看清楚它们的内容,如果把它们一个叠着一个都塞进主视图,将会变得很难看。

我们把一个有颜色的 UILabel 放置在视图内部,是为了清楚的辨别它们之间的关系,并且 UIButton 被放置在第三个面视图里面,后面会做简单的解释。

具体把视图组织成立方体的代码见清单5.9,结果见图5.20

清单5.9 创建一个立方体

@interface ViewController ()@property (nonatomic, weak) IBOutlet UIView *containerView;@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces; @end @implementation ViewController - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{ //get the face view and add it to the container UIView *face = self.faces[index];    [self.containerView addSubview:face]; //center the face view within the container CGSize containerSize = self.containerView.bounds.size;    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0); // apply the transform face.layer.transform = transform;} - (void)viewDidLoad{    [super viewDidLoad]; //set up the container sublayer transform CATransform3D perspective = CATransform3DIdentity;    perspective.m34 = -1.0 / 500.0;    self.containerView.layer.sublayerTransform = perspective; //add cube face 1 CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);    [self addFace:0 withTransform:transform]; //add cube face 2 transform = CATransform3DMakeTranslation(100, 0, 0);    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);    [self addFace:1 withTransform:transform]; //add cube face 3 transform = CATransform3DMakeTranslation(0, -100, 0);    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);    [self addFace:2 withTransform:transform]; //add cube face 4 transform = CATransform3DMakeTranslation(0, 100, 0);    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);    [self addFace:3 withTransform:transform]; //add cube face 5 transform = CATransform3DMakeTranslation(-100, 0, 0);    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);    [self addFace:4 withTransform:transform]; //add cube face 6 transform = CATransform3DMakeTranslation(0, 0, -100);    transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);    [self addFace:5 withTransform:transform];} @end
登入後複製

图5.20 正面朝上的立方体

从这个角度看立方体并不是很明显;看起来只是一个方块,为了更好地欣赏它,我们将更换一个不同的视角。

旋转这个立方体将会显得很笨重,因为我们要单独对每个面做旋转。另一个简单的方案是通过调整容器视图的sublayerTransform去旋转照相机。

添加如下几行去旋转containerView图层的perspective变换矩阵:

perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0); perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);
登入後複製

这就对相机(或者相对相机的整个场景,你也可以这么认为)绕Y轴旋转45度,并且绕X轴旋转45度。现在从另一个角度去观察立方体,就能看出它的真实面貌(图5.21)。

图5.21 从一个边角观察的立方体

光亮和阴影

现在它看起来更像是一个立方体没错了,但是对每个面之间的连接还是很难分辨。Core Animation可以用3D显示图层,但是它对光线并没有概念。如果想让立方体看起来更加真实,需要自己做一个阴影效果。你可以通过改变每个面的背景颜色或者直接用带光亮效果的图片来调整。

如果需要动态地创建光线效果,你可以根据每个视图的方向应用不同的alpha值做出半透明的阴影图层,但为了计算阴影图层的不透明度,你需要得到每个面的正太向量(垂直于表面的向量),然后根据一个想象的光源计算出两个向量叉乘结果。叉乘代表了光源和图层之间的角度,从而决定了它有多大程度上的光亮。

清单5.10实现了这样一个结果,我们用GLKit框架来做向量的计算(你需要引入GLKit库来运行代码),每个面的CATransform3D都被转换成GLKMatrix4,然后通过GLKMatrix4GetMatrix3函数得出一个3×3的旋转矩阵。这个旋转矩阵指定了图层的方向,然后可以用它来得到正太向量的值。

结果如图5.22所示,试着调整 LIGHT_DIRECTION 和 AMBIENT_LIGHT 的值来切换光线效果

清单5.10 对立方体的表面应用动态的光线效果

#import "ViewController.h" #import <QuartzCore/QuartzCore.h> #import <GLKit/GLKit.h> #define LIGHT_DIRECTION 0, 1, -0.5 #define AMBIENT_LIGHT 0.5 @interface ViewController ()@property (nonatomic, weak) IBOutlet UIView *containerView;@property (nonatomic, strong) IBOutletCollection(UIView) NSArray *faces; @end @implementation ViewController - (void)applyLightingToFace:(CALayer *)face{ //add lighting layer CALayer *layer = [CALayer layer];    layer.frame = face.bounds;    [face addSublayer:layer]; //convert the face transform to matrix //(GLKMatrix4 has the same structure as CATransform3D) //译者注:GLKMatrix4和CATransform3D内存结构一致,但坐标类型有长度区别,所以理论上应该做一次float到CGFloat的转换,感谢[@zihuyishi](https://github.com/zihuyishi)同学~ CATransform3D transform = face.transform;    GLKMatrix4 matrix4 = *(GLKMatrix4 *)&transform;    GLKMatrix3 matrix3 = GLKMatrix4GetMatrix3(matrix4); //get face normal GLKVector3 normal = GLKVector3Make(0, 0, 1);    normal = GLKMatrix3MultiplyVector3(matrix3, normal);    normal = GLKVector3Normalize(normal); //get dot product with light direction GLKVector3 light = GLKVector3Normalize(GLKVector3Make(LIGHT_DIRECTION)); float dotProduct = GLKVector3DotProduct(light, normal); //set lighting layer opacity CGFloat shadow = 1 + dotProduct - AMBIENT_LIGHT;    UIColor *color = [UIColor colorWithWhite:0 alpha:shadow];    layer.backgroundColor = color.CGColor;} - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform{ //get the face view and add it to the container UIView *face = self.faces[index];    [self.containerView addSubview:face]; //center the face view within the container CGSize containerSize = self.containerView.bounds.size;    face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0); // apply the transform face.layer.transform = transform; //apply lighting  [self applyLightingToFace:face.layer];} - (void)viewDidLoad{    [super viewDidLoad]; //set up the container sublayer transform CATransform3D perspective = CATransform3DIdentity;    perspective.m34 = -1.0 / 500.0;    perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0);    perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0);    self.containerView.layer.sublayerTransform = perspective; //add cube face 1 CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100);    [self addFace:0 withTransform:transform]; //add cube face 2 transform = CATransform3DMakeTranslation(100, 0, 0);    transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0);    [self addFace:1 withTransform:transform]; //add cube face 3 transform = CATransform3DMakeTranslation(0, -100, 0);    transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0);    [self addFace:2 withTransform:transform]; //add cube face 4 transform = CATransform3DMakeTranslation(0, 100, 0);    transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0);    [self addFace:3 withTransform:transform]; //add cube face 5 transform = CATransform3DMakeTranslation(-100, 0, 0);    transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0);    [self addFace:4 withTransform:transform]; //add cube face 6 transform = CATransform3DMakeTranslation(0, 0, -100);    transform = CATransform3DRotate(transform, M_PI, 0, 1, 0);    [self addFace:5 withTransform:transform];} @end
登入後複製

 

图5.22 动态计算光线效果之后的立方体

点击事件

你应该能注意到现在可以在第三个表面的顶部看见按钮了,点击它,什么都没发生,为什么呢?

这并不是因为iOS在3D场景下正确地处理响应事件,实际上是可以做到的。问题在于视图顺序。在第三章中我们简要提到过,点击事件的处理由视图在父视图中的顺序决定的,并不是3D空间中的Z轴顺序。当给立方体添加视图的时候,我们实际上是按照一个顺序添加,所以按照视图/图层顺序来说,4,5,6在3的前面。

即使我们看不见4,5,6的表面(因为被1,2,3遮住了),iOS在事件响应上仍然保持之前的顺序。当试图点击表面3上的按钮,表面4,5,6截断了点击事件(取决于点击的位置),这就和普通的2D布局在按钮上覆盖物体一样。

你也许认为把doubleSided设置成NO可以解决这个问题,因为它不再渲染视图后面的内容,但实际上并不起作用。因为背对相机而隐藏的视图仍然会响应点击事件(这和通过设置hidden属性或者设置alpha为0而隐藏的视图不同,那两种方式将不会响应事件)。所以即使禁止了双面渲染仍然不能解决这个问题(虽然由于性能问题,还是需要把它设置成NO)。

这里有几种正确的方案:把除了表面3的其他视图userInteractionEnabled属性都设置成NO来禁止事件传递。或者简单通过代码把视图3覆盖在视图6上。无论怎样都可以点击按钮了(图5.23)。

图5.23 背景视图不再阻碍按钮,我们可以点击它了

总结

这一章涉及了一些2D和3D的变换。你学习了一些矩阵计算的基础,以及如何用Core Animation创建3D场景。你看到了图层背后到底是如何呈现的,并且知道了不能把扁平的图片做成真实的立体效果,最后我们用demo说明了触摸事件的处理,视图中图层添加的层级顺序会比屏幕上显示的顺序更有意义。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

HTML容易為初學者學習嗎? HTML容易為初學者學習嗎? Apr 07, 2025 am 12:11 AM

HTML適合初學者學習,因為它簡單易學且能快速看到成果。 1)HTML的學習曲線平緩,易於上手。 2)只需掌握基本標籤即可開始創建網頁。 3)靈活性高,可與CSS和JavaScript結合使用。 4)豐富的學習資源和現代工具支持學習過程。

HTML,CSS和JavaScript的角色:核心職責 HTML,CSS和JavaScript的角色:核心職責 Apr 08, 2025 pm 07:05 PM

HTML定義網頁結構,CSS負責樣式和佈局,JavaScript賦予動態交互。三者在網頁開發中各司其職,共同構建豐富多彩的網站。

了解HTML,CSS和JavaScript:初學者指南 了解HTML,CSS和JavaScript:初學者指南 Apr 12, 2025 am 12:02 AM

WebDevelovermentReliesonHtml,CSS和JavaScript:1)HTMLStructuresContent,2)CSSStyleSIT和3)JavaScriptAddSstractivity,形成thebasisofmodernWebemodernWebExexperiences。

HTML中起始標籤的示例是什麼? HTML中起始標籤的示例是什麼? Apr 06, 2025 am 12:04 AM

AnexampleOfAstartingTaginHtmlis,beginSaparagraph.startingTagSareEssentialInhtmlastheyInitiateEllements,defiteTheeTheErtypes,andarecrucialforsstructuringwebpages wepages webpages andConstructingthedom。

Gitee Pages靜態網站部署失敗:單個文件404錯誤如何排查和解決? Gitee Pages靜態網站部署失敗:單個文件404錯誤如何排查和解決? Apr 04, 2025 pm 11:54 PM

GiteePages靜態網站部署失敗:404錯誤排查與解決在使用Gitee...

如何用CSS3和JavaScript實現圖片點擊後周圍圖片散開並放大效果? 如何用CSS3和JavaScript實現圖片點擊後周圍圖片散開並放大效果? Apr 05, 2025 am 06:15 AM

實現圖片點擊後周圍圖片散開並放大效果許多網頁設計中,需要實現一種交互效果:點擊某張圖片,使其周圍的...

網頁批註如何實現Y軸位置的自適應佈局? 網頁批註如何實現Y軸位置的自適應佈局? Apr 04, 2025 pm 11:30 PM

網頁批註功能的Y軸位置自適應算法本文將探討如何實現類似Word文檔的批註功能,特別是如何處理批註之間的間�...

HTML,CSS和JavaScript:Web開發人員的基本工具 HTML,CSS和JavaScript:Web開發人員的基本工具 Apr 09, 2025 am 12:12 AM

HTML、CSS和JavaScript是Web開發的三大支柱。 1.HTML定義網頁結構,使用標籤如、等。 2.CSS控製網頁樣式,使用選擇器和屬性如color、font-size等。 3.JavaScript實現動態效果和交互,通過事件監聽和DOM操作。

See all articles