mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-03 07:04:34 +00:00
Implement easing curve in the runtime
This commit is contained in:
parent
feec73674f
commit
46a011683f
11 changed files with 93 additions and 15 deletions
|
@ -28,6 +28,7 @@ using internal::ItemTreeNode;
|
||||||
using ComponentRef = VRef<ComponentVTable>;
|
using ComponentRef = VRef<ComponentVTable>;
|
||||||
using ItemVisitorRefMut = VRefMut<internal::ItemVisitorVTable>;
|
using ItemVisitorRefMut = VRefMut<internal::ItemVisitorVTable>;
|
||||||
using internal::WindowProperties;
|
using internal::WindowProperties;
|
||||||
|
using internal::EasingCurve;
|
||||||
|
|
||||||
struct ComponentWindow
|
struct ComponentWindow
|
||||||
{
|
{
|
||||||
|
|
|
@ -166,6 +166,7 @@ fn to_js_value<'cx>(
|
||||||
}
|
}
|
||||||
Value::Color(c) => JsNumber::new(cx, c.as_argb_encoded()).as_value(cx),
|
Value::Color(c) => JsNumber::new(cx, c.as_argb_encoded()).as_value(cx),
|
||||||
Value::PathElements(_) => todo!(),
|
Value::PathElements(_) => todo!(),
|
||||||
|
Value::EasingCurve(_) => todo!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Spanned};
|
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Spanned};
|
||||||
use crate::expression_tree::{Expression, NamedReference, OperatorClass, Path};
|
use crate::expression_tree::{EasingCurve, Expression, NamedReference, OperatorClass, Path};
|
||||||
use crate::object_tree::{Component, ElementRc};
|
use crate::object_tree::{Component, ElementRc};
|
||||||
use crate::typeregister::Type;
|
use crate::typeregister::Type;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
|
@ -709,7 +709,12 @@ fn compile_expression(e: &Expression, component: &Rc<Component>) -> TokenStream
|
||||||
let name = quote::format_ident!("{}", name);
|
let name = quote::format_ident!("{}", name);
|
||||||
quote!(#name)
|
quote!(#name)
|
||||||
}
|
}
|
||||||
Expression::EasingCurve(_) => quote!(todo!("EasingCurve not yet implemented in rust.rs")),
|
Expression::EasingCurve(EasingCurve::Linear) => {
|
||||||
|
quote!(sixtyfps::re_exports::EasingCurve::Linear)
|
||||||
|
}
|
||||||
|
Expression::EasingCurve(EasingCurve::CubicBezier(a, b, c, d)) => {
|
||||||
|
quote!(sixtyfps::re_exports::EasingCurve::CubicBezier([#a, #b, #c, #d]))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -647,6 +647,23 @@ pub struct MouseEvent {
|
||||||
pub what: MouseEventType,
|
pub what: MouseEventType,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The representation of an easing curve, for animations
|
||||||
|
#[repr(C, u32)]
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum EasingCurve {
|
||||||
|
/// The linear curve
|
||||||
|
Linear,
|
||||||
|
/// A Cubic bezier curve, with its 4 parameter
|
||||||
|
CubicBezier([f32; 4]),
|
||||||
|
//Custom(Box<dyn Fn(f32) -> f32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EasingCurve {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Linear
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
/// WindowProperties is used to pass the references to properties of the instantiated
|
/// WindowProperties is used to pass the references to properties of the instantiated
|
||||||
|
|
|
@ -365,4 +365,6 @@ pub struct PropertyAnimation {
|
||||||
pub duration: i32,
|
pub duration: i32,
|
||||||
#[rtti_field]
|
#[rtti_field]
|
||||||
pub loop_count: i32,
|
pub loop_count: i32,
|
||||||
|
#[rtti_field]
|
||||||
|
pub easing: crate::abi::datastructures::EasingCurve,
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,7 +622,8 @@ impl<T: InterpolatedPropertyValue> PropertyValueAnimationData<T> {
|
||||||
}
|
}
|
||||||
let progress = time_progress as f32 / self.details.duration as f32;
|
let progress = time_progress as f32 / self.details.duration as f32;
|
||||||
assert!(progress <= 1.);
|
assert!(progress <= 1.);
|
||||||
let val = self.from_value.interpolate(self.to_value, progress);
|
let t = crate::animations::easing_curve(&self.details.easing, progress);
|
||||||
|
let val = self.from_value.interpolate(self.to_value, t);
|
||||||
(val, false)
|
(val, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -997,8 +998,10 @@ mod animation_tests {
|
||||||
fn properties_test_animation_triggered_by_set() {
|
fn properties_test_animation_triggered_by_set() {
|
||||||
let compo = Component::new_test_component();
|
let compo = Component::new_test_component();
|
||||||
|
|
||||||
let animation_details =
|
let animation_details = PropertyAnimation {
|
||||||
PropertyAnimation { duration: DURATION.as_millis() as _, loop_count: 0 };
|
duration: DURATION.as_millis() as _,
|
||||||
|
..PropertyAnimation::default()
|
||||||
|
};
|
||||||
|
|
||||||
compo.width.set(100);
|
compo.width.set(100);
|
||||||
assert_eq!(get_prop_value(&compo.width), 100);
|
assert_eq!(get_prop_value(&compo.width), 100);
|
||||||
|
@ -1036,8 +1039,10 @@ mod animation_tests {
|
||||||
let start_time =
|
let start_time =
|
||||||
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick());
|
crate::animations::CURRENT_ANIMATION_DRIVER.with(|driver| driver.current_tick());
|
||||||
|
|
||||||
let animation_details =
|
let animation_details = PropertyAnimation {
|
||||||
PropertyAnimation { duration: DURATION.as_millis() as _, loop_count: 0 };
|
duration: DURATION.as_millis() as _,
|
||||||
|
..PropertyAnimation::default()
|
||||||
|
};
|
||||||
|
|
||||||
let w = Rc::downgrade(&compo);
|
let w = Rc::downgrade(&compo);
|
||||||
compo.width.set_animated_binding(
|
compo.width.set_animated_binding(
|
||||||
|
@ -1073,8 +1078,11 @@ mod animation_tests {
|
||||||
fn test_loop() {
|
fn test_loop() {
|
||||||
let compo = Component::new_test_component();
|
let compo = Component::new_test_component();
|
||||||
|
|
||||||
let animation_details =
|
let animation_details = PropertyAnimation {
|
||||||
PropertyAnimation { duration: DURATION.as_millis() as _, loop_count: 2 };
|
duration: DURATION.as_millis() as _,
|
||||||
|
loop_count: 2,
|
||||||
|
..PropertyAnimation::default()
|
||||||
|
};
|
||||||
|
|
||||||
compo.width.set(100);
|
compo.width.set(100);
|
||||||
|
|
||||||
|
@ -1116,8 +1124,11 @@ mod animation_tests {
|
||||||
fn test_loop_overshoot() {
|
fn test_loop_overshoot() {
|
||||||
let compo = Component::new_test_component();
|
let compo = Component::new_test_component();
|
||||||
|
|
||||||
let animation_details =
|
let animation_details = PropertyAnimation {
|
||||||
PropertyAnimation { duration: DURATION.as_millis() as _, loop_count: 2 };
|
duration: DURATION.as_millis() as _,
|
||||||
|
loop_count: 2,
|
||||||
|
..PropertyAnimation::default()
|
||||||
|
};
|
||||||
|
|
||||||
compo.width.set(100);
|
compo.width.set(100);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
use crate::abi::datastructures::EasingCurve;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
/// The AnimationDriver
|
/// The AnimationDriver
|
||||||
|
@ -44,3 +45,27 @@ impl AnimationDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local!(pub(crate) static CURRENT_ANIMATION_DRIVER : AnimationDriver = AnimationDriver::default());
|
thread_local!(pub(crate) static CURRENT_ANIMATION_DRIVER : AnimationDriver = AnimationDriver::default());
|
||||||
|
|
||||||
|
/// map a value betwen 0 and 1 to another value between 0 and 1 according to the curve
|
||||||
|
pub fn easing_curve(curve: &EasingCurve, value: f32) -> f32 {
|
||||||
|
match curve {
|
||||||
|
EasingCurve::Linear => value,
|
||||||
|
EasingCurve::CubicBezier([a, b, c, d]) => {
|
||||||
|
if !(0.0..=1.0).contains(a) && !(0.0..=1.0).contains(c) {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
let curve = lyon::algorithms::geom::cubic_bezier::CubicBezierSegment {
|
||||||
|
from: (0., 0.).into(),
|
||||||
|
ctrl1: (*a, *b).into(),
|
||||||
|
ctrl2: (*c, *d).into(),
|
||||||
|
to: (1., 1.).into(),
|
||||||
|
};
|
||||||
|
let curve = curve.assume_monotonic();
|
||||||
|
curve.y(curve.solve_t_for_x(
|
||||||
|
value,
|
||||||
|
0.0..1.0,
|
||||||
|
lyon::tessellation::StrokeOptions::DEFAULT_TOLERANCE,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ fn main() {
|
||||||
"Slice",
|
"Slice",
|
||||||
"ComponentWindowOpaque",
|
"ComponentWindowOpaque",
|
||||||
"PropertyAnimation",
|
"PropertyAnimation",
|
||||||
|
"EasingCurve",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| x.to_string())
|
.map(|x| x.to_string())
|
||||||
|
@ -142,6 +143,11 @@ fn main() {
|
||||||
"CachedRenderingData".to_owned(),
|
"CachedRenderingData".to_owned(),
|
||||||
" constexpr CachedRenderingData() : cache_index{}, cache_ok{} {}".to_owned(),
|
" constexpr CachedRenderingData() : cache_index{}, cache_ok{} {}".to_owned(),
|
||||||
);
|
);
|
||||||
|
config.export.body.insert(
|
||||||
|
"EasingCurve".to_owned(),
|
||||||
|
" constexpr EasingCurve() : tag(Tag::Linear), cubic_bezier{{0,0,1,1}} {}
|
||||||
|
constexpr explicit EasingCurve(EasingCurve::Tag tag, float a, float b, float c, float d) : tag(tag), cubic_bezier{{a,b,c,d}} {}".into()
|
||||||
|
);
|
||||||
cbindgen::Builder::new()
|
cbindgen::Builder::new()
|
||||||
.with_config(config)
|
.with_config(config)
|
||||||
.with_src(crate_dir.join("abi/datastructures.rs"))
|
.with_src(crate_dir.join("abi/datastructures.rs"))
|
||||||
|
|
|
@ -23,7 +23,8 @@ declare_ValueType![
|
||||||
crate::SharedString,
|
crate::SharedString,
|
||||||
crate::Resource,
|
crate::Resource,
|
||||||
crate::Color,
|
crate::Color,
|
||||||
crate::PathData
|
crate::PathData,
|
||||||
|
crate::abi::datastructures::EasingCurve
|
||||||
];
|
];
|
||||||
|
|
||||||
pub trait PropertyInfo<Item, Value> {
|
pub trait PropertyInfo<Item, Value> {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use core::convert::{TryFrom, TryInto};
|
use core::convert::{TryFrom, TryInto};
|
||||||
use core::pin::Pin;
|
use core::pin::Pin;
|
||||||
use sixtyfps_compilerlib::expression_tree::{
|
use sixtyfps_compilerlib::expression_tree::{
|
||||||
Expression, ExpressionSpanned, NamedReference, Path as ExprPath, PathElement as ExprPathElement,
|
EasingCurve, Expression, ExpressionSpanned, NamedReference, Path as ExprPath,
|
||||||
|
PathElement as ExprPathElement,
|
||||||
};
|
};
|
||||||
use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
|
use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
|
||||||
use sixtyfps_corelib as corelib;
|
use sixtyfps_corelib as corelib;
|
||||||
|
@ -69,6 +70,8 @@ pub enum Value {
|
||||||
Color(Color),
|
Color(Color),
|
||||||
/// The elements of a path
|
/// The elements of a path
|
||||||
PathElements(PathData),
|
PathElements(PathData),
|
||||||
|
/// An easing curve
|
||||||
|
EasingCurve(corelib::abi::datastructures::EasingCurve),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Value {
|
impl Default for Value {
|
||||||
|
@ -114,6 +117,7 @@ declare_value_conversion!(Resource => [Resource] );
|
||||||
declare_value_conversion!(Object => [HashMap<String, Value>] );
|
declare_value_conversion!(Object => [HashMap<String, Value>] );
|
||||||
declare_value_conversion!(Color => [Color] );
|
declare_value_conversion!(Color => [Color] );
|
||||||
declare_value_conversion!(PathElements => [PathData]);
|
declare_value_conversion!(PathElements => [PathData]);
|
||||||
|
declare_value_conversion!(EasingCurve => [corelib::abi::datastructures::EasingCurve]);
|
||||||
|
|
||||||
/// The local variable needed for binding evaluation
|
/// The local variable needed for binding evaluation
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -348,7 +352,12 @@ pub fn eval_expression(
|
||||||
Expression::ReadLocalVariable { name, .. } => {
|
Expression::ReadLocalVariable { name, .. } => {
|
||||||
local_context.local_variables.get(name).unwrap().clone()
|
local_context.local_variables.get(name).unwrap().clone()
|
||||||
}
|
}
|
||||||
Expression::EasingCurve(_) => todo!("EasingCurve not yet implemented"),
|
Expression::EasingCurve(curve) => Value::EasingCurve(match curve {
|
||||||
|
EasingCurve::Linear => corelib::abi::datastructures::EasingCurve::Linear,
|
||||||
|
EasingCurve::CubicBezier(a, b, c, d) => {
|
||||||
|
corelib::abi::datastructures::EasingCurve::CubicBezier([*a, *b, *c, *d])
|
||||||
|
}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ export CheckBox := Rectangle {
|
||||||
y: 4lx;
|
y: 4lx;
|
||||||
x: root.checked ? 4lx : indicator.width - bubble.width - 4lx;
|
x: root.checked ? 4lx : indicator.width - bubble.width - 4lx;
|
||||||
color: root.checked ? #aea : #eaa;
|
color: root.checked ? #aea : #eaa;
|
||||||
animate x, color { duration: 200ms; }
|
animate x, color { duration: 200ms; easing: ease;}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue