Skip to content

Commit

Permalink
transform and scale (#589)
Browse files Browse the repository at this point in the history
  • Loading branch information
jrmoulton authored Sep 24, 2024
1 parent 7a128a5 commit b9bcd24
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 60 deletions.
4 changes: 3 additions & 1 deletion examples/view-transition/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use floem::{
reactive::{RwSignal, SignalGet, SignalUpdate},
style::Style,
taffy::FlexWrap,
unit::UnitExt,
views::*,
IntoView,
};
Expand All @@ -27,7 +28,8 @@ impl ViewSwitcher {
ViewSwitcher::One => music_player::music_player().into_any(),
ViewSwitcher::Two => view_two(state).into_any(),
}
.animation(Animation::scale_effect)
.style(|s| s.scale(100.pct()))
.animation(Animation::scale_size_effect)
.clip()
.style(|s| s.padding(8))
.animation(|a| {
Expand Down
39 changes: 22 additions & 17 deletions examples/view-transition/src/music_player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use floem::{
animate::Animation,
peniko::{Brush, Color},
reactive::{RwSignal, SignalGet, SignalUpdate},
style::{Background, Transition},
style::{ScaleX, ScaleY, Style, Transition},
text::Weight,
unit::DurationUnitExt,
unit::{DurationUnitExt, UnitExt},
views::{
container, dyn_container, empty, h_stack, slider, svg, v_stack, ButtonClass, Decorators,
Stack, SvgClass,
Expand Down Expand Up @@ -129,7 +129,21 @@ pub fn music_player() -> impl IntoView {
.apply(box_shadow())
});

container(card).style(|s| {
let button_style = |s: Style| {
s.border(0)
.padding(5)
.items_center()
.justify_center()
.background(Color::TRANSPARENT)
.hover(|s| s.background(SLIDER))
.active(|s| {
s.class(SvgClass, |s| {
s.color(ICON).scale_x(50.pct()).scale_y(50.pct())
})
})
};

container(card).style(move |s| {
s.size(300, 175)
.items_center()
.justify_center()
Expand All @@ -139,20 +153,11 @@ pub fn music_player() -> impl IntoView {
s.size(20, 20)
.items_center()
.justify_center()
.transition_size(Transition::linear(25.millis()))
.transition_color(Transition::linear(25.millis()))
})
.class(ButtonClass, |s| {
s.border(0)
.size(25, 25)
.items_center()
.justify_center()
.background(Color::TRANSPARENT)
.hover(|s| s.background(SLIDER))
.active(|s| {
s.set_style_value(Background, floem::style::StyleValue::Unset)
.class(SvgClass, |s| s.size(12, 12).color(ICON))
})
.scale(100.pct())
.transition(ScaleX, Transition::spring(50.millis()))
.transition(ScaleY, Transition::spring(50.millis()))
.transition_color(Transition::linear(50.millis()))
})
.class(ButtonClass, button_style)
})
}
10 changes: 9 additions & 1 deletion src/animate/animation.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::{
animate::{AnimState, AnimStateCommand, AnimStateKind, Bezier, Easing, Linear, Spring},
style::{Style, StylePropRef},
unit::UnitExt,
view_state::StackOffset,
ViewId,
};
Expand Down Expand Up @@ -335,8 +336,15 @@ impl Animation {
.keyframe(100, |kf| kf.computed())
}

/// Quickly set an animation to be a view transition and set the animation to animate from size(0, 0) to the "normal" computed style of a view (the view with no animations applied).
/// Quickly set an animation to be a view transition and set the animation to animate from scale 0% to the "normal" computed style of a view (the view with no animations applied).
pub fn scale_effect(self) -> Self {
self.view_transition()
.keyframe(0, |kf| kf.style(|s| s.scale(0.pct())))
.debug_name("Scale the width and height from zero to the default")
}

/// Quickly set an animation to be a view transition and set the animation to animate from size(0, 0) to the "normal" computed style of a view (the view with no animations applied).
pub fn scale_size_effect(self) -> Self {
self.view_transition()
.keyframe(0, |kf| kf.style(|s| s.size(0, 0)))
.debug_name("Scale the width and height from zero to the default")
Expand Down
42 changes: 38 additions & 4 deletions src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1122,10 +1122,44 @@ impl<'a> PaintCx<'a> {
pub fn transform(&mut self, id: ViewId) -> Size {
if let Some(layout) = id.get_layout() {
let offset = layout.location;
let mut new = self.transform.as_coeffs();
new[4] += offset.x as f64;
new[5] += offset.y as f64;
self.transform = Affine::new(new);
self.transform *= Affine::translate(Vec2 {
x: offset.x as f64,
y: offset.y as f64,
});

let view_state = id.state();
let transform_x = match view_state.borrow().layout_props.transform_x() {
crate::unit::PxPct::Px(px) => px,
crate::unit::PxPct::Pct(pct) => pct / 100.,
};
let transform_y = match view_state.borrow().layout_props.transform_y() {
crate::unit::PxPct::Px(px) => px,
crate::unit::PxPct::Pct(pct) => pct / 100.,
};
self.transform *= Affine::translate(Vec2 {
x: transform_x,
y: transform_y,
});

let scale_x = match view_state.borrow().layout_props.scale_x() {
crate::unit::PxPct::Px(px) => px / layout.size.width as f64,
crate::unit::PxPct::Pct(pct) => pct / 100.,
};
let scale_y = match view_state.borrow().layout_props.scale_y() {
crate::unit::PxPct::Px(px) => px / layout.size.height as f64,
crate::unit::PxPct::Pct(pct) => pct / 100.,
};
let center_x = layout.size.width as f64 / 2.0;
let center_y = layout.size.height as f64 / 2.0;
self.transform *= Affine::translate(Vec2 {
x: center_x,
y: center_y,
});
self.transform *= Affine::scale_non_uniform(scale_x, scale_y);
self.transform *= Affine::translate(Vec2 {
x: -center_x,
y: -center_y,
});
self.paint_state.renderer_mut().transform(self.transform);

if let Some(rect) = self.clip.as_mut() {
Expand Down
17 changes: 15 additions & 2 deletions src/style.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
//! # Style
//!
//!

use floem_reactive::create_updater;
use floem_renderer::text::{LineHeightValue, Weight};
Expand Down Expand Up @@ -1592,6 +1590,10 @@ define_builtin_props!(
AspectRatio aspect_ratio: Option<f32> {} = None,
ColGap col_gap nocb: PxPct {} = PxPct::Px(0.),
RowGap row_gap nocb: PxPct {} = PxPct::Px(0.),
ScaleX scale_x: PxPct {} = PxPct::Pct(100.),
ScaleY scale_y: PxPct {} = PxPct::Pct(100.),
TransformX transform_x: PxPct {} = PxPct::Px(0.),
TransformY transform_y: PxPct {} = PxPct::Px(0.),
);

prop_extractor! {
Expand Down Expand Up @@ -1640,6 +1642,12 @@ prop_extractor! {

pub row_gap: RowGap,
pub col_gap: ColGap,

pub scale_x: ScaleX,
pub scale_y: ScaleY,

pub transform_x: TransformX,
pub transform_y: TransformY,
}
}
impl LayoutProps {
Expand Down Expand Up @@ -2169,6 +2177,11 @@ impl Style {
self.set(ZIndex, Some(z_index))
}

pub fn scale(self, scale: impl Into<PxPct>) -> Self {
let val = scale.into();
self.scale_x(val).scale_y(val)
}

/// Allow the application of a function if the option exists.
/// This is useful for chaining together a bunch of optional style changes.
/// ```rust
Expand Down
14 changes: 12 additions & 2 deletions src/views/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ use floem_renderer::{
use peniko::kurbo::Size;
use sha2::{Digest, Sha256};

use crate::{id::ViewId, style_class, view::View};
use crate::{id::ViewId, prop_extractor, style::TextColor, style_class, view::View};

use super::Decorators;

style_class!(pub SvgClass);

prop_extractor!(SvgProps {
text_color: TextColor,
});

pub struct Svg {
id: ViewId,
props: SvgProps,
svg_tree: Option<Tree>,
svg_hash: Option<Vec<u8>>,
}
Expand All @@ -37,6 +42,7 @@ pub fn svg(svg_str: impl Into<String> + 'static) -> Svg {
id,
svg_tree: None,
svg_hash: None,
props: Default::default(),
}
.class(SvgClass)
}
Expand All @@ -46,6 +52,10 @@ impl View for Svg {
self.id
}

fn style_pass(&mut self, cx: &mut crate::context::StyleCx<'_>) {
self.props.read(cx);
}

fn update(&mut self, _cx: &mut crate::context::UpdateCx, state: Box<dyn std::any::Any>) {
if let Ok(state) = state.downcast::<String>() {
let text = &*state;
Expand All @@ -67,7 +77,7 @@ impl View for Svg {
let hash = self.svg_hash.as_ref().unwrap();
let layout = self.id.get_layout().unwrap_or_default();
let rect = Size::new(layout.size.width as f64, layout.size.height as f64).to_rect();
let color = self.id.state().borrow().combined_style.builtin().color();
let color = self.props.text_color();
cx.draw_svg(floem_renderer::Svg { tree, hash }, rect, color);
}
}
Expand Down
Loading

0 comments on commit b9bcd24

Please sign in to comment.