【问题标题】:Animating line shaped CAShapeLayer in circular path在圆形路径中动画线形 CAShapeLayer
【发布时间】:2019-07-23 06:01:47
【问题描述】:

我正在尝试沿圆形路径为我的线形 CAShapeLayer 设置动画。我创建了线层的初始路径和最终路径,并将它们添加到 CABasicAnimation 的 from 和 to 值中。

然而动画并不是我所期待的。过去两天我都卡在这里了。

func getAllLayers() -> [CALayer] 

   {

    var layers = []

    let pointerEndPoint = CGPoint(x: xPointOfPointer , y: yPointOfPointer)  // Final end point of pointer
    let radius = 5

    let pointerFinalPath = UIBezierPath()
    pointerFinalPath.addArc(withCenter: circleCenter, radius: radius, startAngle: 0, endAngle:2 * CGFloat.pi, clockwise: false)
    pointerFinalPath.move(to: circleCenter)
    pointerFinalPath.addLine(to: endPoint)
    pointerFinalPath.close()

    let pointerPathLayer = CAShapeLayer()
    pointerPathLayer.path = pointerFinalPath.cgPath
    pointerPathLayer.lineWidth = 2
    pointerPathLayer.fillColor = UIColor.black.cgColor
    pointerPathLayer.strokeColor = UIColor.black.cgColor
    layers.append(pointerPathLayer)

    let pointerInitialBezierPath = UIBezierPath()
    pointerInitialBezierPath.addArc(withCenter: circleCenter, radius: radius,startAngle: 0 , endAngle: 2 * CGFloat.pi , clockwise: false)
    pointerInitialBezierPath.move(to: circleCenter)
    pointerInitialBezierPath.addLine(to: CGPoint(x: circleCenter.x - 50 , y: centerY))
    pointerInitialBezierPath.close()


    let pointerInitialCGPath = pointerInitialBezierPath.cgPath
    let pointerFinalCGPath = pointerFinalPath.cgPath


                    // Pointer Animation
    let pointerAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.path))
     pointerAnimation.fromValue = pointerInitialCGPath
     pointerAnimation.toValue = pointerFinalCGPath
      pointerAnimation.duration = 0.5
      pointerAnimation.isCumulative = true
      pointerPathLayer.add(pointerAnimation, forKey: "Animation")

return layers
}

在堆栈溢出中搜索解决方案,但无法找到我的错误。我希望我的动画像时钟的指针一样移动。

请注意,针的长度会随着动画的进行而变化。我希望它保持恒定的长度,只希望我的针角改变。我将不胜感激。

编辑 1:如果我尝试 transform.rotation 动画,我会得到针旋转,但它发生在错误的中心点周围。

      // Pointer Animation

            let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
            rotateAnimation.fromValue = 0.0
            rotateAnimation.toValue = angleOfActualValueinRad
            rotateAnimation.duration = 2.0
            pointerPathLayer.add(rotateAnimation, forKey: "Animation")

编辑 2:使用此链接查找我的 github 项目。 Animation Issue

【问题讨论】:

  • 如果您更改anchorPointpointerPathLayer 并添加旋转动画,该怎么办?
  • 我用轮换代码发布答案。
  • @girish_pro 问题已更新
  • 您需要设置锚点。检查我的答案。添加以下行。 pointerPathLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)
  • 人们感谢您的帮助和时间。这里的问题是我没有为 CAShapeLayer 设置框架。这种做法是由于对 UIViews 使用约束而产生的。因此,正如@girish_pro 建议的那样,我通过正确地为每个 CAShapelayer 计算框架并在我的 CABasicAnimation 中应用角度值和角度值来修复它。

标签: ios swift cashapelayer cabasicanimation


【解决方案1】:

您无法使用 UIBezierPath 实现速度计动画。 您需要将 Line 转换为您想要的任何角度。 请参考以下我为解决您的问题而制作的旋转代码。

let circleCenter = CGPoint(x: 150.0, y: 150.0)

let startPoint = CGPoint(x: 100, y: 150.0)
    // Start Path
    let pointerStartPath = UIBezierPath()
    pointerStartPath.move(to: circleCenter)
    pointerStartPath.addLine(to: startPoint)
    pointerStartPath.close()

    let pointerPathLayer = CAShapeLayer()
    pointerPathLayer.frame = CGRect(x: 0, y:0, width: 300, height: 300)
    pointerPathLayer.path = pointerStartPath.cgPath
    pointerPathLayer.lineWidth = 2
    pointerPathLayer.fillColor = UIColor.green.cgColor
    pointerPathLayer.strokeColor = UIColor.black.cgColor
    self.view.layer.addSublayer(pointerPathLayer)

// New : Set Anchor point
pointerPathLayer.anchorPoint = CGPoint(x: 0.5, y: 0.5)

    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnimation.fromValue = 0.0
    rotateAnimation.toValue = CGFloat.pi * 2.0
    rotateAnimation.duration = 2.0
    pointerPathLayer.add(rotateAnimation, forKey: "Animation")

使用这段代码,可以实现360度旋转。

【讨论】:

  • 还是同样的问题?
  • 是的,兄弟,请检查我附加的 git 项目,它显示了问题。
【解决方案2】:

你使用了错误的变换旋转,尝试这样做

let rAnimation = CABasicAnimation(keyPath:"transform.rotation.z")
    rAnimation.duration = 1
    rAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    rAnimation.fromValue = degree2radian(90)
    rAnimation.repeatCount = Float.infinity
    rAnimation.toValue = degree2radian(180)
    progressLineLayer.add(rAnimation, forKey:"rotate")

func degree2radian(_ a: CGFloat) -> CGFloat {
    return CGFloat.pi * a / 180
}

查看此链接http://sketchytech.blogspot.com/2014/11/swift-how-to-draw-clock-face-using_12.html,可能会有所帮助

更新:

我写了一些代码来展示它是如何工作的

private func animateProgress() {

    // black circle at center

    let blackCirclePath = UIBezierPath()
    blackCirclePath.addArc(withCenter: view.center, radius: 5, startAngle: 0, endAngle: CGFloat.pi * 2, clockwise: true)
    blackCirclePath.fill()
    UIColor.black.setFill()
    blackCirclePath.close()

    let circleLayer = CAShapeLayer()
    circleLayer.path = blackCirclePath.cgPath
    view.layer.addSublayer(circleLayer)

    // all scale gray background layer

    let grayHalfCirclePath = UIBezierPath()
    grayHalfCirclePath.addArc(withCenter: view.center, radius: 50, startAngle: 0, endAngle: CGFloat.pi, clockwise: false)

    let grayHalfCircleLayer = CAShapeLayer()
    grayHalfCircleLayer.frame = view.bounds
    grayHalfCircleLayer.path = grayHalfCirclePath.cgPath
    grayHalfCircleLayer.strokeColor = UIColor.lightGray.cgColor
    grayHalfCircleLayer.fillColor = nil
    grayHalfCircleLayer.lineWidth = 10
    view.layer.addSublayer(grayHalfCircleLayer)

    // animated progress scale

    let progressHalfCirclePath = UIBezierPath()
    progressHalfCirclePath.addArc(withCenter: view.center, radius: 50, startAngle: CGFloat.pi, endAngle: -CGFloat.pi / 2, clockwise: true)

    let progressLayer = CAShapeLayer()
    progressLayer.frame = view.bounds
    progressLayer.path = progressHalfCirclePath.cgPath
    progressLayer.strokeColor = UIColor.orange.cgColor
    progressLayer.fillColor = nil
    progressLayer.lineWidth = 10
    view.layer.addSublayer(progressLayer)

    // animation

    let pAnimation = CABasicAnimation(keyPath: "strokeEnd")
    pAnimation.duration = 1.0
    pAnimation.fromValue = NSNumber(floatLiteral: 0)
    pAnimation.toValue = NSNumber(floatLiteral: 1)
    progressLayer.add(pAnimation, forKey: "strokeEnd")

    // black arrow

    let progressLineLayer = CAShapeLayer()
    progressLineLayer.frame = view.bounds

    let progressLinePath = CGMutablePath()
    progressLinePath.move(to: CGPoint(x: view.center.x, y: view.center.y))
    progressLinePath.addLine(to: CGPoint(x: view.center.x - 30, y: view.center.y - 30))
    progressLineLayer.path = progressLinePath
    progressLineLayer.strokeColor = UIColor.green.cgColor
    progressLineLayer.lineWidth = 1
    progressLineLayer.strokeColor = UIColor.black.cgColor
    view.layer.addSublayer(progressLineLayer)

    let rAnimation = CABasicAnimation(keyPath:"transform.rotation.z")
    rAnimation.duration = 1
    rAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
    rAnimation.fromValue = degree2radian(-45)
    rAnimation.repeatCount = 1
    rAnimation.toValue = degree2radian(45)
    progressLineLayer.add(rAnimation, forKey:"rotate")
}

func degree2radian(_ a: CGFloat) -> CGFloat {
    return CGFloat.pi * a / 180
}

【讨论】:

  • 感谢您的帮助。非常感谢您抽出时间来帮助我。
【解决方案3】:

试试这个库 ABGuageViewKit

重量轻,可自定义动画。

希望这对编码有所帮助。

【讨论】:

  • 感谢您的宝贵时间。关于我的代码为什么不起作用的任何想法。
猜你喜欢
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 2016-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多