专栏名称: Andy_Ron
iOSer
目录
相关文章推荐
51好读  ›  专栏  ›  Andy_Ron

系统学习iOS动画之六:3D动画

Andy_Ron  · 掘金  · ios  · 2018-12-27 17:42

正文

阅读 3

系统学习iOS动画之六:3D动画

本文是我学习 《iOS Animations by Tutorials》 笔记中的一篇。 文中详细代码都放在我的Github上 andyRon/LearniOSAnimations

到目前为止,之前的文章只使用了二维动画——这是在平面设备屏幕上动画元素的最自然方式。 毕竟,从iOS 7扁平化后的世界中的按钮,文本字段,开关和图像没有了第三维; 这些元素存在于由X和Y轴定义的平面中:

核心动画 可以帮助我们摆脱这个二维世界; 虽然它不是真正的3D框架,但 核心动画 有很多好的方法可以帮助我们在3D空间中描绘二维对象。

换句话说,图层和动画仍然以二维方式进行描绘,但可以在3D空间中旋转和定位每个元素的2D平面,如下所示:

上面显示的是在3D空间中旋转的两个2D图像。 透视变形使我们可以从渲染器的角度了解它们的位置。

本文将学习如何在3D空间中定位和旋转图层。 CATransform3D 类似于 CGAffineTransform ,但除了在x和y方向上缩放,倾斜和平移之外,它还带来了第三维:z。 z轴直接从设备屏幕朝向您的眼睛。

请考虑以下几个示例,以更好地了解透视的工作原理。

将相机设置得非常靠近屏幕会相应地扭曲图层的视角:

image-20181204230708868

如果将相机离物体比较远时的视角:

image-20181204230723724

最后,如果你在相机和屏幕之间设置了很大的距离:

image-20181204230830029

预览:

24-简单的3D动画 —— 尝试新发现的有关相机距离和视角的知识。设置图层的透视图,处理图层的变换以旋转,平移和缩放三维图层。

25-中级3D动画 —— 在前一章的基础上,既然知道了m34和相机距离的秘密,就可以创建具有多个视图的各种3D动画。

24-简单3D动画

本章将尝新发现的有关相机距离和视角的知识。

开始项目 Office Buddy 是一个办公室帮助应用程序,供员工访问有关日常公司生活的分类信息。这个应用很简单就是点击左上角的按钮或者左右滑到,然后左边侧栏出现。下面👇将向这个开始项目中添加一些3D元素。

开始项目预览:

创造3Dtransformations

打开 ContainerViewController.swift ContainerViewController 在屏幕上显示菜单视图控制器和内容视图控制器。 它还处理平移手势,以便用户可以打开和关闭菜单。

您的第一个任务是构建一个类方法,该方法为侧面菜单的给定百分比“开放性”创建相应的3D变换。 将以下方法声明添加到 ContainerViewController

func menuTransform(percent: CGFloat) -> CATransform3D {

}
复制代码

上述方法接受菜单当前进度的单个参数,该参数由 handleGesture(_ :) 中的代码计算,并返回 CATransform3D 的实例。 您将直接将此方法的结果分配给菜单图层的transform属性。

将以下代码添加到上面方法中:

var identity = CATransform3DIdentity
identity.m34 = -1.0/1000
复制代码

这段代码可能看起来有点令人惊讶; 到目前为止,您只使用函数来创建或修改变换。 但是,这一次,您正在修改其中一个类的属性。

注意: CATransform3D CGAffineTransform 分表表示 4*4 3*3 的数学 矩阵 ,在Swift和OC中都是用结构体表示的。

属性 m34 指矩阵的第3行第4列,这个属性比较常用,表示透视效果, m34 = -1 / D ,D可以理解为 相机距离 ,D越小,透视效果越明显,必须在有旋转效果的前提下,才会看到透视效果。

相机距离

对于普通应用程序中的UI元素,相机距离大概可以表示:
0.1 ... 500 :非常接近,透视失真。
750 ... 2,000 :视角不错,内容清晰可见。
2000+ :几乎没有透视失真。

对于Office Buddy应用程序,1000点的距离将为菜单提供一个非常微妙的视角。

将以下代码添加到 menuTransform(percent:) 的底部:

let remainingPercent = 1.0 - percent
let angle = remainingPercent * .pi * -0.5
复制代码

将以下代码添加到 menuTransform(percent:) 的底部:

let rotationTransform = CATransform3DRotate(identity, angle, 0.0, 1.0, 0.0)
let translationTransform = CATransform3DMakeTranslation(menuWidth * percent, 0, 0)
return CATransform3DConcat(rotationTransform, translationTransform)
复制代码

在这里,使用 rotationTransform 将图层绕y轴旋转。 菜单从左侧移动,因此还需要创建平移变换以沿x轴移动它,最终将菜单宽度设置为100%。 最后,连接两个转换并返回结果。

setMenu(toPercent:) 中删除下面:

menuViewController.view.frame.origin.x = menuWidth * CGFloat(percent) - menuWidth
复制代码

替代为:

menuViewController.view.layer.transform = menuTransform(percent: percent)
复制代码

菜单栏的位置通过转换来控制了。

运行项目, 向右平移查看菜单如何围绕其y轴旋转:

菜单以3D形式旋转,但它围绕其水平中心旋转,菜单与内容视图控制器中间有间隙。

移动图层的锚点

默认情况下,图层的锚点的x坐标为0.5,表示它位于中心。 将锚点的x设置为1.0,就不会出现上面的那种间隙,如下所示:

image-20181205104831488

所有变换都是围绕图层的锚点计算的。

viewDidLoad() 中找到以下行:

menuViewController.view.frame = CGRect(x: -menuWidth, y: 0, width: menuWidth, height: view.frame.height)
复制代码

现在在该行上方插入以下代码(在设置视图帧之前插入行非常重要,否则设置锚点将偏移视图):

menuViewController.view.layer.anchorPoint.x = 1.0
复制代码

这会使菜单围绕其右边缘旋转。

运行效果:

这看起来好多了!

通过阴影创建远景

阴影为3D动画带来了很多真实感。这里不需要使用任何先进的着色技术,只要旋转时更改 alpha

将以下代码添加到 setMenu(toPercent:)

menuViewController.view.alpha = CGFloat(max(0.2, percent))
复制代码

0.2让菜单最小还可见,百分比让菜单越小透明度越低。

由于此应用程序的背景为黑色,因此降低菜单视图的alpha值会使菜单中显示黑色并模拟阴影效果。

运行效果:

这是一个让3D效果更加真实的小细节。

如果仔细观察,会发现第一次点击按钮时,菜单不是以3D效果展示,以后才是。这是因为第一次切换菜单之前,设置3D动画参数和图层转换。在 viewDidLoad() 中添加:

setMenu(toPercent: 0.0)
复制代码

光栅化的效率

让动画更加“完美”。如果在来回平移时盯着菜单足够长,会注意到菜单项的边框看起来像素化,如下所示:

image-20181205110350367

核心动画不断重绘菜单视图控制器的所有内容,并在所有元素移动时重新计算所有元素的透视失真,这个过程中会出现 锯齿状边缘

最好让Core Animation知道我们不会在动画期间更改菜单内容,以便它可以渲染菜单一次并简单地旋转渲染和缓存的图像。 这听起来很复杂,但很容易实现。

找到 handleGesture() 中的 .began 代码块,此代码在用户平移操作时执行。

将以下代码添加到 .began 代码块的末尾:

menuViewController.view.layer.shouldRasterize = true
menuViewController.view.layer.rasterizationScale = UIScreen.main.scale
复制代码

shouldRasterize 让核心动画将图层内容缓存为图像。 然后设置 rasterizationScale 以匹配当前的屏幕比例。

运行,效果:

image-20181205110814153

为避免在使用应用程序时进行任何不必要的缓存,应该在动画完成后立即关闭光栅化。 在 .failed 代码块找到动画完成闭包并添加以下代码:

self.menuViewController.view.layer.shouldRasterize = false
复制代码

现在,只在动画期间激活光栅化。提高了效率!😊

菜单按钮的3D旋转动画

菜单展示时,菜单按钮也进行自身的旋转。具体来说,您将围绕x轴和y轴创建旋转,以使菜单按钮在其对角线上翻转。

ContainerViewController setMenu(toPercent:) 中添加:

let centerVC = centerViewController.viewControllers.first as? CenterViewController
if let menuButton = centerVC?.menuButton {
    menuButton.imageView.layer.transform = buttonTransform(percent: percent)
}
复制代码

buttonTransform 函数为:

func






请到「今天看啥」查看全文