使用 CABasicAnimation 缩放 CAShapeLayer 会导致它平移
Scaling a CAShapeLayer with CABasicAnimation causes it to translate
我有一个 CAShapeLayer,它被绘制为给定的圆 point
:
let circlePath = UIBezierPath(arcCenter: point, radius: 20.0, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
我希望能够使用 CABasicAnimation 缩放该图层。我尝试了几种方法,但大多数都给我同样的问题:圆圈适当缩放,但似乎同时 translate/move。圆圈缩放和平移的速率似乎会影响 point
的值:point
离原点越近,圆圈的动画效果就越慢。我显然在这里遗漏了一些基本的东西,但我一直找不到。这是我迄今为止尝试过的最简单的动画制作尝试:
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0
shapeLayer.add(growthAnimation, forKey: nil)
self.view.layer.addSublayer(shapeLayer)
我尝试设置“转换”(而不是“transform.scale”),我尝试使用 NSValue 作为动画的 toValue,我尝试将转换添加到转换为尝试将其保持在当前位置,但运气不好。
我目前的理论是,图层实际上比圆圈大,因此当整个图层缩放时,圆圈看起来只是在平移。但是我无法为图层的边框或背景颜色着色,我不确定如何证明这一点。
“我目前的理论是该层实际上比圆更大,因此随着整个层的缩放...”
您目前的理论是正确的。
这样试试:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let radius: CGFloat = 20.0
let point = CGPoint(x: radius, y: radius)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0
shapeLayer.add(growthAnimation, forKey: nil)
// set the layer frame
shapeLayer.frame = CGRect(x: view.frame.midX - radius, y: view.frame.midY - radius, width: radius * 2.0, height: radius * 2.0)
shapeLayer.backgroundColor = UIColor.green.cgColor
self.view.layer.addSublayer(shapeLayer)
}
编辑 - 这可能会使事情更清楚...
如果您 运行 这个例子,它会创建 4 个带圆圈的形状层:
- 圆心位于
0,0
,没有给出图层框架
- 圆心位于
100,100
,没有给出图层框架
- 圆心位于
0,0
层框架 x: 100, y: 240, w: 40, h: 40
(40 是半径 * 2)...圆心是框架的左上角
- 圆心位于
radius,radius
(所以,20,20),图层框架 x: 100, y: 380, w: 40, h: 40
(40 是半径 * 2)...圆心现在位于图层框架的中心
当您点击视图上的任意位置时,所有 4 个层将执行相同的缩放动画(减慢到 3 秒以便于观看)。
应该清楚路径和图层框架如何影响变换。
class ViewController: UIViewController {
func test1() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// no frame set for the shape layer
// point is at top-left corner of shape layer frame
// since we haven't set a frame for the layer, it's top-left corner of the view
let point = CGPoint(x: 0, y: 0)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test2() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// no frame set for the shape layer
// set point to 100,100
let point = CGPoint(x: 100, y: 100)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test3() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,240
shapeLayer.frame = CGRect(x: 100.0, y: 240.0, width: radius * 2.0, height: radius * 2.0)
// point is at top-left corner of shape layer frame
let point = CGPoint(x: 0, y: 0)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.orange.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test4() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,380
shapeLayer.frame = CGRect(x: 100.0, y: 380.0, width: radius * 2.0, height: radius * 2.0)
// set point to center of layer frame
let point = CGPoint(x: radius, y: radius)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap))
view.addGestureRecognizer(t)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
test1()
test2()
test3()
test4()
}
@objc func didTap() -> Void {
scaleLayers()
}
func scaleLayers() -> Void {
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 3.0
guard let layers = view.layer.sublayers else {
return
}
layers.forEach { layer in
if let shapeLayer = layer as? CAShapeLayer {
shapeLayer.add(growthAnimation, forKey: nil)
}
}
}
}
我有一个 CAShapeLayer,它被绘制为给定的圆 point
:
let circlePath = UIBezierPath(arcCenter: point, radius: 20.0, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
我希望能够使用 CABasicAnimation 缩放该图层。我尝试了几种方法,但大多数都给我同样的问题:圆圈适当缩放,但似乎同时 translate/move。圆圈缩放和平移的速率似乎会影响 point
的值:point
离原点越近,圆圈的动画效果就越慢。我显然在这里遗漏了一些基本的东西,但我一直找不到。这是我迄今为止尝试过的最简单的动画制作尝试:
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0
shapeLayer.add(growthAnimation, forKey: nil)
self.view.layer.addSublayer(shapeLayer)
我尝试设置“转换”(而不是“transform.scale”),我尝试使用 NSValue 作为动画的 toValue,我尝试将转换添加到转换为尝试将其保持在当前位置,但运气不好。
我目前的理论是,图层实际上比圆圈大,因此当整个图层缩放时,圆圈看起来只是在平移。但是我无法为图层的边框或背景颜色着色,我不确定如何证明这一点。
“我目前的理论是该层实际上比圆更大,因此随着整个层的缩放...”
您目前的理论是正确的。
这样试试:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let radius: CGFloat = 20.0
let point = CGPoint(x: radius, y: radius)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 1.0
shapeLayer.add(growthAnimation, forKey: nil)
// set the layer frame
shapeLayer.frame = CGRect(x: view.frame.midX - radius, y: view.frame.midY - radius, width: radius * 2.0, height: radius * 2.0)
shapeLayer.backgroundColor = UIColor.green.cgColor
self.view.layer.addSublayer(shapeLayer)
}
编辑 - 这可能会使事情更清楚...
如果您 运行 这个例子,它会创建 4 个带圆圈的形状层:
- 圆心位于
0,0
,没有给出图层框架 - 圆心位于
100,100
,没有给出图层框架 - 圆心位于
0,0
层框架x: 100, y: 240, w: 40, h: 40
(40 是半径 * 2)...圆心是框架的左上角 - 圆心位于
radius,radius
(所以,20,20),图层框架x: 100, y: 380, w: 40, h: 40
(40 是半径 * 2)...圆心现在位于图层框架的中心
当您点击视图上的任意位置时,所有 4 个层将执行相同的缩放动画(减慢到 3 秒以便于观看)。
应该清楚路径和图层框架如何影响变换。
class ViewController: UIViewController {
func test1() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// no frame set for the shape layer
// point is at top-left corner of shape layer frame
// since we haven't set a frame for the layer, it's top-left corner of the view
let point = CGPoint(x: 0, y: 0)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test2() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// no frame set for the shape layer
// set point to 100,100
let point = CGPoint(x: 100, y: 100)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test3() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,240
shapeLayer.frame = CGRect(x: 100.0, y: 240.0, width: radius * 2.0, height: radius * 2.0)
// point is at top-left corner of shape layer frame
let point = CGPoint(x: 0, y: 0)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.orange.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
func test4() -> Void {
let radius: CGFloat = 20.0
let shapeLayer = CAShapeLayer()
// set shape layer frame to 40x40 at 100,380
shapeLayer.frame = CGRect(x: 100.0, y: 380.0, width: radius * 2.0, height: radius * 2.0)
// set point to center of layer frame
let point = CGPoint(x: radius, y: radius)
let circlePath = UIBezierPath(arcCenter: point, radius: radius, startAngle: 0, endAngle: CGFloat.pi * 2.0, clockwise: true)
shapeLayer.path = circlePath.cgPath
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.fillColor = nil
shapeLayer.lineWidth = 2.0
self.view.layer.addSublayer(shapeLayer)
shapeLayer.backgroundColor = UIColor.green.cgColor
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap))
view.addGestureRecognizer(t)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
test1()
test2()
test3()
test4()
}
@objc func didTap() -> Void {
scaleLayers()
}
func scaleLayers() -> Void {
let growthAnimation = CABasicAnimation(keyPath: "transform.scale")
growthAnimation.toValue = 3
growthAnimation.fillMode = .forwards
growthAnimation.isRemovedOnCompletion = false
growthAnimation.duration = 3.0
guard let layers = view.layer.sublayers else {
return
}
layers.forEach { layer in
if let shapeLayer = layer as? CAShapeLayer {
shapeLayer.add(growthAnimation, forKey: nil)
}
}
}
}