圆形路径中的动画线形 CAShapeLayer
Animating line shaped CAShapeLayer in circular path
我正在尝试沿着圆形路径为我的线形 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
}
在 Stack over flow 中搜索了解决方案,但找不到我的错误。我希望我的动画像时钟的指针一样移动。
请注意针的长度随着动画的进行而变化。我希望它保持恒定长度,只希望改变针的角度。如果有任何帮助,我将不胜感激。
编辑 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:使用此 link 查找我的 github 项目。 Animation Issue
试试这个库
ABGuageViewKit
重量轻,可自定义动画。
希望这对您有所帮助,祝您编码愉快。
您无法使用 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度旋转。
你使用了错误的变换旋转,尝试这样做
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
}
通读一下link 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
}
我正在尝试沿着圆形路径为我的线形 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
}
在 Stack over flow 中搜索了解决方案,但找不到我的错误。我希望我的动画像时钟的指针一样移动。
请注意针的长度随着动画的进行而变化。我希望它保持恒定长度,只希望改变针的角度。如果有任何帮助,我将不胜感激。
编辑 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:使用此 link 查找我的 github 项目。 Animation Issue
试试这个库 ABGuageViewKit
重量轻,可自定义动画。
希望这对您有所帮助,祝您编码愉快。
您无法使用 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度旋转。
你使用了错误的变换旋转,尝试这样做
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
}
通读一下link 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
}