搜索
首页web前端html教程[iOS Animation]-CALayer 变换-固体对象_html/css_WEB-ITnose

固体对象

现在你懂得了在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
HTML,CSS和JavaScript的未来:网络开发趋势HTML,CSS和JavaScript的未来:网络开发趋势Apr 19, 2025 am 12:02 AM

HTML的未来趋势是语义化和Web组件,CSS的未来趋势是CSS-in-JS和CSSHoudini,JavaScript的未来趋势是WebAssembly和Serverless。1.HTML的语义化提高可访问性和SEO效果,Web组件提升开发效率但需注意浏览器兼容性。2.CSS-in-JS增强样式管理灵活性但可能增大文件体积,CSSHoudini允许直接操作CSS渲染。3.WebAssembly优化浏览器应用性能但学习曲线陡,Serverless简化开发但需优化冷启动问题。

HTML:结构,CSS:样式,JavaScript:行为HTML:结构,CSS:样式,JavaScript:行为Apr 18, 2025 am 12:09 AM

HTML、CSS和JavaScript在Web开发中的作用分别是:1.HTML定义网页结构,2.CSS控制网页样式,3.JavaScript添加动态行为。它们共同构建了现代网站的框架、美观和交互性。

HTML的未来:网络设计的发展和趋势HTML的未来:网络设计的发展和趋势Apr 17, 2025 am 12:12 AM

HTML的未来充满了无限可能。1)新功能和标准将包括更多的语义化标签和WebComponents的普及。2)网页设计趋势将继续向响应式和无障碍设计发展。3)性能优化将通过响应式图片加载和延迟加载技术提升用户体验。

HTML与CSS vs. JavaScript:比较概述HTML与CSS vs. JavaScript:比较概述Apr 16, 2025 am 12:04 AM

HTML、CSS和JavaScript在网页开发中的角色分别是:HTML负责内容结构,CSS负责样式,JavaScript负责动态行为。1.HTML通过标签定义网页结构和内容,确保语义化。2.CSS通过选择器和属性控制网页样式,使其美观易读。3.JavaScript通过脚本控制网页行为,实现动态和交互功能。

HTML:是编程语言还是其他?HTML:是编程语言还是其他?Apr 15, 2025 am 12:13 AM

HTMLISNOTAPROGRAMMENGUAGE; ITISAMARKUMARKUPLAGUAGE.1)htmlStructures andFormatSwebContentusingtags.2)itworkswithcsssforstylingandjavascript for Interactivity,增强WebevebDevelopment。

HTML:建立网页的结构HTML:建立网页的结构Apr 14, 2025 am 12:14 AM

HTML是构建网页结构的基石。1.HTML定义内容结构和语义,使用、、等标签。2.提供语义化标记,如、、等,提升SEO效果。3.通过标签实现用户交互,需注意表单验证。4.使用、等高级元素结合JavaScript实现动态效果。5.常见错误包括标签未闭合和属性值未加引号,需使用验证工具。6.优化策略包括减少HTTP请求、压缩HTML、使用语义化标签等。

从文本到网站:HTML的力量从文本到网站:HTML的力量Apr 13, 2025 am 12:07 AM

HTML是一种用于构建网页的语言,通过标签和属性定义网页结构和内容。1)HTML通过标签组织文档结构,如、。2)浏览器解析HTML构建DOM并渲染网页。3)HTML5的新特性如、、增强了多媒体功能。4)常见错误包括标签未闭合和属性值未加引号。5)优化建议包括使用语义化标签和减少文件大小。

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

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

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

Atom编辑器mac版下载

Atom编辑器mac版下载

最流行的的开源编辑器

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境