-
Notifications
You must be signed in to change notification settings - Fork 12
Jumpy compass animation when viewpoints are animated back to zero #266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The jump is explained by the fact that before the animation actually starts, the viewpoint rotation is in fact zero, so the compass immediately displays a heading of zero. When the animation starts, the scene view's viewpoint is updated, causing the compass to be updated to the heading at the start of the animation. The solution is to observe when the viewpoint rotation changes and before starting the animation, set the viewpoint rotation back to the old value. Since the reproducer only cares about the @State private var controller: OrbitLocationCameraController =
OrbitLocationCameraController(
targetPoint: Point(
x: -117.19494,
y: 34.05723,
spatialReference: .wgs84
),
distance: 2_000
)!
@State private var viewpointRotation: Double = .nan
@State private var scene = Scene(basemapStyle: .arcGISCommunity)
var body: some View {
SceneView(scene: scene, cameraController: controller)
.onViewpointChanged(kind: .centerAndScale) { newViewpoint in
viewpointRotation = newViewpoint.rotation
}
.onChange(of: viewpointRotation) { [oldViewpointRotation = viewpointRotation] newViewpointRotation in
guard newViewpointRotation == .zero && oldViewpointRotation != .zero else {
return
}
// Reset the viewpoint rotation since it will be animated to zero.
viewpointRotation = oldViewpointRotation
let delta = oldViewpointRotation > .zero && oldViewpointRotation < 180
? -oldViewpointRotation
: 360 - oldViewpointRotation
Task {
try await controller.moveCamera(
distanceDelta: .zero,
headingDelta: delta,
pitchDelta: .zero,
duration: 0.3
)
}
}
.overlay(alignment: .topTrailing) {
Compass(viewpointRotation: $viewpointRotation, autoHide: false)
.padding()
}
} |
I've been thinking about this the past few days and I can't help but wonder if the compass should be read only, as a compass is essentially just an instrument. In the case of a mapping app, a compass acts as a (fancy) button. When this component was originally written, viewpoint animations weren't yet supported and so simply setting heading to zero at that time made sense. Now that viewpoint animations are supported, I presume they will be used more often than not. It seems to me that letting the user define what happens when the compass is tapped (via a closure) might be the best path, especially when you consider they may be using a map with a viewpoint, a scene with a camera or a scene with a camera controller. This would drop the need to check the new and old rotation for zero, and instead we'd confidently know when the user intended to put their map/scene back to north. I've prototyped this out on branch A PR of my branch would also close #25 Alternatively, the compass could also take a closure to perform on a tap (either via an init parameter or modifier) but the branch in it's current form is a lighter weight solution. |
Resolved by #286 |
Because the compass simply sets heading back to .zero when tapped, the needle will visibly jump to zero and back and then finally animate to zero.
The compass should be updated to work well with newly added animated viewpoint functionality.
Reproducer
The text was updated successfully, but these errors were encountered: