feat: use lyon for easing
This commit is contained in:
parent
cdfb8b3068
commit
2ebc923197
7 changed files with 214 additions and 116 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "progress_indicators"
|
name = "loading_spinners"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["Nick Senger <dev@nsenger.com>"]
|
authors = ["Nick Senger <dev@nsenger.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
@ -10,4 +10,5 @@ flo_curves = "0.7"
|
||||||
iced = { path = "../..", features = ["canvas"] }
|
iced = { path = "../..", features = ["canvas"] }
|
||||||
iced_core = { path = "../../core" }
|
iced_core = { path = "../../core" }
|
||||||
iced_widget = { path = "../../widget" }
|
iced_widget = { path = "../../widget" }
|
||||||
lazy_static = "1.4"
|
once_cell = "1"
|
||||||
|
lyon = "1"
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
## Progress indicators
|
## Loading Spinners
|
||||||
|
|
||||||
Example implementation of animated indeterminate progress indicators.
|
Example implementation of animated indeterminate loading spinners.
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://gfycat.com/importantdevotedhammerheadbird">
|
<a href="https://gfycat.com/importantdevotedhammerheadbird">
|
||||||
|
|
@ -10,5 +10,5 @@ Example implementation of animated indeterminate progress indicators.
|
||||||
|
|
||||||
You can run it with `cargo run`:
|
You can run it with `cargo run`:
|
||||||
```
|
```
|
||||||
cargo run --package progress_indicators
|
cargo run --package loading_spinners
|
||||||
```
|
```
|
||||||
|
|
@ -14,7 +14,6 @@ use iced_core::{
|
||||||
|
|
||||||
use super::easing::{self, Easing};
|
use super::easing::{self, Easing};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
|
@ -22,7 +21,7 @@ type R<Theme> = iced_widget::renderer::Renderer<Theme>;
|
||||||
|
|
||||||
const MIN_RADIANS: f32 = PI / 8.0;
|
const MIN_RADIANS: f32 = PI / 8.0;
|
||||||
const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0;
|
const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0;
|
||||||
const PROCESSION_VELOCITY: u32 = u32::MAX / 120;
|
const BASE_ROTATION_SPEED: u32 = u32::MAX / 80;
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
pub struct Circular<'a, Theme>
|
pub struct Circular<'a, Theme>
|
||||||
|
|
@ -32,8 +31,9 @@ where
|
||||||
size: f32,
|
size: f32,
|
||||||
bar_height: f32,
|
bar_height: f32,
|
||||||
style: <Theme as StyleSheet>::Style,
|
style: <Theme as StyleSheet>::Style,
|
||||||
easing: Cow<'a, Easing>,
|
easing: &'a Easing,
|
||||||
cycle_duration: Duration,
|
cycle_duration: Duration,
|
||||||
|
rotation_speed: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Theme> Circular<'a, Theme>
|
impl<'a, Theme> Circular<'a, Theme>
|
||||||
|
|
@ -46,8 +46,9 @@ where
|
||||||
size: 40.0,
|
size: 40.0,
|
||||||
bar_height: 4.0,
|
bar_height: 4.0,
|
||||||
style: <Theme as StyleSheet>::Style::default(),
|
style: <Theme as StyleSheet>::Style::default(),
|
||||||
easing: Cow::Borrowed(&easing::STANDARD),
|
easing: &easing::STANDARD,
|
||||||
cycle_duration: Duration::from_millis(600),
|
cycle_duration: Duration::from_millis(600),
|
||||||
|
rotation_speed: BASE_ROTATION_SPEED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,8 +71,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the easing of this [`Circular`].
|
/// Sets the easing of this [`Circular`].
|
||||||
pub fn easing(mut self, easing: impl Into<Cow<'a, Easing>>) -> Self {
|
pub fn easing(mut self, easing: &'a Easing) -> Self {
|
||||||
self.easing = easing.into();
|
self.easing = easing;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +81,14 @@ where
|
||||||
self.cycle_duration = duration / 2;
|
self.cycle_duration = duration / 2;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the rotation speed of this [`Circular`]. Must be set to between 0.0 and 10.0.
|
||||||
|
/// Defaults to 1.0.
|
||||||
|
pub fn rotation_speed(mut self, speed: f32) -> Self {
|
||||||
|
let multiplier = speed.min(10.0).max(0.0);
|
||||||
|
self.rotation_speed = (BASE_ROTATION_SPEED as f32 * multiplier) as u32;
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Theme> Default for Circular<'a, Theme>
|
impl<'a, Theme> Default for Circular<'a, Theme>
|
||||||
|
|
@ -121,13 +130,13 @@ impl State {
|
||||||
Self::Expanding { procession, .. } => Self::Contracting {
|
Self::Expanding { procession, .. } => Self::Contracting {
|
||||||
start: now,
|
start: now,
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
procession: procession.wrapping_add(PROCESSION_VELOCITY),
|
procession: procession.wrapping_add(BASE_ROTATION_SPEED),
|
||||||
},
|
},
|
||||||
Self::Contracting { procession, .. } => Self::Expanding {
|
Self::Contracting { procession, .. } => Self::Expanding {
|
||||||
start: now,
|
start: now,
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
procession: procession.wrapping_add(
|
procession: procession.wrapping_add(
|
||||||
PROCESSION_VELOCITY.wrapping_add(
|
BASE_ROTATION_SPEED.wrapping_add(
|
||||||
((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32,
|
((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -143,18 +152,24 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self {
|
fn timed_transition(
|
||||||
|
&self,
|
||||||
|
cycle_duration: Duration,
|
||||||
|
rotation_speed: u32,
|
||||||
|
now: Instant,
|
||||||
|
) -> Self {
|
||||||
let elapsed = now.duration_since(self.start());
|
let elapsed = now.duration_since(self.start());
|
||||||
|
|
||||||
match elapsed {
|
match elapsed {
|
||||||
elapsed if elapsed > cycle_duration => self.next(now),
|
elapsed if elapsed > cycle_duration => self.next(now),
|
||||||
_ => self.with_elapsed(cycle_duration, elapsed),
|
_ => self.with_elapsed(cycle_duration, rotation_speed, elapsed),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_elapsed(
|
fn with_elapsed(
|
||||||
&self,
|
&self,
|
||||||
cycle_duration: Duration,
|
cycle_duration: Duration,
|
||||||
|
rotation_speed: u32,
|
||||||
elapsed: Duration,
|
elapsed: Duration,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32();
|
let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32();
|
||||||
|
|
@ -164,14 +179,14 @@ impl State {
|
||||||
} => Self::Expanding {
|
} => Self::Expanding {
|
||||||
start: *start,
|
start: *start,
|
||||||
progress,
|
progress,
|
||||||
procession: procession.wrapping_add(PROCESSION_VELOCITY),
|
procession: procession.wrapping_add(rotation_speed),
|
||||||
},
|
},
|
||||||
Self::Contracting {
|
Self::Contracting {
|
||||||
start, procession, ..
|
start, procession, ..
|
||||||
} => Self::Contracting {
|
} => Self::Contracting {
|
||||||
start: *start,
|
start: *start,
|
||||||
progress,
|
progress,
|
||||||
procession: procession.wrapping_add(PROCESSION_VELOCITY),
|
procession: procession.wrapping_add(rotation_speed),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +246,11 @@ where
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
if let Event::Window(window::Event::RedrawRequested(now)) = event {
|
if let Event::Window(window::Event::RedrawRequested(now)) = event {
|
||||||
*state = state.timed_transition(self.cycle_duration, now);
|
*state = state.timed_transition(
|
||||||
|
self.cycle_duration,
|
||||||
|
self.rotation_speed,
|
||||||
|
now,
|
||||||
|
);
|
||||||
|
|
||||||
shell.request_redraw(RedrawRequest::NextFrame);
|
shell.request_redraw(RedrawRequest::NextFrame);
|
||||||
}
|
}
|
||||||
|
|
@ -263,7 +282,7 @@ where
|
||||||
state,
|
state,
|
||||||
style: &self.style,
|
style: &self.style,
|
||||||
bar_height: self.bar_height,
|
bar_height: self.bar_height,
|
||||||
easing: self.easing.as_ref(),
|
easing: self.easing,
|
||||||
},
|
},
|
||||||
&(),
|
&(),
|
||||||
renderer,
|
renderer,
|
||||||
132
examples/loading_spinners/src/easing.rs
Normal file
132
examples/loading_spinners/src/easing.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
use iced_core::Point;
|
||||||
|
use lyon::algorithms::measure::PathMeasurements;
|
||||||
|
use lyon::path::builder::NoAttributes;
|
||||||
|
use lyon::path::path::BuilderImpl;
|
||||||
|
use lyon::path::Path;
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
pub static EMPHASIZED: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.05, 0.0], [0.133333, 0.06], [0.166666, 0.4])
|
||||||
|
.cubic_bezier_to([0.208333, 0.82], [0.25, 1.0], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static EMPHASIZED_DECELERATE: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.05, 0.7], [0.1, 1.0], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static EMPHASIZED_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.3, 0.0], [0.8, 0.15], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static STANDARD: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.2, 0.0], [0.0, 1.0], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static STANDARD_DECELERATE: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.0, 0.0], [0.0, 1.0], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub static STANDARD_ACCELERATE: Lazy<Easing> = Lazy::new(|| {
|
||||||
|
Easing::builder()
|
||||||
|
.cubic_bezier_to([0.3, 0.0], [1.0, 1.0], [1.0, 1.0])
|
||||||
|
.build()
|
||||||
|
});
|
||||||
|
|
||||||
|
pub struct Easing {
|
||||||
|
path: Path,
|
||||||
|
measurements: PathMeasurements,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Easing {
|
||||||
|
pub fn builder() -> Builder {
|
||||||
|
Builder::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn y_at_x(&self, x: f32) -> f32 {
|
||||||
|
let mut sampler = self.measurements.create_sampler(
|
||||||
|
&self.path,
|
||||||
|
lyon::algorithms::measure::SampleType::Normalized,
|
||||||
|
);
|
||||||
|
let sample = sampler.sample(x);
|
||||||
|
|
||||||
|
sample.position().y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Builder(NoAttributes<BuilderImpl>);
|
||||||
|
|
||||||
|
impl Builder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut builder = Path::builder();
|
||||||
|
builder.begin(lyon::geom::point(0.0, 0.0));
|
||||||
|
|
||||||
|
Self(builder)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a line segment. Points must be between 0,0 and 1,1
|
||||||
|
pub fn line_to(mut self, to: impl Into<Point>) -> Self {
|
||||||
|
self.0.line_to(Self::point(to));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a quadratic bézier curve. Points must be between 0,0 and 1,1
|
||||||
|
pub fn quadratic_bezier_to(
|
||||||
|
mut self,
|
||||||
|
ctrl: impl Into<Point>,
|
||||||
|
to: impl Into<Point>,
|
||||||
|
) -> Self {
|
||||||
|
self.0
|
||||||
|
.quadratic_bezier_to(Self::point(ctrl), Self::point(to));
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a cubic bézier curve. Points must be between 0,0 and 1,1
|
||||||
|
pub fn cubic_bezier_to(
|
||||||
|
mut self,
|
||||||
|
ctrl1: impl Into<Point>,
|
||||||
|
ctrl2: impl Into<Point>,
|
||||||
|
to: impl Into<Point>,
|
||||||
|
) -> Self {
|
||||||
|
self.0.cubic_bezier_to(
|
||||||
|
Self::point(ctrl1),
|
||||||
|
Self::point(ctrl2),
|
||||||
|
Self::point(to),
|
||||||
|
);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(mut self) -> Easing {
|
||||||
|
self.0.line_to(lyon::geom::point(1.0, 1.0));
|
||||||
|
self.0.end(false);
|
||||||
|
|
||||||
|
let path = self.0.build();
|
||||||
|
let measurements = PathMeasurements::from_path(&path, 0.0);
|
||||||
|
|
||||||
|
Easing { path, measurements }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn point(p: impl Into<Point>) -> lyon::geom::Point<f32> {
|
||||||
|
let p: Point = p.into();
|
||||||
|
lyon::geom::point(p.x.min(1.0).max(0.0), p.y.min(1.0).max(0.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Builder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,7 +13,6 @@ use iced_core::{
|
||||||
|
|
||||||
use super::easing::{self, Easing};
|
use super::easing::{self, Easing};
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[allow(missing_debug_implementations)]
|
#[allow(missing_debug_implementations)]
|
||||||
|
|
@ -25,7 +24,7 @@ where
|
||||||
width: Length,
|
width: Length,
|
||||||
height: Length,
|
height: Length,
|
||||||
style: <Renderer::Theme as StyleSheet>::Style,
|
style: <Renderer::Theme as StyleSheet>::Style,
|
||||||
easing: Cow<'a, Easing>,
|
easing: &'a Easing,
|
||||||
cycle_duration: Duration,
|
cycle_duration: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ where
|
||||||
width: Length::Fixed(100.0),
|
width: Length::Fixed(100.0),
|
||||||
height: Length::Fixed(4.0),
|
height: Length::Fixed(4.0),
|
||||||
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
style: <Renderer::Theme as StyleSheet>::Style::default(),
|
||||||
easing: Cow::Borrowed(&easing::STANDARD),
|
easing: &easing::STANDARD,
|
||||||
cycle_duration: Duration::from_millis(600),
|
cycle_duration: Duration::from_millis(600),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -67,8 +66,8 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the motion easing of this [`Linear`].
|
/// Sets the motion easing of this [`Linear`].
|
||||||
pub fn easing(mut self, easing: impl Into<Cow<'a, Easing>>) -> Self {
|
pub fn easing(mut self, easing: &'a Easing) -> Self {
|
||||||
self.easing = easing.into();
|
self.easing = easing;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -12,17 +12,17 @@ use circular::Circular;
|
||||||
use linear::Linear;
|
use linear::Linear;
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
ProgressIndicators::run(Settings {
|
LoadingSpinners::run(Settings {
|
||||||
antialiasing: true,
|
antialiasing: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProgressIndicators {
|
struct LoadingSpinners {
|
||||||
cycle_duration: f32,
|
cycle_duration: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProgressIndicators {
|
impl Default for LoadingSpinners {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
cycle_duration: 2.0,
|
cycle_duration: 2.0,
|
||||||
|
|
@ -35,7 +35,7 @@ enum Message {
|
||||||
CycleDurationChanged(f32),
|
CycleDurationChanged(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for ProgressIndicators {
|
impl Application for LoadingSpinners {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
type Flags = ();
|
type Flags = ();
|
||||||
type Executor = executor::Default;
|
type Executor = executor::Default;
|
||||||
|
|
@ -46,7 +46,7 @@ impl Application for ProgressIndicators {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
String::from("Progress Indicators - Iced")
|
String::from("Loading Spinners - Iced")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, message: Message) -> Command<Message> {
|
fn update(&mut self, message: Message) -> Command<Message> {
|
||||||
|
|
@ -60,25 +60,39 @@ impl Application for ProgressIndicators {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
let column = easing::EXAMPLES
|
let column = [
|
||||||
.iter()
|
&easing::EMPHASIZED,
|
||||||
.zip(["Decelerating:", "Accelerating:", "Standard:"])
|
&easing::EMPHASIZED_DECELERATE,
|
||||||
.fold(column![], |column, (easing, label)| {
|
&easing::EMPHASIZED_ACCELERATE,
|
||||||
column.push(
|
&easing::STANDARD,
|
||||||
row![
|
&easing::STANDARD_DECELERATE,
|
||||||
text(label).width(150),
|
&easing::STANDARD_ACCELERATE,
|
||||||
Linear::new().easing(easing).cycle_duration(
|
]
|
||||||
Duration::from_secs_f32(self.cycle_duration)
|
.iter()
|
||||||
),
|
.zip([
|
||||||
Circular::new().easing(easing).cycle_duration(
|
"Emphasized:",
|
||||||
Duration::from_secs_f32(self.cycle_duration)
|
"Emphasized Decelerate:",
|
||||||
)
|
"Emphasized Accelerate:",
|
||||||
]
|
"Standard:",
|
||||||
.align_items(iced::Alignment::Center)
|
"Standard Decelerate:",
|
||||||
.spacing(20.0),
|
"Standard Accelerate:",
|
||||||
)
|
])
|
||||||
})
|
.fold(column![], |column, (easing, label)| {
|
||||||
.spacing(20);
|
column.push(
|
||||||
|
row![
|
||||||
|
text(label).width(250),
|
||||||
|
Linear::new().easing(easing).cycle_duration(
|
||||||
|
Duration::from_secs_f32(self.cycle_duration)
|
||||||
|
),
|
||||||
|
Circular::new().easing(easing).cycle_duration(
|
||||||
|
Duration::from_secs_f32(self.cycle_duration)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
.align_items(iced::Alignment::Center)
|
||||||
|
.spacing(20.0),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.spacing(20);
|
||||||
|
|
||||||
container(
|
container(
|
||||||
column.push(
|
column.push(
|
||||||
|
|
@ -87,7 +101,7 @@ impl Application for ProgressIndicators {
|
||||||
slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| {
|
slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| {
|
||||||
Message::CycleDurationChanged(x / 100.0)
|
Message::CycleDurationChanged(x / 100.0)
|
||||||
})
|
})
|
||||||
.width(150.0)
|
.width(200.0)
|
||||||
.into(),
|
.into(),
|
||||||
text(format!("{:.2}s", self.cycle_duration)).into(),
|
text(format!("{:.2}s", self.cycle_duration)).into(),
|
||||||
])
|
])
|
||||||
|
|
@ -1,67 +0,0 @@
|
||||||
use flo_curves::bezier::Curve;
|
|
||||||
use flo_curves::*;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref EXAMPLES: [Easing; 3] = [
|
|
||||||
Easing::CubicBezier(Curve::from_points(
|
|
||||||
Coord2(0.0, 0.0),
|
|
||||||
(Coord2(0.05, 0.7), Coord2(0.1, 1.0)),
|
|
||||||
Coord2(1.0, 1.0),
|
|
||||||
)),
|
|
||||||
Easing::CubicBezier(Curve::from_points(
|
|
||||||
Coord2(0.0, 0.0),
|
|
||||||
(Coord2(0.3, 0.0), Coord2(0.8, 0.15)),
|
|
||||||
Coord2(1.0, 1.0),
|
|
||||||
)),
|
|
||||||
Easing::CubicBezier(Curve::from_points(
|
|
||||||
Coord2(0.0, 0.0),
|
|
||||||
(Coord2(0.2, 0.0), Coord2(0.0, 1.0)),
|
|
||||||
Coord2(1.0, 1.0),
|
|
||||||
))
|
|
||||||
];
|
|
||||||
pub static ref STANDARD: Easing = {
|
|
||||||
Easing::CubicBezier(Curve::from_points(
|
|
||||||
Coord2(0.0, 0.0),
|
|
||||||
(Coord2(0.2, 0.0), Coord2(0.0, 1.0)),
|
|
||||||
Coord2(1.0, 1.0),
|
|
||||||
))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub enum Easing {
|
|
||||||
BezierPath(Vec<Curve<Coord2>>),
|
|
||||||
CubicBezier(Curve<Coord2>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Easing {
|
|
||||||
pub fn y_at_x(&self, x: f32) -> f32 {
|
|
||||||
let x = x as f64;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Self::BezierPath(curves) => curves
|
|
||||||
.iter()
|
|
||||||
.find_map(|curve| {
|
|
||||||
(curve.start_point().0 <= x && curve.end_point().0 >= x)
|
|
||||||
.then(|| curve.point_at_pos(x).1 as f32)
|
|
||||||
})
|
|
||||||
.unwrap_or_default(),
|
|
||||||
Self::CubicBezier(curve) => curve.point_at_pos(x).1 as f32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Easing> for Cow<'a, Easing> {
|
|
||||||
fn from(easing: Easing) -> Self {
|
|
||||||
Cow::Owned(easing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a Easing> for Cow<'a, Easing> {
|
|
||||||
fn from(easing: &'a Easing) -> Self {
|
|
||||||
Cow::Borrowed(easing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue