如何通过动画围绕某个点旋转 CAShapeLayer?

How to rotate a CAShapeLayer around some point with animation?

提问人:cool8jay 提问时间:5/9/2023 最后编辑:cool8jay 更新时间:5/9/2023 访问量:89

问:

自定义视图包含一些 CALayer 作为其内容。

picture

我想为红线添加一致的旋转动画。(把它想象成一个模拟时钟的指针)

预期结果:红线在蓝色方块的中心旋转。

当前结果:红线在蓝色方块的左下角旋转。请参阅下面的 gif。

gif


红线的动画代码为:

let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
    
// Set animation properties
rotationAnimation.fromValue = CGFloat.pi * 2.0
rotationAnimation.toValue = 0
rotationAnimation.duration = 2.0
rotationAnimation.isCumulative = true
rotationAnimation.repeatCount = Float.greatestFiniteMagnitude
    
// Add rotation animation to pointer layer
pointerLayer.add(rotationAnimation, forKey: "rotationAnimation")

完整的项目在这里:

https://github.com/cool8jay/public/files/11426695/testDrawPointer.zip


以下是我在谷歌或stackoverflow在线后尝试过的内容:

  1. 更改 anchorPoint
  2. 使用 CATransform3D
  3. 使用 CGAffineTransform
  4. 以上组合

没有一个对我有用。

Swift macOS 动画 Calayer

评论

0赞 Sweeper 5/9/2023
您应该发布代码的最小可重现示例,而不是指向 GitHub 的链接。您还应该展示您如何尝试找到的解决方案,以及它们如何不起作用。

答:

1赞 ix4n33 5/9/2023 #1

您忘记设置图层的框架,将这些行添加到您的代码中,它将起作用:

borderLayer.frame = bounds
pointerLayer.frame = borderLayer.bounds

Leo的回答也有效。您看到红色指针拉长蓝色边框的原因是,您使用的颜色和形状,结合描边线的平行绘制和边界剪裁,很容易产生视错觉。所以它看起来没有围绕中心旋转,但实际上它确实如此。

以下是完整代码:

class ClockView: NSView {
    
    let pointerLayer = CAShapeLayer()
    let borderLayer = CAShapeLayer()
    
    override public init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        setup()
    }
    
    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setup()
    }
    
    private func setup() {
        self.wantsLayer = true

        borderLayer.frame = bounds
        borderLayer.fillColor = NSColor.gray.cgColor
        borderLayer.path = CGPath(ellipseIn: borderLayer.bounds, transform: nil)
        layer?.addSublayer(borderLayer)
        
        let path = CGMutablePath()
        path.move(to: CGPointMake(bounds.midX, bounds.midY))
        path.addLine(to: CGPointMake(bounds.midX + 50, bounds.midY))
        pointerLayer.path = path
        pointerLayer.frame = bounds
        pointerLayer.lineWidth = 8
        pointerLayer.strokeColor = NSColor.red.cgColor
        layer?.addSublayer(pointerLayer)

        let animation = CABasicAnimation(keyPath: "transform.rotation.z")
        animation.fromValue = CGFloat.pi * 2
        animation.toValue = 0
        animation.duration = 8
        animation.isCumulative = true
        animation.repeatCount = Float.greatestFiniteMagnitude
        pointerLayer.add(animation, forKey: "rotationAnimation")
    }
}

我将 borderLayer 的形状更改为圆形,去掉线条描边,并将颜色更改为最不刺眼的颜色。

我还重新排列了代码,使其更易于阅读。