Implemented slerp.
This commit is contained in:
parent
7432a7c058
commit
703c845634
|
@ -35,11 +35,7 @@ fn app_main() {
|
|||
transformed("Skew-X 15º", skew_x(15.deg())),
|
||||
transformed("Scale 130%", scale(130.pct())),
|
||||
transformed("Identity", Transform::identity()),
|
||||
transformed("Rotate Lerp", rotate(350.deg())),
|
||||
Container! {
|
||||
rotate_transition_mode = RotateTransitionMode::Slerp;
|
||||
child = transformed("Rotate Slerp", rotate(350.deg()));
|
||||
}
|
||||
|
||||
];
|
||||
},
|
||||
Stack! {
|
||||
|
@ -278,8 +274,6 @@ fn cube() -> impl UiNode {
|
|||
}.boxed())
|
||||
.collect::<UiNodeVec>();
|
||||
|
||||
rotate_transition_mode = RotateTransitionMode::Slerp;
|
||||
#[easing(1.secs())]
|
||||
transform = show.map(|&i| match i {
|
||||
1 => rotate_y(0.deg()),
|
||||
2 => rotate_y(-90.deg()),
|
||||
|
@ -288,7 +282,9 @@ fn cube() -> impl UiNode {
|
|||
5 => rotate_x(-90.deg()),
|
||||
6 => rotate_x(90.deg()),
|
||||
_ => unreachable!(),
|
||||
}.translate_z(-100))
|
||||
}
|
||||
.translate_z(-100))
|
||||
.easing_with(1.secs(), easing::linear, units::slerp_sampler)
|
||||
}
|
||||
},
|
||||
Wrap! {
|
||||
|
|
|
@ -2,8 +2,8 @@ use derive_more as dm;
|
|||
|
||||
use super::{about_eq, euclid, EasingStep, Factor, EPSILON, EPSILON_100};
|
||||
use crate::{
|
||||
impl_from_and_into_var,
|
||||
var::{animation::Transitionable, context_var, Var},
|
||||
context_local, impl_from_and_into_var,
|
||||
var::animation::{Transition, Transitionable},
|
||||
};
|
||||
|
||||
use std::{
|
||||
|
@ -11,46 +11,27 @@ use std::{
|
|||
fmt,
|
||||
};
|
||||
|
||||
/// Defines how rotation animations compute the change between angles.
|
||||
/// Spherical linear interpolation sampler.
|
||||
///
|
||||
/// The angle units and transform use the [`ROTATE_TRANSITION_MODE_VAR`] to select the mode, in the
|
||||
/// main crate the `rotate_transition_mode` property can be used to set the mode.
|
||||
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub enum RotateTransitionMode {
|
||||
/// Linear interpolation.
|
||||
///
|
||||
/// Always animates over the full numeric range between angles. This mode is required for animations that
|
||||
/// iterate over multiple rotation turns.
|
||||
///
|
||||
/// A transition from 358º to 1º iterates over almost a full counterclockwise turn to reach the final value.
|
||||
///
|
||||
/// This is the default mode.
|
||||
#[default]
|
||||
Lerp,
|
||||
/// Spherical linear interpolation.
|
||||
///
|
||||
/// Always animates over the shortest distance between angles by modulo wrapping. This mode is recommended
|
||||
/// for animations that
|
||||
///
|
||||
/// A transition from 358º to 1º goes directly to 361º (modulo normalized to 1º).
|
||||
Slerp,
|
||||
/// Animates rotations over the shortest change between angles by modulo wrapping.
|
||||
/// A transition from 358º to 1º goes directly to 361º (modulo normalized to 1º).
|
||||
///
|
||||
/// Types that support this use the [`is_slerp_enabled`] function inside [`Transitionable::lerp`] to change
|
||||
/// mode, types that don't support this use the normal linear interpolation. All angle and transform units
|
||||
/// implement this.
|
||||
pub fn slerp_sampler<T: Transitionable>(t: &Transition<T>, step: super::EasingStep) -> T {
|
||||
SLERP_ENABLED.with_context_value(true, || t.sample(step))
|
||||
}
|
||||
|
||||
impl fmt::Debug for RotateTransitionMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
write!(f, "RotateTransitionMode::")?;
|
||||
}
|
||||
match self {
|
||||
Self::Lerp => write!(f, "Lerp"),
|
||||
Self::Slerp => write!(f, "Slerp"),
|
||||
}
|
||||
}
|
||||
/// Gets if slerp mode is enabled in the context.
|
||||
///
|
||||
/// See [`slerp_sampler`] for more details.
|
||||
pub fn is_slerp_enabled() -> bool {
|
||||
SLERP_ENABLED.get_clone()
|
||||
}
|
||||
|
||||
context_var! {
|
||||
/// Defines the rotation animation mode for angle units and transform.
|
||||
pub static ROTATE_TRANSITION_MODE_VAR: RotateTransitionMode = RotateTransitionMode::default();
|
||||
context_local! {
|
||||
static SLERP_ENABLED: bool = false;
|
||||
}
|
||||
|
||||
/// Angle in radians.
|
||||
|
@ -95,9 +76,9 @@ impl AngleRadian {
|
|||
}
|
||||
impl Transitionable for AngleRadian {
|
||||
fn lerp(self, to: &Self, step: EasingStep) -> Self {
|
||||
match ROTATE_TRANSITION_MODE_VAR.get() {
|
||||
RotateTransitionMode::Lerp => self.lerp(*to, step),
|
||||
RotateTransitionMode::Slerp => self.slerp(*to, step),
|
||||
match is_slerp_enabled() {
|
||||
false => self.lerp(*to, step),
|
||||
true => self.slerp(*to, step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,9 +151,9 @@ impl AngleGradian {
|
|||
}
|
||||
impl Transitionable for AngleGradian {
|
||||
fn lerp(self, to: &Self, step: EasingStep) -> Self {
|
||||
match ROTATE_TRANSITION_MODE_VAR.get() {
|
||||
RotateTransitionMode::Lerp => self.lerp(*to, step),
|
||||
RotateTransitionMode::Slerp => self.slerp(*to, step),
|
||||
match is_slerp_enabled() {
|
||||
false => self.lerp(*to, step),
|
||||
true => self.slerp(*to, step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,9 +226,9 @@ impl AngleDegree {
|
|||
}
|
||||
impl Transitionable for AngleDegree {
|
||||
fn lerp(self, to: &Self, step: EasingStep) -> Self {
|
||||
match ROTATE_TRANSITION_MODE_VAR.get() {
|
||||
RotateTransitionMode::Lerp => self.lerp(*to, step),
|
||||
RotateTransitionMode::Slerp => self.slerp(*to, step),
|
||||
match is_slerp_enabled() {
|
||||
false => self.lerp(*to, step),
|
||||
true => self.slerp(*to, step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,9 +301,9 @@ impl AngleTurn {
|
|||
}
|
||||
impl Transitionable for AngleTurn {
|
||||
fn lerp(self, to: &Self, step: EasingStep) -> Self {
|
||||
match ROTATE_TRANSITION_MODE_VAR.get() {
|
||||
RotateTransitionMode::Lerp => self.lerp(*to, step),
|
||||
RotateTransitionMode::Slerp => self.slerp(*to, step),
|
||||
match is_slerp_enabled() {
|
||||
false => self.lerp(*to, step),
|
||||
true => self.slerp(*to, step),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
use zero_ui_view_api::{units::Px, webrender_api::euclid};
|
||||
|
||||
use crate::{
|
||||
impl_from_and_into_var,
|
||||
var::{animation::Transitionable, Var},
|
||||
};
|
||||
use crate::{impl_from_and_into_var, var::animation::Transitionable};
|
||||
|
||||
use super::{
|
||||
AngleRadian, AngleUnits, EasingStep, Factor, FactorUnits, Layout1d, Length, PxTransform, RotateTransitionMode,
|
||||
ROTATE_TRANSITION_MODE_VAR,
|
||||
};
|
||||
use super::{is_slerp_enabled, AngleRadian, AngleUnits, EasingStep, Factor, FactorUnits, Layout1d, Length, PxTransform};
|
||||
|
||||
/// A transform builder type.
|
||||
///
|
||||
|
@ -25,8 +19,6 @@ use super::{
|
|||
/// # use zero_ui_core::units::*;
|
||||
/// let rotate_then_move = rotate(10.deg()).translate(50, 30);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
#[derive(Clone, Default, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct Transform {
|
||||
parts: Vec<TransformPart>,
|
||||
|
@ -661,9 +653,9 @@ impl MatrixDecomposed3D {
|
|||
}
|
||||
|
||||
pub fn lerp_quaternion(from: Quaternion, to: Quaternion, step: EasingStep) -> Quaternion {
|
||||
match ROTATE_TRANSITION_MODE_VAR.get() {
|
||||
RotateTransitionMode::Lerp => from.lerp(&to, step.0 as _),
|
||||
RotateTransitionMode::Slerp => from.slerp(&to, step.0 as _),
|
||||
match is_slerp_enabled() {
|
||||
false => from.lerp(&to, step.0 as _),
|
||||
true => from.slerp(&to, step.0 as _),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1941,12 +1941,36 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
E: Into<T>,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
{
|
||||
self.animate(animation::var_set_ease(
|
||||
self.set_ease_with(start_value, end_value, duration, easing, animation::Transition::sample)
|
||||
}
|
||||
|
||||
/// Schedule an easing transition from the `start_value` to `end_value` using a custom value sampler.
|
||||
///
|
||||
/// The variable updates every time the [`EasingStep`] for each frame changes and a different value is sampled.
|
||||
///
|
||||
/// See [`Var::animate`] for details about animations.
|
||||
fn set_ease_with<S, E, F, Sa>(
|
||||
&self,
|
||||
start_value: S,
|
||||
end_value: E,
|
||||
duration: Duration,
|
||||
easing: F,
|
||||
sampler: Sa,
|
||||
) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
S: Into<T>,
|
||||
E: Into<T>,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
Sa: Fn(&animation::Transition<T>, EasingStep) -> T + Send + 'static,
|
||||
{
|
||||
self.animate(animation::var_set_ease_with(
|
||||
start_value.into(),
|
||||
end_value.into(),
|
||||
duration,
|
||||
easing,
|
||||
999.fct(),
|
||||
sampler,
|
||||
))
|
||||
}
|
||||
|
||||
|
@ -1961,7 +1985,29 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
E: Into<T>,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
{
|
||||
self.animate(animation::var_set_ease(self.get(), new_value.into(), duration, easing, 0.fct()))
|
||||
self.ease_with(new_value, duration, easing, animation::Transition::sample)
|
||||
}
|
||||
|
||||
/// Schedule an easing transition from the current value to `new_value` using a custom value sampler.
|
||||
///
|
||||
/// The variable updates every time the [`EasingStep`] for each frame changes and a different value is sampled.
|
||||
///
|
||||
/// See [`Var::animate`] for details about animations.
|
||||
fn ease_with<E, F, S>(&self, new_value: E, duration: Duration, easing: F, sampler: S) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
E: Into<T>,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
S: Fn(&animation::Transition<T>, EasingStep) -> T + Send + 'static,
|
||||
{
|
||||
self.animate(animation::var_set_ease_with(
|
||||
self.get(),
|
||||
new_value.into(),
|
||||
duration,
|
||||
easing,
|
||||
0.fct(),
|
||||
sampler,
|
||||
))
|
||||
}
|
||||
|
||||
/// Schedule a keyframed transition animation for the variable, starting from the first key.
|
||||
|
@ -1972,11 +2018,24 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
fn set_ease_keyed<F>(&self, keys: Vec<(Factor, T)>, duration: Duration, easing: F) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
{
|
||||
self.set_ease_keyed_with(keys, duration, easing, animation::TransitionKeyed::sample)
|
||||
}
|
||||
|
||||
/// Schedule a keyframed transition animation for the variable, starting from the first key, using a custom value sampler.
|
||||
///
|
||||
/// The variable will be set to to the first keyframe, then animated across all other keys.
|
||||
///
|
||||
/// See [`Var::animate`] for details about animations.
|
||||
fn set_ease_keyed_with<F, S>(&self, keys: Vec<(Factor, T)>, duration: Duration, easing: F, sampler: S) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
S: Fn(&animation::TransitionKeyed<T>, EasingStep) -> T + Send + 'static,
|
||||
{
|
||||
if let Some(transition) = animation::TransitionKeyed::new(keys) {
|
||||
self.animate(animation::var_set_ease_keyed(transition, duration, easing, 999.fct()))
|
||||
self.animate(animation::var_set_ease_keyed_with(transition, duration, easing, 999.fct(), sampler))
|
||||
} else {
|
||||
animation::AnimationHandle::dummy()
|
||||
}
|
||||
|
@ -1987,16 +2046,29 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
/// The variable will be set to to the first keyframe, then animated across all other keys.
|
||||
///
|
||||
/// See [`Var::animate`] for details about animations.
|
||||
fn ease_keyed<F>(&self, mut keys: Vec<(Factor, T)>, duration: Duration, easing: F) -> animation::AnimationHandle
|
||||
fn ease_keyed<F>(&self, keys: Vec<(Factor, T)>, duration: Duration, easing: F) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
{
|
||||
self.ease_keyed_with(keys, duration, easing, animation::TransitionKeyed::sample)
|
||||
}
|
||||
|
||||
/// Schedule a keyframed transition animation for the variable, starting from the current value, using a custom value sampler.
|
||||
///
|
||||
/// The variable will be set to to the first keyframe, then animated across all other keys.
|
||||
///
|
||||
/// See [`Var::animate`] for details about animations.
|
||||
fn ease_keyed_with<F, S>(&self, mut keys: Vec<(Factor, T)>, duration: Duration, easing: F, sampler: S) -> animation::AnimationHandle
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
S: Fn(&animation::TransitionKeyed<T>, EasingStep) -> T + Send + 'static,
|
||||
{
|
||||
keys.insert(0, (0.fct(), self.get()));
|
||||
|
||||
let transition = animation::TransitionKeyed::new(keys).unwrap();
|
||||
self.animate(animation::var_set_ease_keyed(transition, duration, easing, 0.fct()))
|
||||
self.animate(animation::var_set_ease_keyed_with(transition, duration, easing, 0.fct(), sampler))
|
||||
}
|
||||
|
||||
/// Set the variable to `new_value` after a `delay`.
|
||||
|
@ -2072,8 +2144,8 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
/// Create a vars that [`ease`] to each new value of `self`.
|
||||
///
|
||||
/// Note that the mapping var is [contextualized], meaning the binding will initialize in the fist usage context, not
|
||||
/// the creation context, so `property = CONTEXT_VAR.easing(500.ms(), easing::linear);` will bind with the `CONTEXT_VAR` in the `property` context,
|
||||
/// not the property instantiation.
|
||||
/// the creation context, so `property = CONTEXT_VAR.easing(500.ms(), easing::linear);` will bind with the `CONTEXT_VAR`
|
||||
/// in the `property` context, not the property instantiation.
|
||||
///
|
||||
/// [contextualized]: types::ContextualizedVar
|
||||
/// [`ease`]: Var::ease
|
||||
|
@ -2101,6 +2173,43 @@ pub trait Var<T: VarValue>: IntoVar<T, Var = Self> + AnyVar + Clone {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Create a vars that [`ease_with`] to each new value of `self`.
|
||||
///
|
||||
/// Note that the mapping var is [contextualized], meaning the binding will initialize in the fist usage context, not
|
||||
/// the creation context, so `property = CONTEXT_VAR.easing(500.ms(), easing::linear);` will bind with the `CONTEXT_VAR`
|
||||
/// in the `property` context, not the property instantiation.
|
||||
///
|
||||
/// [contextualized]: types::ContextualizedVar
|
||||
/// [`ease_with`]: Var::ease_with
|
||||
fn easing_with<F, S>(&self, duration: Duration, easing: F, sampler: S) -> types::ContextualizedVar<T, ReadOnlyArcVar<T>>
|
||||
where
|
||||
T: animation::Transitionable,
|
||||
F: Fn(EasingTime) -> EasingStep + Send + Sync + 'static,
|
||||
S: Fn(&animation::Transition<T>, EasingStep) -> T + Send + Sync + 'static,
|
||||
{
|
||||
let source = self.clone();
|
||||
let fns = Arc::new((easing, sampler));
|
||||
types::ContextualizedVar::new(Arc::new(move || {
|
||||
let easing_var = var(source.get());
|
||||
|
||||
let fns = fns.clone();
|
||||
let mut _anim_handle = animation::AnimationHandle::dummy();
|
||||
var_bind(&source, &easing_var, move |value, args, easing_var| {
|
||||
_anim_handle = easing_var.ease_with(
|
||||
value.clone(),
|
||||
duration,
|
||||
clmv!(fns, |t| (fns.0)(t)),
|
||||
clmv!(fns, |t, s| (fns.1)(t, s)),
|
||||
);
|
||||
if args.update {
|
||||
easing_var.update();
|
||||
}
|
||||
})
|
||||
.perm();
|
||||
easing_var.read_only()
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns a wrapper that implements [`fmt::Debug`] to write the var value.
|
||||
fn debug(&self) -> types::VarDebug<T, Self> {
|
||||
types::VarDebug {
|
||||
|
|
|
@ -17,11 +17,12 @@ pub mod easing;
|
|||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// The attribute takes two arguments that match the [`Var::easing`] parameters, a duration and easing function. The easing
|
||||
/// function can be omitted, if not present the [`easing::linear`] is used.
|
||||
/// The attribute takes one required argument and one optional that matches the [`Var::easing`]
|
||||
/// parameters. The required first arg is the duration, the second arg is an easing function, if not present the [`easing::linear`] is used.
|
||||
///
|
||||
/// Some items are auto-imported in the arguments cope, the [`TimeUnits`] and a block import for the [`easing::*`] functions, this
|
||||
/// means you can just name.
|
||||
/// Some items are auto-imported in each argument scope, the [`TimeUnits`] are imported in the first argument, so you can use syntax
|
||||
/// like `300.ms()` to declare the duration, all of the [`easing::*`] functions are imported in the second argument so you can use
|
||||
/// the function names directly.
|
||||
///
|
||||
/// ## Unset
|
||||
///
|
||||
|
@ -417,6 +418,11 @@ where
|
|||
Some(TransitionKeyed { keys })
|
||||
}
|
||||
|
||||
/// Keyed values.
|
||||
pub fn keys(&self) -> &[(Factor, T)] {
|
||||
&self.keys
|
||||
}
|
||||
|
||||
/// Compute the transition value at the `step`.
|
||||
pub fn sample(&self, step: EasingStep) -> T {
|
||||
if let Some(i) = self.keys.iter().position(|(f, _)| *f > step) {
|
||||
|
@ -747,12 +753,13 @@ pub(super) fn var_sequence<T: VarValue, V: Var<T>>(
|
|||
VarHandle::dummy()
|
||||
}
|
||||
|
||||
pub(super) fn var_set_ease<T>(
|
||||
pub(super) fn var_set_ease_with<T>(
|
||||
start_value: T,
|
||||
end_value: T,
|
||||
duration: Duration,
|
||||
easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
init_step: EasingStep, // set to 0 skips first frame, set to 999 includes first frame.
|
||||
sampler: impl Fn(&Transition<T>, EasingStep) -> T + Send + 'static,
|
||||
) -> impl FnMut(&Animation, &mut VarModify<T>) + Send
|
||||
where
|
||||
T: VarValue + Transitionable,
|
||||
|
@ -763,17 +770,18 @@ where
|
|||
let step = easing(a.elapsed_stop(duration));
|
||||
|
||||
if prev_step != step {
|
||||
vm.set(transition.sample(step));
|
||||
vm.set(sampler(&transition, step));
|
||||
prev_step = step;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn var_set_ease_keyed<T>(
|
||||
pub(super) fn var_set_ease_keyed_with<T>(
|
||||
transition: TransitionKeyed<T>,
|
||||
duration: Duration,
|
||||
easing: impl Fn(EasingTime) -> EasingStep + Send + 'static,
|
||||
init_step: EasingStep,
|
||||
sampler: impl Fn(&TransitionKeyed<T>, EasingStep) -> T + Send + 'static,
|
||||
) -> impl FnMut(&Animation, &mut VarModify<T>) + Send
|
||||
where
|
||||
T: VarValue + Transitionable,
|
||||
|
@ -783,7 +791,7 @@ where
|
|||
let step = easing(a.elapsed_stop(duration));
|
||||
|
||||
if prev_step != step {
|
||||
value.set(transition.sample(step));
|
||||
value.set(sampler(&transition, step));
|
||||
prev_step = step;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -144,8 +144,8 @@ pub mod prelude {
|
|||
units::{
|
||||
self, rotate, rotate_x, rotate_y, scale, scale_x, scale_xy, scale_y, skew, skew_x, skew_y, translate, translate_3d,
|
||||
translate_x, translate_y, translate_z, Align, AngleUnits, ByteUnits, EasingStep, EasingTime, FactorUnits, Length, LengthUnits,
|
||||
Line, LineFromTuplesBuilder, LineHeight, Point, Px, PxPoint, PxSize, Rect, RectFromTuplesBuilder, RotateTransitionMode,
|
||||
SideOffsets, Size, TimeUnits, Transform, Vector,
|
||||
Line, LineFromTuplesBuilder, LineHeight, Point, Px, PxPoint, PxSize, Rect, RectFromTuplesBuilder, SideOffsets, Size, TimeUnits,
|
||||
Transform, Vector,
|
||||
},
|
||||
var::{
|
||||
animation::{self, easing},
|
||||
|
|
|
@ -323,12 +323,6 @@ pub fn backface_visibility(child: impl UiNode, visible: impl IntoVar<bool>) -> i
|
|||
})
|
||||
}
|
||||
|
||||
/// Defines how rotation animations transition between angles.
|
||||
#[property(CONTEXT, default(ROTATE_TRANSITION_MODE_VAR))]
|
||||
pub fn rotate_transition_mode(child: impl UiNode, mode: impl IntoVar<RotateTransitionMode>) -> impl UiNode {
|
||||
with_context_var(child, ROTATE_TRANSITION_MODE_VAR, mode)
|
||||
}
|
||||
|
||||
context_var! {
|
||||
/// Point relative to the widget inner bounds around which the [`transform`] is applied.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue