如何创建羽毛状的“圆形抹布"?iOS中的动画? [英] How do you create a feathered "Circle wipe" animation in iOS?

查看:78
本文介绍了如何创建羽毛状的“圆形抹布"?iOS中的动画?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题需要很多解释.如果您想跳到该问题,请查找粗体的问题:"到最后.

创建圆形擦"很容易.使用Core Animation和遮罩在iOS或Mac OS中制作动画.

一种方法是在视图(通常是图像视图)上安装CAShapeLayer作为遮罩,并在形状层中安装一个圆,然后将圆的大小从0设置为动画,使其大小足以覆盖整个图像( r = sqrt(高度^ 2 +宽度^ 2 )

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

这些方法中的两种都产生了锋利的圆形擦拭物".导致图像缩小到一定程度并消失.

效果如下:

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

这是一个旧派圈子擦拭的例子.从技术上讲,这是一个虹膜输出".过渡到黑色,但想法是一样的:

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

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

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

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

这是我用来在本文中使用描述的3色径向渐变创建动画的项目的链接:

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

编辑#2:已解决!

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

我已经在

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

解决方案

您可以在 CAShapeLayer 上使用 shadowPath 创建轮廓模糊的圆形,以用作面具.

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

  let shapeLayer = CAShapeLayer()shapeLayer.shadowColor = UIColor.black.cgColorshapeLayer.shadowOffset = CGSize(宽度:0,高度:0)shapeLayer.shadowOpacity = 1shapeLayer.shadowRadius = 5self.maskLayer = shapeLayerself.layer.mask = shapeLayer 

并使用 CABasicAnimation 对其进行动画处理.

  func show(_ show:Bool){让中心= CGPoint(x:self.bounds.width/2,y:self.bounds.height/2)var newPath:CGPath如果显示{let radius = sqrt(self.bounds.height * self.bounds.height + self.bounds.width * self.bounds.width)/2//假设视图是正方形newPath = UIBezierPath(arcCenter:中心,半径:半径,startAngle:0,endAngle:CGFloat.pi * 2,顺时针:true).cgPath} 别的 {newPath = UIBezierPath(arcCenter:中心,半径:0.01,startAngle:0,endAngle:CGFloat.pi * 2,顺时针:true).cgPath}让shadowAnimation = CABasicAnimation(keyPath:" shadowPath")shadowAnimation.fromValue = self.maskLayer.shadowPathshadowAnimation.toValue = newPathshadowAnimation.duration = totalDurationself.maskLayer.shadowPath = newPathself.maskLayer.add(shadowAnimation,forKey:"shadowAnimation")} 

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

My question requires quite a bit of explanation. If you want to skip to the question, look for the bold "Question:" towards the end.

It's fairly easy to create a "circle wipe" animation in iOS or Mac OS using a Core Animation and masks.

One way is to install a CAShapeLayer as a mask on a view (typically an image view), and installing a circle in the shape layer, and animating the circle from 0 size to large enough to cover the whole image (r = sqrt(height^2 + width^2)

Another way is to use a radial CAGradientLayer as a mask, set up the center of the gradient as an opaque color, and have a sudden transition to clear just beyond the corners of the image. You then animate the "positions" values of the gradient layer to move the clear color in to the center of the image.

Both of these approaches create a sharp-edged "circle wipe" that causes the image to shrink to a point and disappear.

That effect looks like this:

The classic cinematic circle wipe animation has a feathered edge, however, rather than a sharp border. The outer edge of the circle is completely transparent. As you move in towards the center the image becomes more and more solid over a fairly short distance, and then the image is completely opaque from there into the center. As the circle shrinks, the thickness of the transition from transparent to opaque remains constant, and the circle stinks down to a point and disappears. (The mask is a circle is opaque in the middle and has a slightly blurred edge, and the radius of the circle shrinks to 0 while the thickness of the blurred edge remains constant.)

Here is an example of an old school circle wipe. This is technically an "Iris Out" where the transition is to black, but the idea is the same:

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

I have no idea how to achieve a circle wipe with a feathered edge using a shape layer as a mask. Shape layers are always sharp-edged. I could get fairly close by using a shape layer where the circle is stroked with 50% opacity, and the middle of the circle is filled with a color at 100% opacity, but that wouldn't get the smooth transition from transparent to opaque that I'm after.

Another approach I've tried is using a radial gradient with 3 colors in it. I set the inner colors as opaque, and the outer color as clear. I set the location of the outer color as > 1 (say 1.2), the middle location to 1, and the inner location to 0. The locations array contains [1.2, 1.0, 0] That makes the whole part of the mask that covers the image opaque, with a feathered transition to clear that happens past the edges of the image.

I then do an animation of the locations array in 2 steps. In the first step, I animate the locations array to [0, 0, 0.2]. That causes a band of feathering to move in from the corners and stop at 0.2 from the center of the image.

In the 2nd step, I animate the locations array from [0, 0, 0.2] to [0,0,0]. That causes an inner, transparent-to-opaque center to shrink to a point and disappear.

The effect is ok, and if I run it with a narrow blur band and a fast duration, it looks decent, but it's not perfect.

Here is what it looks like with a blur range of 0.2, and a duration of (I think) 0.25 seconds:

If I introduce a pause between the animation steps, however, you can see the imperfections in the effect:

Question: Is there a way to animate a circle with a small blur radius (5 points or so) from the full size of a view down to 0 using Core Animation?

The same question applies to other types of wipe animations as well: Keyhole, star, side wipe, box, and many others. For those, the radial gradient trick I'm using for a feathered circle wipe wouldn't work at all. (You create those specialized wipe animations using a CAShapeLayer that's the shape you want to animate, and then adjust it's size from large to small.

It would be easy to create a static blurred circle (or other shape) by first drawing a circle and then applying a Core Image blur filter to it, but there's no way to animate that in iOS, and the CI blur filter is too slow on iOS even if you could.

Here is a link to the project I used to create the animations in this post using the 3-color radial gradient I describe: https://github.com/DuncanMC/GradientView.git.

Edit:

Based On James' comment, I tried using a shape layer with a shadow on it as a mask. The effect is on the right track, but there is still a stark transition from the opaque part of the image to the mostly transparent part which is masked by the shadow layer. Even with a shadow opacity of 1.0 the shadow is still mostly transparent. Here's what it looks like:

Making the line color of the circle shape partly transparent helps some, and gives a transition between the opaque part of the image and the mostly transparent shadow. Here's the image above but stroking the shape layer at a line width of 2 points and an opacity of 0.6:

Edit #2: SOLVED!

James' idea of using a shadowPath is the solution. You don't want anything in your mask layer other than a shadowPath. If you do that you can use the shadowRadius property to control the amount of blurring, and smoothly animate a shadow path from a circle that fully encloses the view down to a vanishing point all in one step.

I've updated my github project at https://github.com/DuncanMC/GradientView.git to include both the radial gradient animation and the shadowPath animation for comparison.

Here is what the finished effect looks like, first with a 5 pixel blur radius:

And then with a 30 pixel blur radius:

解决方案

You could use the shadowPath on a CAShapeLayer to create a circle with a blurred outline to use as the mask.

Create a shape layer with no fill or stroke (we only want to see the shadow) and add it as the layer mask to your view.

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

And animate it with a 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, startAngle: 0, endAngle: CGFloat.pi * 2, 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")

}

As you can set any path for the shadow it should also work for other shapes (star, box etc.).

这篇关于如何创建羽毛状的“圆形抹布"?iOS中的动画?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆