如何避免旋转动画在 2π 边界跳跃?
How to avoid rotation animation jumping at 2π boundary?
我正在使用 CAKeyframeAnimation 沿 CGPath 为箭头制作动画。箭头遵循路径,但 atan2
'flips' 从 0 到 2π 的输出除外,反之亦然。在这种情况下,箭头会在继续之前进行完整的 360° 旋转。
有没有什么方法可以计算我的 CAKeyframeAnimation 的值,以便箭头始终旋转最小量以到达它要去的地方?
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
var values = [CGFloat]()
for i in 0..<points.count - 1 {
let point1 = points[i]
let point2 = points[i + 1]
let delta = point2 - point1
let value = atan2(delta.y, delta.x)
values.append(value)
}
turnAnimation.values = values
turnAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.repeatCount = Float.greatestFiniteMagnitude
arrowPointLayer.transform = CATransform3DMakeRotation(values.first!, 0, 0, 1)
arrowPointLayer.add(turnAnimation, forKey: "rotateArrow")
编辑:根据答案进行了更新,但仍然不太有效:
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
var rawValues = [CGFloat]()
var values = [CGFloat]()
for i in 0..<points.count - 1 {
let point1 = points[i]
let point2 = points[i + 1]
let delta = point2 - point1
let rawValue = atan2(delta.y, delta.x)
var value: CGFloat
if i == 0 {
value = 0 // we will apply this value from rawValues
} else {
let lastValue = rawValues.last!
value = rawValue - lastValue
}
if value < 0 {
value = .pi * 2 + value
}
rawValues.append(rawValue)
values.append(value)
}
for (i, p) in values.enumerated() {
print("\(rawValues[i]): \(p)")
}
turnAnimation.values = values
turnAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.repeatCount = Float.greatestFiniteMagnitude
turnAnimation.isAdditive = true
arrowPointLayer.transform = CATransform3DMakeRotation(rawValues.first!, 0, 0, 1)
arrowPointLayer.add(turnAnimation, forKey: "rotateArrow")
不要将 values
设为绝对旋转值的列表,而是将其设为旋转 差异 的列表,并将动画的 isAdditive
设置为 true
.这样,你 就可以告诉箭头向哪个方向转,转多少。
我这样测试(在我的测试中,v
只是我界面中的一些视图):
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
let values : [CGFloat] = [0,2,-1,2,-1,3,-3]
turnAnimation.values = values
turnAnimation.timingFunction =
CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.isAdditive = true
self.v.layer.add(turnAnimation, forKey: "rotateArrow")
视图的行为完全符合我的预期。
我正在使用 CAKeyframeAnimation 沿 CGPath 为箭头制作动画。箭头遵循路径,但 atan2
'flips' 从 0 到 2π 的输出除外,反之亦然。在这种情况下,箭头会在继续之前进行完整的 360° 旋转。
有没有什么方法可以计算我的 CAKeyframeAnimation 的值,以便箭头始终旋转最小量以到达它要去的地方?
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
var values = [CGFloat]()
for i in 0..<points.count - 1 {
let point1 = points[i]
let point2 = points[i + 1]
let delta = point2 - point1
let value = atan2(delta.y, delta.x)
values.append(value)
}
turnAnimation.values = values
turnAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.repeatCount = Float.greatestFiniteMagnitude
arrowPointLayer.transform = CATransform3DMakeRotation(values.first!, 0, 0, 1)
arrowPointLayer.add(turnAnimation, forKey: "rotateArrow")
编辑:根据答案进行了更新,但仍然不太有效:
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
var rawValues = [CGFloat]()
var values = [CGFloat]()
for i in 0..<points.count - 1 {
let point1 = points[i]
let point2 = points[i + 1]
let delta = point2 - point1
let rawValue = atan2(delta.y, delta.x)
var value: CGFloat
if i == 0 {
value = 0 // we will apply this value from rawValues
} else {
let lastValue = rawValues.last!
value = rawValue - lastValue
}
if value < 0 {
value = .pi * 2 + value
}
rawValues.append(rawValue)
values.append(value)
}
for (i, p) in values.enumerated() {
print("\(rawValues[i]): \(p)")
}
turnAnimation.values = values
turnAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.repeatCount = Float.greatestFiniteMagnitude
turnAnimation.isAdditive = true
arrowPointLayer.transform = CATransform3DMakeRotation(rawValues.first!, 0, 0, 1)
arrowPointLayer.add(turnAnimation, forKey: "rotateArrow")
不要将 values
设为绝对旋转值的列表,而是将其设为旋转 差异 的列表,并将动画的 isAdditive
设置为 true
.这样,你 就可以告诉箭头向哪个方向转,转多少。
我这样测试(在我的测试中,v
只是我界面中的一些视图):
let turnAnimation = CAKeyframeAnimation(keyPath: "transform.rotation")
turnAnimation.duration = 5
let values : [CGFloat] = [0,2,-1,2,-1,3,-3]
turnAnimation.values = values
turnAnimation.timingFunction =
CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
turnAnimation.isAdditive = true
self.v.layer.add(turnAnimation, forKey: "rotateArrow")
视图的行为完全符合我的预期。