Ios 反转简单UIView遮罩(切割孔而不是剪裁到圆)

Ios 反转简单UIView遮罩(切割孔而不是剪裁到圆),ios,swift,uiview,mask,Ios,Swift,Uiview,Mask,我试图避免使用CAShapeLayer,因为我需要处理cabasicanitation,而不是UIView.animate。因此,我只是使用UIView的属性来屏蔽视图。这是我目前的代码: class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let imageView = UIImageView(frame:

我试图避免使用
CAShapeLayer
,因为我需要处理
cabasicanitation
,而不是
UIView.animate
。因此,我只是使用UIView的属性来屏蔽视图。这是我目前的代码:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let imageView = UIImageView(frame: CGRect(x: 50, y: 50, width: 200, height: 300))
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        
        let maskView = UIView(frame: CGRect(x: 100, y: 100, width: 80, height: 80))
        maskView.backgroundColor = UIColor.blue /// ensure opaque
        maskView.layer.cornerRadius = 10
        
        imageView.mask = maskView /// set the mask
    }
}
不带
imageView.mask=maskView
使用
imageView.mask=maskView

可以创建图像视图并将其设置为遮罩。请注意,这不适用于动画。如果要将遮罩设置为不同形状的动画,应将遮罩添加到视图的CALayer并使用CALayerAnimation,如您所述。没那么糟

下面我将概述如何生成一个具有透明部分(孔)的图像,您可以将其用作图像视图中的遮罩。但是,如果您的目标是为孔的大小、形状或位置设置动画,那么这将不起作用。您必须为每一帧重新生成遮罩图像,这将非常缓慢

以下是如何使用图像视图作为遮罩来获得静态视图的效果:

使用
UIGraphicsBeginImageContextWithOptions()
UIGraphicsImageRenderer
创建一个对大多数图像都不透明的图像,并且在需要孔的位置有一个透明的“孔”

然后在图像视图中安装该图像,并使该图像视图成为您的掩码

创建带有透明圆形矩形“孔”的不透明图像的代码可能如下所示:

/**
 Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
 for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
    - Parameter size:  The size of the image to create
    - Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
    - Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
 */
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let image = renderer.image { (context) in
        let frame = CGRect(origin: .zero, size: size)
        UIColor.white.setFill()
        context.fill(frame)
        let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
        context.cgContext.setFillColor(UIColor.clear.cgColor)
        context.cgContext.setBlendMode(.clear)

        roundedRect.fill()
    }
    return image
}
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .cyan
        let size = CGSize(width: 200, height: 300)
        let origin = CGPoint(x: 50, y: 50)
        let frame =  CGRect(origin: origin, size: size)
        let imageView = UIImageView(frame: frame)
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        imageView.layer.borderWidth = 2

        //Create a mask image view the same size as the (image) view we will be masking
        let maskView = UIImageView(frame: imageView.bounds)

        //Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
        let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
        let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)

        //Install the image with the "hole" into the mask image view
        maskView.image = maskImage

        //Make the maskView the ImageView's mask
        imageView.mask = maskView /// set the mask
    }
}
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
    let fullRect = UIBezierPath(rect: maskLayer.frame)
    let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
    fullRect.append(roundedRect)
    return fullRect
}
@IBAction func handleAnimateButton(_ sender: Any) {

    //Create a CABasicAnimation that will change the path of our maskLayer
    //Use the keypath "path". That tells the animation object what property we are animating
    let animation = CABasicAnimation(keyPath: "path")

    animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done

    //Use ease-in, ease-out timing, which looks smooth
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

    animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.


    let transparentRect: CGRect

    //Randomly either animate the transparent rect to a different shape or shift it
    if Bool.random() {
        //Make the transparent rect taller and skinnier
        transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
    } else {
        //Shift the transparent rect to by a random amount that still says inside the image view's bounds.
        transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
    }

    let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
    //install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
    animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath

    //add the animation to the maskLayer. Since the animation's `keyPath` is "path",
    //it will animate the layer's "path" property to the "toValue"
    maskLayer.add(animation, forKey: nil)

    //Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}
一个
viewDidLoad
方法安装一个UIImageView,其中包含一个带有孔的掩码图像视图,该方法可能如下所示:

/**
 Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
 for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
    - Parameter size:  The size of the image to create
    - Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
    - Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
 */
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let image = renderer.image { (context) in
        let frame = CGRect(origin: .zero, size: size)
        UIColor.white.setFill()
        context.fill(frame)
        let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
        context.cgContext.setFillColor(UIColor.clear.cgColor)
        context.cgContext.setBlendMode(.clear)

        roundedRect.fill()
    }
    return image
}
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .cyan
        let size = CGSize(width: 200, height: 300)
        let origin = CGPoint(x: 50, y: 50)
        let frame =  CGRect(origin: origin, size: size)
        let imageView = UIImageView(frame: frame)
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        imageView.layer.borderWidth = 2

        //Create a mask image view the same size as the (image) view we will be masking
        let maskView = UIImageView(frame: imageView.bounds)

        //Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
        let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
        let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)

        //Install the image with the "hole" into the mask image view
        maskView.image = maskImage

        //Make the maskView the ImageView's mask
        imageView.mask = maskView /// set the mask
    }
}
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
    let fullRect = UIBezierPath(rect: maskLayer.frame)
    let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
    fullRect.append(roundedRect)
    return fullRect
}
@IBAction func handleAnimateButton(_ sender: Any) {

    //Create a CABasicAnimation that will change the path of our maskLayer
    //Use the keypath "path". That tells the animation object what property we are animating
    let animation = CABasicAnimation(keyPath: "path")

    animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done

    //Use ease-in, ease-out timing, which looks smooth
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

    animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.


    let transparentRect: CGRect

    //Randomly either animate the transparent rect to a different shape or shift it
    if Bool.random() {
        //Make the transparent rect taller and skinnier
        transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
    } else {
        //Shift the transparent rect to by a random amount that still says inside the image view's bounds.
        transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
    }

    let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
    //install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
    animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath

    //add the animation to the maskLayer. Since the animation's `keyPath` is "path",
    //it will animate the layer's "path" property to the "toValue"
    maskLayer.add(animation, forKey: nil)

    //Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}
我使用上面的代码创建了一个示例项目。您可以从Github下载它:

我刚刚更新了这个项目,以展示如何使用CAShapeLayer作为图像视图层上的遮罩来做同样的事情。这样做,就可以对遮罩层路径的更改设置动画

新版本有一个分段控件,允许您选择是使用视图的遮罩属性中的UIImage遮罩图像视图,还是通过用作图像视图层遮罩的CAShapeLayer遮罩图像视图

对于CAShapeLayer版本,遮罩层的路径是一个与整个图像视图大小相同的矩形,其中绘制了第二个较小的圆角矩形。然后,形状层上的缠绕规则设置为“偶数/奇数”规则,这意味着如果必须穿过偶数个形状边界才能到达一个点,则认为它位于形状之外。这使您能够创建我们需要的空心形状

选择“层遮罩”选项时,将启用一个动画按钮,用于为遮罩中“剪切”透明矩形的随机更改设置动画

创建遮罩路径的函数如下所示:

/**
 Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
 for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
    - Parameter size:  The size of the image to create
    - Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
    - Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
 */
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let image = renderer.image { (context) in
        let frame = CGRect(origin: .zero, size: size)
        UIColor.white.setFill()
        context.fill(frame)
        let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
        context.cgContext.setFillColor(UIColor.clear.cgColor)
        context.cgContext.setBlendMode(.clear)

        roundedRect.fill()
    }
    return image
}
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .cyan
        let size = CGSize(width: 200, height: 300)
        let origin = CGPoint(x: 50, y: 50)
        let frame =  CGRect(origin: origin, size: size)
        let imageView = UIImageView(frame: frame)
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        imageView.layer.borderWidth = 2

        //Create a mask image view the same size as the (image) view we will be masking
        let maskView = UIImageView(frame: imageView.bounds)

        //Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
        let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
        let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)

        //Install the image with the "hole" into the mask image view
        maskView.image = maskImage

        //Make the maskView the ImageView's mask
        imageView.mask = maskView /// set the mask
    }
}
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
    let fullRect = UIBezierPath(rect: maskLayer.frame)
    let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
    fullRect.append(roundedRect)
    return fullRect
}
@IBAction func handleAnimateButton(_ sender: Any) {

    //Create a CABasicAnimation that will change the path of our maskLayer
    //Use the keypath "path". That tells the animation object what property we are animating
    let animation = CABasicAnimation(keyPath: "path")

    animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done

    //Use ease-in, ease-out timing, which looks smooth
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

    animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.


    let transparentRect: CGRect

    //Randomly either animate the transparent rect to a different shape or shift it
    if Bool.random() {
        //Make the transparent rect taller and skinnier
        transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
    } else {
        //Shift the transparent rect to by a random amount that still says inside the image view's bounds.
        transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
    }

    let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
    //install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
    animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath

    //add the animation to the maskLayer. Since the animation's `keyPath` is "path",
    //it will animate the layer's "path" property to the "toValue"
    maskLayer.add(animation, forKey: nil)

    //Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}
制作动画的函数如下所示:

/**
 Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
 for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
    - Parameter size:  The size of the image to create
    - Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
    - Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
 */
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let image = renderer.image { (context) in
        let frame = CGRect(origin: .zero, size: size)
        UIColor.white.setFill()
        context.fill(frame)
        let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
        context.cgContext.setFillColor(UIColor.clear.cgColor)
        context.cgContext.setBlendMode(.clear)

        roundedRect.fill()
    }
    return image
}
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .cyan
        let size = CGSize(width: 200, height: 300)
        let origin = CGPoint(x: 50, y: 50)
        let frame =  CGRect(origin: origin, size: size)
        let imageView = UIImageView(frame: frame)
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        imageView.layer.borderWidth = 2

        //Create a mask image view the same size as the (image) view we will be masking
        let maskView = UIImageView(frame: imageView.bounds)

        //Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
        let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
        let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)

        //Install the image with the "hole" into the mask image view
        maskView.image = maskImage

        //Make the maskView the ImageView's mask
        imageView.mask = maskView /// set the mask
    }
}
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
    let fullRect = UIBezierPath(rect: maskLayer.frame)
    let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
    fullRect.append(roundedRect)
    return fullRect
}
@IBAction func handleAnimateButton(_ sender: Any) {

    //Create a CABasicAnimation that will change the path of our maskLayer
    //Use the keypath "path". That tells the animation object what property we are animating
    let animation = CABasicAnimation(keyPath: "path")

    animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done

    //Use ease-in, ease-out timing, which looks smooth
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

    animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.


    let transparentRect: CGRect

    //Randomly either animate the transparent rect to a different shape or shift it
    if Bool.random() {
        //Make the transparent rect taller and skinnier
        transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
    } else {
        //Shift the transparent rect to by a random amount that still says inside the image view's bounds.
        transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
    }

    let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
    //install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
    animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath

    //add the animation to the maskLayer. Since the animation's `keyPath` is "path",
    //it will animate the layer's "path" property to the "toValue"
    maskLayer.add(animation, forKey: nil)

    //Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}
结果(使用我自己的示例图像)如下所示:

基于CALayer的遮罩动画示例如下所示:

/**
 Function to create a UIImage that is mostly opaque, with a transparent rounded rect "knockout" in it. Such an image might be used ask a mask
 for another view, where the transparent "knockout" appears as a hole in the view that is being masked.
    - Parameter size:  The size of the image to create
    - Parameter transparentRect: The (rounded )rectangle to make transparent in the middle of the image.
    - Parameter cornerRadius: The corner radius ot use in the transparent rectangle. Pass 0 to make the rectangle square-cornered.
 */
func imageWithTransparentRoundedRect(size: CGSize, transparentRect: CGRect, cornerRadius: CGFloat) -> UIImage? {
    let renderer = UIGraphicsImageRenderer(size: size)
    let image = renderer.image { (context) in
        let frame = CGRect(origin: .zero, size: size)
        UIColor.white.setFill()
        context.fill(frame)
        let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
        context.cgContext.setFillColor(UIColor.clear.cgColor)
        context.cgContext.setBlendMode(.clear)

        roundedRect.fill()
    }
    return image
}
override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .cyan
        let size = CGSize(width: 200, height: 300)
        let origin = CGPoint(x: 50, y: 50)
        let frame =  CGRect(origin: origin, size: size)
        let imageView = UIImageView(frame: frame)
        imageView.image = UIImage(named: "TestImage")
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        view.addSubview(imageView)
        imageView.layer.borderWidth = 2

        //Create a mask image view the same size as the (image) view we will be masking
        let maskView = UIImageView(frame: imageView.bounds)

        //Build an opaque UIImage with a transparent "knockout" rounded rect inside it.
        let transparentRect = CGRect(x: 100, y: 100, width: 80, height: 80)
        let maskImage = imageWithTransparentRoundedRect(size: size, transparentRect: transparentRect, cornerRadius: 20)

        //Install the image with the "hole" into the mask image view
        maskView.image = maskImage

        //Make the maskView the ImageView's mask
        imageView.mask = maskView /// set the mask
    }
}
func maskPath(transparentRect: CGRect, cornerRadius: CGFloat) -> UIBezierPath {
    let fullRect = UIBezierPath(rect: maskLayer.frame)
    let roundedRect = UIBezierPath(roundedRect: transparentRect, cornerRadius: cornerRadius)
    fullRect.append(roundedRect)
    return fullRect
}
@IBAction func handleAnimateButton(_ sender: Any) {

    //Create a CABasicAnimation that will change the path of our maskLayer
    //Use the keypath "path". That tells the animation object what property we are animating
    let animation = CABasicAnimation(keyPath: "path")

    animation.autoreverses = true //Make the animation reverse back to the oringinal position once it's done

    //Use ease-in, ease-out timing, which looks smooth
    animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)

    animation.duration = 0.3 //Make each step in the animation last 0.3 seconds.


    let transparentRect: CGRect

    //Randomly either animate the transparent rect to a different shape or shift it
    if Bool.random() {
        //Make the transparent rect taller and skinnier
        transparentRect = self.transparentRect.inset(by: UIEdgeInsets(top: -20, left: 20, bottom: -20, right: 20))
    } else {
        //Shift the transparent rect to by a random amount that still says inside the image view's bounds.
        transparentRect = self.transparentRect.offsetBy(dx: CGFloat.random(in: -100...20), dy: CGFloat.random(in: -100...100))
    }

    let cornerRadius: CGFloat = CGFloat.random(in: 0...30)
    //install the new path as the animation's `toValue`. If we dont specify a `fromValue` the animation will start from the current path.
    animation.toValue = maskPath(transparentRect: transparentRect, cornerRadius: cornerRadius).cgPath

    //add the animation to the maskLayer. Since the animation's `keyPath` is "path",
    //it will animate the layer's "path" property to the "toValue"
    maskLayer.add(animation, forKey: nil)

    //Since we don't actually change the path on the mask layer, the mask will revert to it's original path once the animation completes.
}

你想做什么样的动画?将面具设置为不同的形状/大小?我以前告诉过你什么?遮罩中的所有内容都取决于其像素的透明度。你的是向后的。如果你想要一个洞,圆形的矩形需要是透明的,其余的则需要是不透明的。此外,层可以设置动画-事实上,所有动画最终都是层动画-因此你不使用层遮罩的理由是没有意义的。但是,这与问题无关。:)啊,对。因此,子视图技术在这里不太容易工作。但是你看,你改变了问题的条件,不是吗?尽管如此,我所说的面具是如何工作的仍然是一样的;只需搜索:任何人都不应该再使用带有选项的UIGraphicsBeginImageContextWithOptions了。这一点很好。我又回到了过去。去编辑我的答案…哇!哟,这太疯狂了!然而,这一努力。。非常感谢!我在Github项目中放了一个复活节彩蛋,上面的GIF中没有。(请尝试在项目中使用层遮罩动画。)