如何在iOS中创建羽化的“圆形擦拭”动画?

问题描述

我的问题需要很多解释。如果您想跳到问题,请在结尾处寻找粗体的“问题:”。

使用Core Animation和蒙版在iOS或Mac OS中创建“划圆”动画相当容易。

一种方法是在视图(通常是图像视图)上安装CAShapeLayer作为遮罩,并在形状层中安装一个圆,然后将圆的大小从0设置为大到可以覆盖整个图像({{ 1}})

另一种方法是使用径向CAGradientLayer作为蒙版,将渐变的中心设置为不透明的颜色,并进行突然过渡以清除图像的角落以外的部分。然后,为渐变层的“位置”值设置动画,以将纯色移入图像的中心。

这两种方法都会产生尖锐的“圆形擦拭”,使图像缩小到一个点并消失。

效果如下:

enter image description here

但是,经典的电影圈擦拭动画具有羽化的边缘,而不是尖锐的边框。圆的外边缘是完全透明的。当您向中心移动时,图像在相当短的距离内变得越来越坚固,然后从那里到中心的图像完全不透明。随着圆的缩小,从透明到不透明的过渡厚度保持恒定,圆变臭到一个点并消失。 (蒙版一个在中间不透明的圆形,边缘略有模糊,圆的半径缩小为0,而模糊边缘的厚度保持不变。)

这是一个旧派圈子擦拭的例子。从技术上讲,这是过渡到黑色的“鸢尾花”,但想法是相同的:

https://youtu.be/IqDhAW3TDR8?t=90

我不知道如何使用形状层作为遮罩来实现带有羽毛边缘的圆形擦拭。形状图层总是锋利的。我可以通过使用形状层来获得相当接近的效果,其中以50%的不透明度描边圆圈,并且以100%的不透明度填充圆圈中间的颜色,但这不会使从透明到不透明的平滑过渡我在追。

我尝试过的另一种方法是使用带有3种颜色的径向渐变。我将内部颜色设置为不透明,将外部颜色设置为透明。我将外部颜色的位置设置为> 1(例如1.2),将中间位置设置为1,将内部位置设置为0。locations数组包含r = sqrt(height^2 + width^2,这使得遮罩图像的整个部分覆盖了图像不透明,带有羽毛状过渡以清除发生在图像边缘以外的地方。

然后我分两个步骤对locations数组进行动画处理。 第一步,我将位置数组设置为[1.2,1.0,0]的动画。这导致一条羽毛带从角落移入并从图像中心停在0.2处。

在第二步中,我将位置数组从[0,0.2]设置为[0,0.2]。这会导致一个内部的,从透明到不透明的中心收缩到一个点并消失。

效果 ok ,如果我以较窄的模糊带和较快的持续时间运行它,效果看起来不错,但这并不完美。

这里是0.2的模糊范围和0.25秒的持续时间的外观:

enter image description here

但是,如果在动画步骤之间引入一个暂停,您会看到效果上的缺陷:

enter image description here

问题:有没有一种方法可以使用Core Animation为从视图的整个尺寸到0的小模糊半径(大约5个点)的圆制作动画?

相同的问题也适用于其他类型的划像动画:锁孔,星形,侧面划像,框等。对于那些,我用于羽毛圆擦的径向渐变技巧根本不起作用。 (您可以使用想要设置动画的形状CAShapeLayer创建那些专门的擦除动画,然后将其大小从大到小进行调整。

首先绘制一个圆,然后对其应用Core Image模糊滤镜,可以轻松创建静态模糊圆(或其他形状),但是在iOS中无法对其进行动画处理,CI模糊滤镜是即使可以,在iOS上也太慢了。

这是我用来在本文中使用我描述的三色径向渐变创建动画的项目的链接https://github.com/DuncanMC/GradientView.git

编辑:

根据詹姆斯的评论,我尝试使用带有阴影的形状图层作为蒙版效果在正确的轨道上,但是仍然存在从图像的不透明部分到被阴影层遮盖的最透明部分的明显过渡。即使阴影不透明度为1.0,阴影仍然大部分是透明的。看起来是这样的:

enter image description here

使圆形的线条颜色部分透明会有所帮助,并在图像的不透明部分和大部分透明的阴影之间过渡。这是上面的图像,但是以2点的线宽和0.6的不透明度抚摸了形状层:

enter image description here

编辑#2:已解决

詹姆斯的使用shadowPath的想法是解决方案。除了shadowPath之外,您不需要遮罩层中的任何东西。如果这样做,则可以使用shadowRadius属性来控制模糊的数量,并从一步将平滑地包围视图的圆的阴影路径平滑地动画化为消失点。

我已经在 https://github.com/DuncanMC/GradientView.git 上更新了我的github项目,以包括径向渐变动画和shadowPath动画进行比较。

这是完成的效果,首先是5像素模糊半径:

ShadowPath based circle wipe with 5 pixel blur

然后使用30像素的模糊半径:

enter image description here

解决方法

您可以在shadowPath上使用CAShapeLayer来创建轮廓模糊的圆形,以用作蒙版。

创建一个没有填充或笔触的形状图层(我们只希望看到阴影),并将其作为图层蒙版添加到视图中。

let shapeLayer = CAShapeLayer()
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOffset = CGSize(width: 0,height: 0)
shapeLayer.shadowOpacity = 1
shapeLayer.shadowRadius = 5
self.maskLayer = shapeLayer
self.layer.mask = shapeLayer

并用CABasicAnimation对其进行动画处理。

func show(_ show: Bool) {
    
    let center = CGPoint(x: self.bounds.width/2,y: self.bounds.height/2)
    var newPath: CGPath
    if show {
        let radius = sqrt(self.bounds.height*self.bounds.height + self.bounds.width*self.bounds.width)/2 //assumes view is square
        newPath = UIBezierPath(arcCenter: center,radius: radius,startAngle: 0,endAngle: CGFloat.pi * 2,clockwise: true).cgPath
    } else {
        newPath = UIBezierPath(arcCenter: center,radius: 0.01,clockwise: true).cgPath
    }
    
    let shadowAnimation = CABasicAnimation(keyPath: "shadowPath")
    shadowAnimation.fromValue = self.maskLayer.shadowPath
    shadowAnimation.toValue = newPath
    shadowAnimation.duration = totalDuration

    self.maskLayer.shadowPath = newPath
    self.maskLayer.add(shadowAnimation,forKey: "shadowAnimation")

}

由于您可以为阴影设置任何路径,因此它也应适用于其他形状(星形,盒子等)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...