我有一个应用程序,可以显示一群人,每个人都有其原点和角度。
struct Location {
var centre:CGPoint
var facing:Angle
}
SwiftUI神奇地自动将许多动画从位置A移到位置B
withAnimation {
person.location = newLocation
}
但是-对于“角度(朝向)”属性,我希望动画以最短的路线进行(请记住,在现实世界中,角度会环绕)。
例如当角度改变5-> 10(度)
时,Swift UI会正确设置动画5,6,7,8,9,10
但是从2到358,需要很长的路要走
SwiftUI执行2,3,4,5,6,7 .......,357,358
我想在哪里做
2,1,0,359,358
我该怎么办?
谢谢
更新:我希望有一个解决方案,允许我使用动画系统,也许使用新的MyAngle结构直接提供动画步骤,或者使用某种动画修改器。 .easeInOut修改了步骤-是否有等效的方法可以创建.goTheRightWay动画?
答案 0 :(得分:0)
如何将newLocation值调整为距开始位置不超过180˚?这是一个检查动画距离是否大于一半的功能,并提供一个可以满足要求的新端点。
func adjustedEnd(from start: CGFloat, to target: CGFloat) -> CGFloat {
// Shift end to be greater than start
var end = target
while end < start { end += 360 }
// Mod the distance with 360, shifting by 180 to keep on the same side of a circle
return (end - start + 180).truncatingRemainder(dividingBy: 360) - 180 + start
}
一些示例测试用例:
let startValues: [CGFloat] = [2, -10, 345, 365, 700]
let endValues: [CGFloat] = [2, 10, 180, 185, 350, -10, 715, -700]
for start in startValues {
print("From \(start):")
for end in endValues {
let adjusted = adjustedEnd(from: start, to: end)
print("\t\(end) \tbecomes \(adjusted);\tdistance \(abs(adjusted - start))")
}
}
打印以下内容:
From 2.0:
2.0 becomes 2.0; distance 0.0
10.0 becomes 10.0; distance 8.0
180.0 becomes 180.0; distance 178.0
185.0 becomes -175.0; distance 177.0
350.0 becomes -10.0; distance 12.0
-10.0 becomes -10.0; distance 12.0
715.0 becomes -5.0; distance 7.0
-700.0 becomes 20.0; distance 18.0
From -10.0:
2.0 becomes 2.0; distance 12.0
10.0 becomes 10.0; distance 20.0
180.0 becomes -180.0; distance 170.0
185.0 becomes -175.0; distance 165.0
350.0 becomes -10.0; distance 0.0
-10.0 becomes -10.0; distance 0.0
715.0 becomes -5.0; distance 5.0
-700.0 becomes 20.0; distance 30.0
From 345.0:
2.0 becomes 362.0; distance 17.0
10.0 becomes 370.0; distance 25.0
180.0 becomes 180.0; distance 165.0
185.0 becomes 185.0; distance 160.0
350.0 becomes 350.0; distance 5.0
-10.0 becomes 350.0; distance 5.0
715.0 becomes 355.0; distance 10.0
-700.0 becomes 380.0; distance 35.0
From 365.0:
2.0 becomes 362.0; distance 3.0
10.0 becomes 370.0; distance 5.0
180.0 becomes 540.0; distance 175.0
185.0 becomes 185.0; distance 180.0
350.0 becomes 350.0; distance 15.0
-10.0 becomes 350.0; distance 15.0
715.0 becomes 355.0; distance 10.0
-700.0 becomes 380.0; distance 15.0
From 700.0:
2.0 becomes 722.0; distance 22.0
10.0 becomes 730.0; distance 30.0
180.0 becomes 540.0; distance 160.0
185.0 becomes 545.0; distance 155.0
350.0 becomes 710.0; distance 10.0
-10.0 becomes 710.0; distance 10.0
715.0 becomes 715.0; distance 15.0
-700.0 becomes 740.0; distance 40.0
(已编辑,以计算负的最终值)
编辑:从您关于保持第二个值的评论中,如何将Location.facing
设置为调整后的角度,然后在“位置”中添加类似内容
var prettyFacing: Angle {
var facing = self.facing
while facing.degrees < 0 { facing += Angle(degrees: 360) }
while facing.degrees > 360 { facing -= Angle(degrees: 360) }
return facing
}
答案 1 :(得分:0)
好-发布我自己的答案。 它的工作方式类似于@Ben的答案-但将“阴影角度”管理移至旋转效果。
您要做的就是将rotationEffect(angle:Angle)
的{{1}}切换为
看起来像
shortRotationEffect(angle:Angle,id:UUID)
ShortRotationEffect使用提供的ID维护先前角度的字典。当您设置一个新角度时,它将计算出一个等效的角度,该角度可提供较短的旋转并将其应用于法线 Image(systemName: "person.fill").resizable()
.frame(width: 50, height: 50)
.shortRotationEffect(self.person.angle,id:person.id)
.animation(.easeInOut)
这里是:
rotationEffect(...)
这取决于我的正模函数:
extension View {
/// Like RotationEffect - but when animated, the rotation moves in the shortest direction.
/// - Parameters:
/// - angle: new angle
/// - anchor: anchor point
/// - id: unique id for the item being displayed. This is used as a key to maintain the rotation history and figure out the right direction to move
func shortRotationEffect(_ angle: Angle,anchor: UnitPoint = .center, id:UUID) -> some View {
modifier(ShortRotation(angle: angle,anchor:anchor, id:id))
}
}
struct ShortRotation: ViewModifier {
static var storage:[UUID:Angle] = [:]
var angle:Angle
var anchor:UnitPoint
var id:UUID
func getAngle() -> Angle {
var newAngle = angle
if let lastAngle = ShortRotation.storage[id] {
let change:Double = (newAngle.degrees - lastAngle.degrees) %% 360.double
if change < 180 {
newAngle = lastAngle + Angle.init(degrees: change)
}
else {
newAngle = lastAngle + Angle.init(degrees: change - 360)
}
}
ShortRotation.storage[id] = newAngle
return newAngle
}
func body(content: Content) -> some View {
content
.rotationEffect(getAngle(),anchor: anchor)
}
}
答案 2 :(得分:0)