这篇文章是系列文章的第三篇。git
看过上一篇文章的朋友,已经知道标题中的“景”指代 view,“窗”指代 view.mask,窗景篇就是在梳理 mask 及 mask 动画。若是你还不熟悉 iOS 的 mask,建议先看一下第一篇。github
前两篇咱们介绍了 mask、mask 动画的一些用法。swift
这一篇做为收尾,咱们来实现一个效果练练手, 也借这个效果,让你们回忆起一个简单的道理:复杂的效果,能够等价于简单效果的组合。oop
这个效果以下面的动图所示:post
咱们截取比较有表明性的一帧,以下图所示:性能
从图中能够看到,波浪由两种颜色组成,各部分颜色不一样。动画
这个效果看上去有点复杂,若是不熟悉 mask,可能一时半会儿没有思路。 但看过前两篇的朋友,可能已经暗暗在想,是窗在动?仍是景在动?会不会有多套窗景?ui
那么接下来,咱们先经过一个简单的效果来看一下原理。spa
注:波浪动画的实现和本文关系不大,本文不会讲述。 网上有成熟的波浪动画的教程,本文 demo 中 WaveView 类也有简要的注释。code
这个效果以下图所示:
从图中能够看到,一张黑白图片上有一部分是彩色的。 咱们固然能够经过图像处理来实现这个效果,但在本文中,咱们仍是使用 mask 的方式来实现。
咱们回忆一下前文中的一张图:
经过对frontView 添加一个圆 mask,就造成了图中的效果。
也许有的朋友已经想到,把上图中 backView 的图换成和 frontView 同样的黑白图片,不就是本例的效果吗,以下图所示:
也就是说,这个效果看上去是黑白图片上有一部分变成了彩色, 但其实只是两张内容同样的图片重叠,黑白图片在后,彩色图片在前,而前方的彩色图片,被施加了圆形的 mask。
这个效果很简单,但能让咱们意识到一件事:看上去是一张图,其实多是多张图组合而成。
既然如此,那本文的波浪动画中,各部分颜色不一样的波浪,真的只是一个波浪吗?
没错,本例中的波浪,也是多个波浪组合而成的,接下来,咱们就详细的看一看。
和黑白、彩色图片重叠效果同样,多色波浪也是由一组重叠的波浪 view 组合而成。
每层 view 的波浪只有一种颜色,各层 view 的波浪动画都一致,对于每一帧,全部波浪都是彻底重合的。
每一个波浪 view 都有本身的 mask,在 mask 们 的控制下,每层波浪 view 只显示了波浪的一部分,咱们看到的多色波浪,就是各层波浪 view 可见部分的组合。
咱们取两层来示意一下,以下图所示:
从图中能够看到,白底红波浪 view 有个上半圆 mask、黑底蓝波浪 view 有个下半圆 mask,两个 view 的波浪进度彻底一致,组合以后就成了最右边的的效果。
捅破了这层窗户纸后,其实原理就是如此简单。
知道了原理,其实你们能够本身去动手去实现效果了, 固然,若是不着急的话,那我们一块把流程走一遍。
这一步很简单,建立 frame 彻底一致的 4 层 view,本例中使用 WaveView 做为 view, 根据须要,设置不一样的背景色(黑、白)和波浪色(红、蓝)。
这一步后,4 层波浪 view 如图所示:
示意代码以下:
// A3WaveViewController
private func addSubViews() {
// 上大半圆 view
view.addSubview(bigTopView)
// 上小半圆 view
view.addSubview(smallTopView)
// 下大半圆 view
view.addSubview(bigBottomView)
// 下小半圆 view
view.addSubview(smallBottomView)
}
复制代码
这一步就是作出合适的 mask 。 本例中使用 HalfCircleView 做为 mask,分别为各层 view 设置两个大半圆和两个小半圆的 mask。
这一步后,4层 view 在 mask 的影响下以下图所示,你们能够和前文图中的 4个 view 对照着看:
示意代码以下:
// A3WaveViewController
private func makeLayout() {
// 设置4个 view 的 mask
// 大上半圆
let width: CGFloat = 200
let marginX = (UIScreen.main.bounds.width - width) / 2
bigTopView.frame = CGRect(x: marginX, y: 200, width: width, height: 200)
// 大上半圆 mask
let bigTopMask = HalfCircleView()
bigTopMask.part = .top
bigTopMask.frame = CGRect(x: 0, y: 0, width: bigTopView.bounds.width, height: bigTopView.bounds.height / 2)
bigTopView.mask = bigTopMask
// 小上半圆(半径是大半圆的一半)
smallTopView.frame = bigTopView.frame
// 小上半圆 mask
let smallTopMask = HalfCircleView()
smallTopMask.part = .top
smallTopMask.frame = CGRect(x: smallTopView.bounds.width / 4,
y: smallTopView.bounds.height / 4,
width: smallTopView.bounds.width / 2,
height: smallTopView.bounds.height / 4)
smallTopView.mask = smallTopMask
// 大下半圆
bigBottomView.frame = bigTopView.frame
// 大下半圆 mask
let bigBottomMask = HalfCircleView()
bigBottomMask.part = .bottom
bigBottomMask.frame = CGRect(x: 0,
y: bigBottomView.bounds.height / 2,
width: bigBottomView.bounds.width,
height: bigBottomView.bounds.height / 2)
bigBottomView.mask = bigBottomMask
// 小下半圆
smallBottomView.frame = bigBottomView.frame
// 小下半圆 mask
let smallBottomMask = HalfCircleView()
smallBottomMask.part = .bottom
smallBottomMask.frame = CGRect(x: smallBottomView.bounds.width / 4,
y: smallBottomView.bounds.height / 2,
width: smallBottomView.bounds.width / 2,
height: smallBottomView.bounds.height / 4)
smallBottomView.mask = smallBottomMask
}
复制代码
为了让各层波浪动画彻底一致,咱们在外部启动一个 CADisplayLink,来同时改变 4 个波浪 view 的 progress(前文图中使用了 progress 为 0.5 时做为示例)。
也就实现了本篇开始的效果:
示意代码以下:
// A3WaveViewController
func start() {
if let displayLink = displayLink {
displayLink.invalidate()
self.displayLink = nil
progress = 0
}
// 启动 CADisplayLink
let displayLink = CADisplayLink(target: WeakProxy(self), selector: #selector(work))
displayLink.add(to: RunLoop.current, forMode: .common)
self.displayLink = displayLink
}
@objc private func work() {
if progress < 1 {
progress += 0.0025
progress = min(progress, 1)
} else {
progress = 0
}
// CADisplayLink 回调时,设置4个波浪 view 的 progress
bigTopView.progress = progress
bigBottomView.progress = progress
smallTopView.progress = progress
smallBottomView.progress = progress
}
复制代码
至此,效果就完成了。
固然,你能够用本身的动画 view 替换掉 WaveView、改变 view 的个数、使用其余的 mask等, 来实现本身想要的拼接效果。
固然,这种动画方式的性能未评估,也许不适合用在生产环境。 这个例子更多地是想让你们回忆起一个简单的道理:
复杂的动画,能够等价为简单动画的组合;只要还以为复杂,就能够继续拆分。
若是你想要拆分,以为不知道从何处下手,那么能够这么尝试:
只要某一部分的动画和其余部分有区别,就能够拆分。
简单的动画一旦实现了,组合起来了就完成了复杂的动画。
组合是咱们经常使用的方法,好比下图的双波浪:
咱们固然能够直接写个双波浪的类,但也能够组合两个波浪 view 来造成双波浪。
组合看上去有点傻,不过也有它的优点, 好比咱们想复用的效果很复杂,难以改写,或者咱们根本没有该效果的源码时,组合可能就是最简单的方式。
本系列只是展现了常见的 mask 效果,挂一漏万,毕竟窗无限,景无限,组合无限,效果无限。
若是你发现了有意思的 mask 动画,欢迎在评论区留言,愿咱们共同进步。
本文全部示例,在 GitHub 库 里都有完整的代码。
本系列至此完结,感谢您的阅读。