fix(animation): settle fullscreen zoom at pixel precision
This commit is contained in:
1 parent
a6ec721437
commit
87bc68fe8a
1 file changed
+51
-2
@@ -416,9 +416,22 @@ impl WindowGeometry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(zoom) = &mut self.zoom {
|
if let Some(zoom) = &mut self.zoom {
|
||||||
let active = zoom.progress.advance(delta, config);
|
|
||||||
let p = zoom.progress.current;
|
|
||||||
let target = self.visual.target;
|
let target = self.visual.target;
|
||||||
|
// Spring tolerances describe pixels and pixels/second, whereas this
|
||||||
|
// spring advances unit progress. Without conversion it snaps several
|
||||||
|
// pixels early on large zooms, visibly shifting the trailing edge.
|
||||||
|
let travel = rect_distance(zoom.from, target)
|
||||||
|
.max((zoom.from.x + zoom.from.width - target.x - target.width).abs())
|
||||||
|
.max((zoom.from.y + zoom.from.height - target.y - target.height).abs())
|
||||||
|
.max(1.0);
|
||||||
|
let config = config.normalized();
|
||||||
|
let progress_config = SpringConfig {
|
||||||
|
position_tolerance: config.position_tolerance / travel,
|
||||||
|
velocity_tolerance: config.velocity_tolerance / travel,
|
||||||
|
..config
|
||||||
|
};
|
||||||
|
let active = zoom.progress.advance(delta, progress_config);
|
||||||
|
let p = zoom.progress.current;
|
||||||
let lerp = |a: f64, b: f64| a + (b - a) * p;
|
let lerp = |a: f64, b: f64| a + (b - a) * p;
|
||||||
self.visual.current = Rect::new(
|
self.visual.current = Rect::new(
|
||||||
lerp(zoom.from.x, target.x),
|
lerp(zoom.from.x, target.x),
|
||||||
@@ -541,6 +554,42 @@ fn finite_or(value: f64, fallback: f64) -> f64 {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn zoom_finishes_without_a_visible_final_edge_jump() {
|
||||||
|
let tiled = Rect::new(12.0, 12.0, 942.0, 1056.0);
|
||||||
|
let fullscreen = Rect::new(0.0, 0.0, 1920.0, 1080.0);
|
||||||
|
for (start, target, fullscreen_target) in
|
||||||
|
[(tiled, fullscreen, true), (fullscreen, tiled, false)]
|
||||||
|
{
|
||||||
|
let mut geometry = WindowGeometry::new(start, None);
|
||||||
|
if !fullscreen_target {
|
||||||
|
geometry.set_presentation_target(start, true, Duration::ZERO);
|
||||||
|
geometry.advance(Duration::ZERO, SpringConfig::default(), false);
|
||||||
|
}
|
||||||
|
geometry.set_presentation_target(target, fullscreen_target, Duration::ZERO);
|
||||||
|
let mut finished = false;
|
||||||
|
for _ in 0..1000 {
|
||||||
|
let before = geometry.visual.current;
|
||||||
|
if !geometry.advance(
|
||||||
|
Duration::from_secs_f64(1.0 / 144.0),
|
||||||
|
SpringConfig::default(),
|
||||||
|
true,
|
||||||
|
) {
|
||||||
|
let right_before = before.x + before.width;
|
||||||
|
let right_after = target.x + target.width;
|
||||||
|
assert!(
|
||||||
|
(right_after - right_before).abs() < 0.5,
|
||||||
|
"final right-edge jump: {} pixels",
|
||||||
|
(right_after - right_before).abs()
|
||||||
|
);
|
||||||
|
finished = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(finished);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zoom_geometry_and_decorations_share_progress() {
|
fn zoom_geometry_and_decorations_share_progress() {
|
||||||
let start = Rect::new(200.0, 40.0, 600.0, 800.0);
|
let start = Rect::new(200.0, 40.0, 600.0, 800.0);
|
||||||
|
|||||||
Reference in new issue
Block a user