CAShapeLayer 路径动画在完成动画之前收缩

CAShapeLayer path animation shrinking before completing animation

我正在尝试为图层的 CGpath 的旋转设置动画。

旋转效果很好,但由于某种原因,路径似乎缩小了,然后又变大了。我只是想让它显示一个旋转动画。我不想让它缩小

这是一个视频。

这是代码

class ViewController: UIViewController {

@IBOutlet weak var overlayView: UIView!


override func viewDidLoad() {
    super.viewDidLoad()
    
    
    let maskLayer = CAShapeLayer()
    let path = UIBezierPath(rect: view.bounds)
    let mask = UIBezierPath.drawSquare(width: 100, center: view.center)
    
    // Makes it so we can see through the center of the view
    maskLayer.fillRule = CAShapeLayerFillRule.evenOdd
    path.append(mask)
    maskLayer.path = path.cgPath
    overlayView.layer.mask = maskLayer
}

@IBAction func rotateView(_ sender: Any) {
    
    let maskPath = UIBezierPath.drawSquare(width: 100, center: overlayView.center)
    let path = UIBezierPath(rect: overlayView.frame)
    
    let bounds: CGRect = maskPath.cgPath.boundingBox
    let center = CGPoint(x: bounds.midX, y: bounds.midY)
    
    let radians = 90 / 180.0 * .pi
    var transform: CGAffineTransform = .identity
    transform = transform.translatedBy(x: center.x, y: center.y)
    transform = transform.rotated(by: radians)
    transform = transform.translatedBy(x: -center.x, y: -center.y)
    maskPath.apply(transform)

    path.append(maskPath)
    
    // create new animation
    let anim = CABasicAnimation(keyPath: "path")
    anim.toValue = path.cgPath
    
    (overlayView.layer.mask as? CAShapeLayer)?.add(anim, forKey: "path")

}

}

extension UIBezierPath{

static func drawSquare(width: CGFloat, center: CGPoint) -> UIBezierPath {
    let rect = CGRect(x: center.x - width/2, y: center.y - width/2, width: width, height: width)

    let path = UIBezierPath()
    path.move(to: rect.origin)
    
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY))
    path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
    path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY))
    path.close()
    return path
}
}

您的“剪切框”正在缩小和扩大,因为您正在使用 路径 动画。

当您为从一组点到另一组点的路径设置动画时,每个点将从其起点到终点采用一条直线。

几种可视化方法...

首先,我们将向镂空遮罩框添加一个“轮廓”框:

如您所见,每个角都直接通往下一个位置。

如果我们突出一侧,它可能更明显一点:

而且,为了更清楚,我们可以对角进行编号:

所以,如果我们想在不改变大小的情况下旋转正方形,我们可以使用关键帧动画和运行沿圆的角:

或者,更简单的方法是旋转遮罩 而不是其路径:

let radians = 90 / 180.0 * .pi
let anim = CABasicAnimation(keyPath: "transform.rotation.z")
anim.toValue = radians
anim.duration = 1.0
(overlayView.layer.mask as? CAShapeLayer)?.add(anim, forKey: "layerRotate")

毫无疑问,我们注意到遮罩层框架没有完全覆盖 overlayView 框架。

所以,我们可以通过增大遮罩层框架来解决这个问题。

我们可以准确地计算它需要多大,但是因为形状图层非常有效,我们只将它设为正方形,比长轴大 1.5 倍:

所以当它旋转时,它会覆盖视图:

我们就此结束: