fix(core/mercury): improve share words swiping animation

pull/4065/head
tychovrahe 2 months ago committed by TychoVrahe
parent 008490bf4b
commit a15dd6598f

@ -0,0 +1 @@
[T3T1] Improve share words swiping animation

@ -1,25 +1,25 @@
use super::{theme, InternallySwipableContent};
use crate::{
strutil::TString,
time::Duration,
translations::TR,
ui::{
animation::Animation,
component::{
base::AttachType,
swipe_detect::{SwipeConfig, SwipeSettings},
Component, Event, EventCtx, Never, SwipeDirection,
},
event::SwipeEvent,
geometry::{Alignment, Alignment2D, Insets, Offset, Rect},
model_mercury::component::{Frame, FrameMsg, InternallySwipable},
model_mercury::component::{
swipe_content::SwipeAttachAnimation, Frame, FrameMsg, InternallySwipable,
},
shape::{self, Renderer},
util,
},
};
use heapless::Vec;
const MAX_WORDS: usize = 33; // super-shamir has 33 words, all other have less
const ANIMATION_DURATION_MS: Duration = Duration::from_millis(166);
type IndexVec = Vec<u8, MAX_WORDS>;
/// Component showing mnemonic/share words during backup procedure. Model T3T1
@ -133,9 +133,9 @@ struct ShareWordsInner<'a> {
next_index: i16,
/// Area reserved for a shown word from mnemonic/share
area_word: Rect,
/// `Some` when transition animation is in progress
animation: Option<Animation<f32>>,
progress: i16,
attach_animation: SwipeAttachAnimation,
wait_for_attach: bool,
}
impl<'a> ShareWordsInner<'a> {
@ -147,8 +147,9 @@ impl<'a> ShareWordsInner<'a> {
page_index: 0,
next_index: 0,
area_word: Rect::zero(),
animation: None,
progress: 0,
attach_animation: SwipeAttachAnimation::new(),
wait_for_attach: false,
}
}
@ -175,6 +176,27 @@ impl<'a> ShareWordsInner<'a> {
.render(target);
});
}
fn should_animate_progress(&self) -> (SwipeDirection, bool) {
let (dir, should_animate) = if self.page_index < self.next_index {
(SwipeDirection::Up, !self.is_final_page())
} else {
(SwipeDirection::Down, !self.is_first_page())
};
(dir, should_animate)
}
fn should_animate_attach(&self, event: Event) -> (SwipeDirection, bool) {
match event {
Event::Attach(AttachType::Swipe(SwipeDirection::Up)) => {
(SwipeDirection::Up, !self.is_first_page())
}
Event::Attach(AttachType::Swipe(SwipeDirection::Down)) => {
(SwipeDirection::Down, !self.is_final_page())
}
_ => (SwipeDirection::Up, false),
}
}
}
impl<'a> Component for ShareWordsInner<'a> {
@ -197,19 +219,29 @@ impl<'a> Component for ShareWordsInner<'a> {
fn event(&mut self, ctx: &mut EventCtx, event: Event) -> Option<Self::Msg> {
ctx.set_page_count(self.share_words.len());
let (_, should_animate) = self.should_animate_attach(event);
self.attach_animation.lazy_start(ctx, event, should_animate);
match event {
Event::Attach(_) => {
self.progress = 0;
if !should_animate {
self.wait_for_attach = false;
}
}
Event::Swipe(SwipeEvent::End(dir)) => match dir {
SwipeDirection::Up if !self.is_final_page() => {
self.progress = 0;
self.page_index = (self.page_index + 1).min(self.share_words.len() as i16 - 1);
self.wait_for_attach = true;
ctx.request_paint();
}
SwipeDirection::Down if !self.is_first_page() => {
self.progress = 0;
self.page_index = self.page_index.saturating_sub(1);
self.wait_for_attach = true;
ctx.request_paint();
}
_ => {}
@ -260,35 +292,34 @@ impl<'a> Component for ShareWordsInner<'a> {
.with_fg(theme::GREY)
.render(target);
let (dir, should_animate) = if self.page_index < self.next_index {
(
SwipeDirection::Up,
self.page_index < self.share_words.len() as i16 - 1,
)
} else {
(SwipeDirection::Down, self.page_index > 0)
};
let (dir, should_animate_progress) = self.should_animate_progress();
if self.progress > 0 && should_animate {
if self.progress > 0 && should_animate_progress {
target.in_clip(self.area_word, &|target| {
let progress = pareen::constant(0.0).seq_ease_out(
0.0,
easer::functions::Cubic,
1.0,
pareen::constant(1.0),
);
util::render_slide(
|target| self.render_word(self.page_index, target, target.viewport().clip),
|target| self.render_word(self.next_index, target, target.viewport().clip),
progress.eval(self.progress as f32 / 1000.0),
dir,
target,
)
let bounds = target.viewport().clip;
let full_offset = dir.as_offset(bounds.size());
let current_offset = full_offset * (self.progress as f32 / 1000.0);
target.with_origin(current_offset, &|target| {
self.render_word(self.page_index, target, target.viewport().clip)
});
});
} else if (self.attach_animation.is_active() || self.wait_for_attach) && self.progress == 0
{
let t = self.attach_animation.eval();
let offset = self
.attach_animation
.get_offset(t, ShareWordsInner::AREA_WORD_HEIGHT);
target.in_clip(self.area_word, &|target| {
target.with_origin(offset, &|target| {
self.render_word(self.page_index, target, target.viewport().clip)
});
});
} else {
self.render_word(self.page_index, target, self.area_word);
};
}
}
}

@ -14,13 +14,13 @@ use crate::{
};
#[derive(Default, Clone)]
struct AttachAnimation {
pub struct SwipeAttachAnimation {
pub timer: Stopwatch,
pub attach_type: Option<AttachType>,
pub show_attach_anim: bool,
}
impl AttachAnimation {
impl SwipeAttachAnimation {
const DURATION_MS: u32 = 500;
pub fn new() -> Self {
@ -48,7 +48,7 @@ impl AttachAnimation {
self.timer.elapsed().to_millis() as f32 / 1000.0
}
pub fn get_offset(&self, t: f32) -> Offset {
pub fn get_offset(&self, t: f32, max_offset: i16) -> Offset {
let value = pareen::constant(0.0).seq_ease_in(
0.0,
easer::functions::Linear,
@ -58,14 +58,14 @@ impl AttachAnimation {
match self.attach_type {
Some(AttachType::Initial) => {
Offset::lerp(Offset::new(0, -20), Offset::zero(), value.eval(t))
Offset::lerp(Offset::new(0, -max_offset), Offset::zero(), value.eval(t))
}
Some(AttachType::Swipe(dir)) => match dir {
SwipeDirection::Up => {
Offset::lerp(Offset::new(0, 20), Offset::zero(), value.eval(t))
Offset::lerp(Offset::new(0, max_offset), Offset::zero(), value.eval(t))
}
SwipeDirection::Down => {
Offset::lerp(Offset::new(0, -20), Offset::zero(), value.eval(t))
Offset::lerp(Offset::new(0, -max_offset), Offset::zero(), value.eval(t))
}
_ => Offset::zero(),
},
@ -130,7 +130,7 @@ impl AttachAnimation {
struct SwipeContext {
progress: i16,
dir: SwipeDirection,
attach_animation: AttachAnimation,
attach_animation: SwipeAttachAnimation,
}
impl SwipeContext {
@ -138,7 +138,7 @@ impl SwipeContext {
Self {
progress: 0,
dir: SwipeDirection::Up,
attach_animation: AttachAnimation::new(),
attach_animation: SwipeAttachAnimation::new(),
}
}
@ -174,7 +174,7 @@ impl SwipeContext {
} else {
let t = self.attach_animation.eval();
let opacity = self.attach_animation.get_opacity(t);
offset = self.attach_animation.get_offset(t);
offset = self.attach_animation.get_offset(t, 20);
mask = 255 - opacity;
if offset.x == 0 && offset.y == 0 {

@ -1,6 +1,5 @@
use crate::{
strutil::{ShortString, TString},
time::Duration,
ui::{
component::text::TextStyle,
display,
@ -191,32 +190,6 @@ macro_rules! include_res {
}
pub(crate) use include_res;
pub const SLIDE_DURATION_MS: Duration = Duration::from_millis(333);
#[cfg(feature = "new_rendering")]
pub fn render_slide<'s, F0, F1, R>(
render_old: F0,
render_new: F1,
progress: f32,
direction: crate::ui::component::SwipeDirection,
target: &mut R,
) where
R: crate::ui::shape::Renderer<'s>,
F0: Fn(&mut R),
F1: Fn(&mut R),
{
let bounds = target.viewport().clip;
let full_offset = direction.as_offset(bounds.size());
let current_offset = full_offset * progress;
target.with_origin(current_offset, &|target| {
render_old(target);
});
target.with_origin(current_offset - full_offset, &|target| {
render_new(target);
});
}
#[cfg(test)]
mod tests {
use crate::strutil;

Loading…
Cancel
Save