mirror of
https://github.com/danbulant/cushy
synced 2026-07-05 19:20:36 +00:00
Making all easing functions themable
This commit is contained in:
parent
e773bc123a
commit
9a148b8765
8 changed files with 205 additions and 22 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
<!-- markdownlint-disable no-duplicate-heading -->
|
<!-- markdownlint-disable no-duplicate-heading -->
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `Collapse`, `OverlayLayer`, and `Progress` all honor the theme components
|
||||||
|
`EasingIn` and `EasingOut` rather than hard-coded easing functions.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `ComponentProbe<T>` is a new widget that allows reading a
|
||||||
|
`ComponentDefinition` value from the theme at runtime through a
|
||||||
|
`Dynamic<T::ComponentType>`. For example, a `ComponentProbe<TextSize>` will
|
||||||
|
provide access to a `Dynamic<Dimension>`. Previously this required creating a
|
||||||
|
custom widget to access the runtime theme information.
|
||||||
|
|
||||||
|
`ContextFreeComponent::probe()` and `ContextFreeComponent::probe_wrapping()`
|
||||||
|
provide an easy interface for creating probes from components.
|
||||||
|
|
||||||
## v0.4.0 (2024-08-20)
|
## v0.4.0 (2024-08-20)
|
||||||
|
|
||||||
### Breaking Changes
|
### Breaking Changes
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,9 @@ use crate::context::{Trackable, WidgetContext};
|
||||||
use crate::names::Name;
|
use crate::names::Name;
|
||||||
use crate::utils::Lazy;
|
use crate::utils::Lazy;
|
||||||
use crate::value::{Dynamic, IntoValue, Source, Value};
|
use crate::value::{Dynamic, IntoValue, Source, Value};
|
||||||
|
use crate::widget::MakeWidget;
|
||||||
use crate::widgets::input::CowString;
|
use crate::widgets::input::CowString;
|
||||||
|
use crate::widgets::ComponentProbe;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod components;
|
pub mod components;
|
||||||
|
|
@ -1135,6 +1137,25 @@ pub trait ComponentDefinition: NamedComponent {
|
||||||
fn default_value(&self, context: &WidgetContext<'_>) -> Self::ComponentType;
|
fn default_value(&self, context: &WidgetContext<'_>) -> Self::ComponentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A [`ComponentDefinition`] that can provide a default value without access to
|
||||||
|
/// a runtime context.
|
||||||
|
pub trait ContextFreeComponent: ComponentDefinition {
|
||||||
|
/// Returns the default value for this component.
|
||||||
|
fn default(&self) -> Self::ComponentType;
|
||||||
|
|
||||||
|
/// Returns a new probe that provides access to the runtime value of this
|
||||||
|
/// component.
|
||||||
|
fn probe(self) -> ComponentProbe<Self> {
|
||||||
|
ComponentProbe::default_for(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new probe wrapping `child` that provides access to the runtime
|
||||||
|
/// value of this component.
|
||||||
|
fn probe_wrapping(self, child: impl MakeWidget) -> ComponentProbe<Self> {
|
||||||
|
ComponentProbe::default_wrapping(self, child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Describes whether a type should invalidate a widget.
|
/// Describes whether a type should invalidate a widget.
|
||||||
pub trait RequireInvalidation {
|
pub trait RequireInvalidation {
|
||||||
/// Cushy tracks two different states:
|
/// Cushy tracks two different states:
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ macro_rules! define_components {
|
||||||
|
|
||||||
define_components!($type, $($($default)*)?);
|
define_components!($type, $($($default)*)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_components!(default $component $type, $($($default)*)?);
|
||||||
};
|
};
|
||||||
|
|
||||||
)*)*};
|
)*)*};
|
||||||
|
|
@ -90,6 +92,29 @@ macro_rules! define_components {
|
||||||
($type:ty, $($expr:tt)+) => {
|
($type:ty, $($expr:tt)+) => {
|
||||||
define_components!($type, |_context| $($expr)*);
|
define_components!($type, |_context| $($expr)*);
|
||||||
};
|
};
|
||||||
|
(default $component:ident $type:ty, . $($path:tt)*) => {
|
||||||
|
|
||||||
|
};
|
||||||
|
(default $component:ident $type:ty, |$context:ident| $($expr:tt)*) => {
|
||||||
|
};
|
||||||
|
(default $component:ident $type:ty, @$path:path) => {
|
||||||
|
};
|
||||||
|
(default $component:ident $type:ty, contrasting!($bg:ident, $($fg:ident),+ $(,)?)) => {
|
||||||
|
};
|
||||||
|
(default $component:ident $type:ty, ) => {
|
||||||
|
impl $crate::styles::ContextFreeComponent for $component {
|
||||||
|
fn default(&self) -> Self::ComponentType {
|
||||||
|
<$type>::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(default $component:ident $type:ty, $($expr:tt)+) => {
|
||||||
|
impl $crate::styles::ContextFreeComponent for $component {
|
||||||
|
fn default(&self) -> Self::ComponentType {
|
||||||
|
$($expr)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
define_components! {
|
define_components! {
|
||||||
|
|
@ -204,7 +229,7 @@ define_components! {
|
||||||
LayoutOrder(VisualOrder, "visual_order", VisualOrder::left_to_right())
|
LayoutOrder(VisualOrder, "visual_order", VisualOrder::left_to_right())
|
||||||
/// The set of controls to allow focusing via tab key and initial focus
|
/// The set of controls to allow focusing via tab key and initial focus
|
||||||
/// selection.
|
/// selection.
|
||||||
AutoFocusableControls(FocusableWidgets, "focus", FocusableWidgets::default())
|
AutoFocusableControls(FocusableWidgets, "focus")
|
||||||
/// A [`Color`] to be used as the background color of a widget.
|
/// A [`Color`] to be used as the background color of a widget.
|
||||||
WidgetBackground(Color, "widget_backgrond_color", Color::CLEAR_WHITE)
|
WidgetBackground(Color, "widget_backgrond_color", Color::CLEAR_WHITE)
|
||||||
/// A [`Color`] to be used to accent a widget.
|
/// A [`Color`] to be used to accent a widget.
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ mod canvas;
|
||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
mod collapse;
|
mod collapse;
|
||||||
pub mod color;
|
pub mod color;
|
||||||
|
mod component_probe;
|
||||||
pub mod container;
|
pub mod container;
|
||||||
mod custom;
|
mod custom;
|
||||||
mod data;
|
mod data;
|
||||||
|
|
@ -40,6 +41,7 @@ pub use self::button::Button;
|
||||||
pub use self::canvas::Canvas;
|
pub use self::canvas::Canvas;
|
||||||
pub use self::checkbox::Checkbox;
|
pub use self::checkbox::Checkbox;
|
||||||
pub use self::collapse::Collapse;
|
pub use self::collapse::Collapse;
|
||||||
|
pub use self::component_probe::ComponentProbe;
|
||||||
pub use self::container::Container;
|
pub use self::container::Container;
|
||||||
pub use self::custom::Custom;
|
pub use self::custom::Custom;
|
||||||
pub use self::data::Data;
|
pub use self::data::Data;
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@ use std::time::Duration;
|
||||||
use figures::units::Px;
|
use figures::units::Px;
|
||||||
use figures::{Size, Zero};
|
use figures::{Size, Zero};
|
||||||
|
|
||||||
use crate::animation::easings::{EaseInQuadradic, EaseOutQuadradic};
|
use crate::animation::{AnimationHandle, AnimationTarget, Spawn};
|
||||||
use crate::animation::{AnimationHandle, AnimationTarget, EasingFunction, Spawn};
|
|
||||||
use crate::context::LayoutContext;
|
use crate::context::LayoutContext;
|
||||||
|
use crate::styles::components::{EasingIn, EasingOut};
|
||||||
use crate::value::{Dynamic, IntoDynamic, Source};
|
use crate::value::{Dynamic, IntoDynamic, Source};
|
||||||
use crate::widget::{MakeWidget, WidgetRef, WrappedLayout, WrapperWidget};
|
use crate::widget::{MakeWidget, WidgetRef, WrappedLayout, WrapperWidget};
|
||||||
use crate::ConstraintLimit;
|
use crate::ConstraintLimit;
|
||||||
|
|
@ -53,9 +53,9 @@ impl Collapse {
|
||||||
|
|
||||||
fn note_child_size(&mut self, size: Px, context: &mut LayoutContext<'_, '_, '_, '_>) {
|
fn note_child_size(&mut self, size: Px, context: &mut LayoutContext<'_, '_, '_, '_>) {
|
||||||
let (easing, target) = if self.collapse.get_tracking_invalidate(context) {
|
let (easing, target) = if self.collapse.get_tracking_invalidate(context) {
|
||||||
(EasingFunction::from(EaseOutQuadradic), Px::ZERO)
|
(context.get(&EasingOut), Px::ZERO)
|
||||||
} else {
|
} else {
|
||||||
(EasingFunction::from(EaseInQuadradic), size)
|
(context.get(&EasingIn), size)
|
||||||
};
|
};
|
||||||
match &self.collapse_animation {
|
match &self.collapse_animation {
|
||||||
Some(state) if state.target == target => {}
|
Some(state) if state.target == target => {}
|
||||||
|
|
|
||||||
96
src/widgets/component_probe.rs
Normal file
96
src/widgets/component_probe.rs
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use super::Space;
|
||||||
|
use crate::styles::{ComponentDefinition, ContextFreeComponent};
|
||||||
|
use crate::value::{Destination, Dynamic};
|
||||||
|
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
|
||||||
|
|
||||||
|
/// A widget that provides access to a [`ComponentDefinition`]'s value through a
|
||||||
|
/// [`Dynamic`].
|
||||||
|
///
|
||||||
|
/// This widget enables access to runtime values provided by the theme without
|
||||||
|
/// creating a custom widget. After creating a probe, [`value()`](Self::value)
|
||||||
|
/// can be used to observe and use the value.
|
||||||
|
///
|
||||||
|
/// The theme information retrieved will be the effective theme at the location
|
||||||
|
/// the probe is inserted in the widget hierarchy.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ComponentProbe<Component>
|
||||||
|
where
|
||||||
|
Component: ComponentDefinition,
|
||||||
|
{
|
||||||
|
component: Component,
|
||||||
|
probed: Dynamic<Component::ComponentType>,
|
||||||
|
child: WidgetRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Component> ComponentProbe<Component>
|
||||||
|
where
|
||||||
|
Component: ComponentDefinition,
|
||||||
|
{
|
||||||
|
/// Returns a new probe that provides access to the runtime value of
|
||||||
|
/// `Component`.
|
||||||
|
///
|
||||||
|
/// The initial contents of the dynamic will be `initial_value`.
|
||||||
|
pub fn new(component: Component, initial_value: Component::ComponentType) -> Self {
|
||||||
|
Self::new_wrapping(component, initial_value, Space::clear())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new probe wrapping `child` that provides access to the runtime
|
||||||
|
/// value of this component.
|
||||||
|
///
|
||||||
|
/// The initial contents of the dynamic will be `initial_value`.
|
||||||
|
pub fn new_wrapping(
|
||||||
|
component: Component,
|
||||||
|
initial_value: Component::ComponentType,
|
||||||
|
child: impl MakeWidget,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
component,
|
||||||
|
probed: Dynamic::new(initial_value),
|
||||||
|
child: WidgetRef::new(child),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new probe that provides access to the runtime value of
|
||||||
|
/// `Component`.
|
||||||
|
pub fn default_for(component: Component) -> Self
|
||||||
|
where
|
||||||
|
Component: ContextFreeComponent,
|
||||||
|
{
|
||||||
|
let default = component.default();
|
||||||
|
Self::new(component, default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new probe wrapping `child` that provides access to the runtime
|
||||||
|
/// value of this component.
|
||||||
|
pub fn default_wrapping(component: Component, child: impl MakeWidget) -> Self
|
||||||
|
where
|
||||||
|
Component: ContextFreeComponent,
|
||||||
|
{
|
||||||
|
let default = component.default();
|
||||||
|
Self::new_wrapping(component, default, child)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the dynamic that contains the component's current value.
|
||||||
|
///
|
||||||
|
/// This dynamic's contents will be updated whenever this probe is
|
||||||
|
/// invalidated.
|
||||||
|
pub const fn value(&self) -> &Dynamic<Component::ComponentType> {
|
||||||
|
&self.probed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Component> WrapperWidget for ComponentProbe<Component>
|
||||||
|
where
|
||||||
|
Component: ComponentDefinition + Debug + Send + 'static,
|
||||||
|
Component::ComponentType: PartialEq + Debug + Send + 'static,
|
||||||
|
{
|
||||||
|
fn child_mut(&mut self) -> &mut crate::widget::WidgetRef {
|
||||||
|
&mut self.child
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redraw_foreground(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
|
||||||
|
self.probed.set(context.get(&self.component));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,14 +6,15 @@ use std::time::Duration;
|
||||||
|
|
||||||
use alot::{LotId, OrderedLots};
|
use alot::{LotId, OrderedLots};
|
||||||
use cushy::widget::{RootBehavior, WidgetInstance};
|
use cushy::widget::{RootBehavior, WidgetInstance};
|
||||||
|
use easing_function::EasingFunction;
|
||||||
use figures::units::{Lp, Px, UPx};
|
use figures::units::{Lp, Px, UPx};
|
||||||
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
|
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size, Zero};
|
||||||
use intentional::Assert;
|
use intentional::Assert;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::animation::easings::EaseOutQuadradic;
|
|
||||||
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
use crate::animation::{AnimationHandle, AnimationTarget, IntoAnimate, Spawn, ZeroToOne};
|
||||||
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, Trackable};
|
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, Trackable};
|
||||||
|
use crate::styles::components::EasingIn;
|
||||||
use crate::value::{Destination, Dynamic, DynamicGuard, IntoValue, Source, Value};
|
use crate::value::{Destination, Dynamic, DynamicGuard, IntoValue, Source, Value};
|
||||||
use crate::widget::{
|
use crate::widget::{
|
||||||
Callback, MakeWidget, MountedChildren, MountedWidget, Widget, WidgetId, WidgetList, WidgetRef,
|
Callback, MakeWidget, MountedChildren, MountedWidget, Widget, WidgetId, WidgetList, WidgetRef,
|
||||||
|
|
@ -139,6 +140,7 @@ impl Widget for Layers {
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct OverlayLayer {
|
pub struct OverlayLayer {
|
||||||
state: Dynamic<OverlayState>,
|
state: Dynamic<OverlayState>,
|
||||||
|
easing: Dynamic<EasingFunction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverlayLayer {
|
impl OverlayLayer {
|
||||||
|
|
@ -189,6 +191,7 @@ impl OverlayLayer {
|
||||||
|
|
||||||
impl Widget for OverlayLayer {
|
impl Widget for OverlayLayer {
|
||||||
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
|
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
|
||||||
|
self.easing.set(context.get(&EasingIn));
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
|
|
||||||
for child in &state.overlays {
|
for child in &state.overlays {
|
||||||
|
|
@ -691,7 +694,7 @@ impl OverlayBuilder<'_> {
|
||||||
.opacity
|
.opacity
|
||||||
.transition_to(ZeroToOne::ONE)
|
.transition_to(ZeroToOne::ONE)
|
||||||
.over(Duration::from_millis(250))
|
.over(Duration::from_millis(250))
|
||||||
.with_easing(EaseOutQuadradic)
|
.with_easing(self.overlay.easing.get())
|
||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,17 @@
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use easing_function::EasingFunction;
|
||||||
use figures::units::Px;
|
use figures::units::Px;
|
||||||
use figures::{Angle, Point, Ranged, ScreenScale, Size, Zero};
|
use figures::{Angle, Point, Ranged, ScreenScale, Size, Zero};
|
||||||
use kludgine::shapes::{Path, StrokeOptions};
|
use kludgine::shapes::{Path, StrokeOptions};
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
use crate::animation::easings::{EaseInQuadradic, EaseOutQuadradic};
|
|
||||||
use crate::animation::{
|
use crate::animation::{
|
||||||
AnimationHandle, AnimationTarget, IntoAnimate, PercentBetween, Spawn, ZeroToOne,
|
AnimationHandle, AnimationTarget, IntoAnimate, PercentBetween, Spawn, ZeroToOne,
|
||||||
};
|
};
|
||||||
|
use crate::styles::components::{EasingIn, EasingOut};
|
||||||
|
use crate::styles::ContextFreeComponent;
|
||||||
use crate::value::{Destination, Dynamic, IntoReadOnly, IntoReader, MapEach, ReadOnly, Source};
|
use crate::value::{Destination, Dynamic, IntoReadOnly, IntoReader, MapEach, ReadOnly, Source};
|
||||||
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
|
use crate::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance};
|
||||||
use crate::widgets::slider::{InactiveTrackColor, Slidable, TrackColor, TrackSize};
|
use crate::widgets::slider::{InactiveTrackColor, Slidable, TrackColor, TrackSize};
|
||||||
|
|
@ -91,29 +93,41 @@ impl MakeWidgetWithTag for ProgressBar {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ease_in_probe = EasingIn.probe_wrapping(slider);
|
||||||
|
let ease_in = ease_in_probe.value().clone();
|
||||||
|
let ease_out_probe = EasingOut.probe_wrapping(ease_in_probe);
|
||||||
|
let ease_out = ease_out_probe.value().clone();
|
||||||
update_progress_bar(
|
update_progress_bar(
|
||||||
self.progress.get(),
|
self.progress.get(),
|
||||||
&mut indeterminant_animation,
|
&mut indeterminant_animation,
|
||||||
&start,
|
&start,
|
||||||
&end,
|
&end,
|
||||||
degree_offset.as_ref(),
|
degree_offset.as_ref(),
|
||||||
|
&ease_in,
|
||||||
|
&ease_out,
|
||||||
);
|
);
|
||||||
|
|
||||||
match self.progress {
|
match self.progress {
|
||||||
ReadOnly::Reader(progress) => {
|
ReadOnly::Reader(progress) => {
|
||||||
let callback = progress.for_each(move |progress| {
|
let callback = progress.for_each({
|
||||||
update_progress_bar(
|
let ease_in = ease_in.clone();
|
||||||
*progress,
|
let ease_out = ease_out.clone();
|
||||||
&mut indeterminant_animation,
|
move |progress| {
|
||||||
&start,
|
update_progress_bar(
|
||||||
&end,
|
*progress,
|
||||||
degree_offset.as_ref(),
|
&mut indeterminant_animation,
|
||||||
);
|
&start,
|
||||||
|
&end,
|
||||||
|
degree_offset.as_ref(),
|
||||||
|
&ease_in,
|
||||||
|
&ease_out,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
Data::new_wrapping((callback, progress), slider).make_widget()
|
Data::new_wrapping((callback, progress), ease_out_probe).make_widget()
|
||||||
}
|
}
|
||||||
ReadOnly::Constant(_) => {
|
ReadOnly::Constant(_) => {
|
||||||
Data::new_wrapping(indeterminant_animation, slider).make_widget()
|
Data::new_wrapping(indeterminant_animation, ease_out_probe).make_widget()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,10 +145,14 @@ fn update_progress_bar(
|
||||||
start: &Dynamic<ZeroToOne>,
|
start: &Dynamic<ZeroToOne>,
|
||||||
end: &Dynamic<ZeroToOne>,
|
end: &Dynamic<ZeroToOne>,
|
||||||
degree_offset: Option<&Dynamic<Angle>>,
|
degree_offset: Option<&Dynamic<Angle>>,
|
||||||
|
ease_in: &Dynamic<EasingFunction>,
|
||||||
|
ease_out: &Dynamic<EasingFunction>,
|
||||||
) {
|
) {
|
||||||
match progress {
|
match progress {
|
||||||
Progress::Indeterminant => {
|
Progress::Indeterminant => {
|
||||||
if indeterminant_animation.is_none() {
|
if indeterminant_animation.is_none() {
|
||||||
|
let ease_in = ease_in.get();
|
||||||
|
let ease_out = ease_out.get();
|
||||||
*indeterminant_animation = Some(IndeterminantAnimations {
|
*indeterminant_animation = Some(IndeterminantAnimations {
|
||||||
_primary: (
|
_primary: (
|
||||||
start
|
start
|
||||||
|
|
@ -145,25 +163,25 @@ fn update_progress_bar(
|
||||||
start
|
start
|
||||||
.transition_to(ZeroToOne::new(0.33))
|
.transition_to(ZeroToOne::new(0.33))
|
||||||
.over(Duration::from_millis(500))
|
.over(Duration::from_millis(500))
|
||||||
.with_easing(EaseInQuadradic),
|
.with_easing(ease_in.clone()),
|
||||||
)
|
)
|
||||||
.and_then(
|
.and_then(
|
||||||
start
|
start
|
||||||
.transition_to(ZeroToOne::new(1.0))
|
.transition_to(ZeroToOne::new(1.0))
|
||||||
.over(Duration::from_millis(500))
|
.over(Duration::from_millis(500))
|
||||||
.with_easing(EaseOutQuadradic),
|
.with_easing(ease_out.clone()),
|
||||||
),
|
),
|
||||||
end.transition_to(ZeroToOne::ZERO)
|
end.transition_to(ZeroToOne::ZERO)
|
||||||
.immediately()
|
.immediately()
|
||||||
.and_then(
|
.and_then(
|
||||||
end.transition_to(ZeroToOne::new(0.75))
|
end.transition_to(ZeroToOne::new(0.75))
|
||||||
.over(Duration::from_millis(500))
|
.over(Duration::from_millis(500))
|
||||||
.with_easing(EaseInQuadradic),
|
.with_easing(ease_in),
|
||||||
)
|
)
|
||||||
.and_then(
|
.and_then(
|
||||||
end.transition_to(ZeroToOne::ONE)
|
end.transition_to(ZeroToOne::ONE)
|
||||||
.over(Duration::from_millis(250))
|
.over(Duration::from_millis(250))
|
||||||
.with_easing(EaseOutQuadradic),
|
.with_easing(ease_out.clone()),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.cycle()
|
.cycle()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue