mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-25 10:08:33 +02:00
feat(animation): cubic-bezier for styles
This commit adds ability to use cubic-bezier as an animation style, which allows users to customize the smoothness of animations.
This commit is contained in:
@@ -355,6 +355,61 @@ impl Ease for EaseInOutBounce {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct CubicBezier {
|
||||||
|
pub x1: f64,
|
||||||
|
pub y1: f64,
|
||||||
|
pub x2: f64,
|
||||||
|
pub y2: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CubicBezier {
|
||||||
|
fn x(&self, s: f64) -> f64 {
|
||||||
|
3.0 * self.x1 * s * (1.0 - s).powi(2) + 3.0 * self.x2 * s.powi(2) * (1.0 - s) + s.powi(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn y(&self, s: f64) -> f64 {
|
||||||
|
3.0 * self.y1 * s * (1.0 - s).powi(2) + 3.0 * self.y2 * s.powi(2) * (1.0 - s) + s.powi(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dx_ds(&self, s: f64) -> f64 {
|
||||||
|
3.0 * self.x1 * (1.0 - s) * (1.0 - 3.0 * s)
|
||||||
|
+ 3.0 * self.x2 * (2.0 * s - 3.0 * s.powi(2))
|
||||||
|
+ 3.0 * s.powi(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_s(&self, t: f64) -> f64 {
|
||||||
|
if t <= 0.0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if t >= 1.0 {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut s = t;
|
||||||
|
|
||||||
|
for _ in 0..8 {
|
||||||
|
let x_val = self.x(s);
|
||||||
|
let dx_val = self.dx_ds(s);
|
||||||
|
if dx_val.abs() < 1e-6 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let delta = (x_val - t) / dx_val;
|
||||||
|
s = (s - delta).clamp(0.0, 1.0);
|
||||||
|
if delta.abs() < 1e-6 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
fn evaluate(&self, t: f64) -> f64 {
|
||||||
|
let s = self.find_s(t.clamp(0.0, 1.0));
|
||||||
|
self.y(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn apply_ease_func(t: f64, style: AnimationStyle) -> f64 {
|
pub fn apply_ease_func(t: f64, style: AnimationStyle) -> f64 {
|
||||||
match style {
|
match style {
|
||||||
AnimationStyle::Linear => Linear::evaluate(t),
|
AnimationStyle::Linear => Linear::evaluate(t),
|
||||||
@@ -387,5 +442,6 @@ pub fn apply_ease_func(t: f64, style: AnimationStyle) -> f64 {
|
|||||||
AnimationStyle::EaseInBounce => EaseInBounce::evaluate(t),
|
AnimationStyle::EaseInBounce => EaseInBounce::evaluate(t),
|
||||||
AnimationStyle::EaseOutBounce => EaseOutBounce::evaluate(t),
|
AnimationStyle::EaseOutBounce => EaseOutBounce::evaluate(t),
|
||||||
AnimationStyle::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
AnimationStyle::EaseInOutBounce => EaseInOutBounce::evaluate(t),
|
||||||
|
AnimationStyle::CubicBezier(x1, y1, x2, y2) => CubicBezier { x1, y1, x2, y2 }.evaluate(t),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
|
|
||||||
|
use serde::ser::SerializeSeq;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use strum::Display;
|
use strum::Display;
|
||||||
use strum::EnumString;
|
use strum::EnumString;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Display, EnumString, ValueEnum, PartialEq)]
|
#[derive(Copy, Clone, Debug, Display, EnumString, ValueEnum, PartialEq)]
|
||||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||||
pub enum AnimationStyle {
|
pub enum AnimationStyle {
|
||||||
Linear,
|
Linear,
|
||||||
@@ -38,4 +39,81 @@ pub enum AnimationStyle {
|
|||||||
EaseInBounce,
|
EaseInBounce,
|
||||||
EaseOutBounce,
|
EaseOutBounce,
|
||||||
EaseInOutBounce,
|
EaseInOutBounce,
|
||||||
|
#[value(skip)]
|
||||||
|
CubicBezier(f64, f64, f64, f64),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom serde implementation
|
||||||
|
impl<'de> Deserialize<'de> for AnimationStyle {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct AnimationStyleVisitor;
|
||||||
|
|
||||||
|
impl<'de> serde::de::Visitor<'de> for AnimationStyleVisitor {
|
||||||
|
type Value = AnimationStyle;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a string or an array of four f64 values")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle string variants (e.g., "EaseInOutExpo")
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: serde::de::Error,
|
||||||
|
{
|
||||||
|
value.parse().map_err(|_| E::unknown_variant(value, &[]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle CubicBezier array (e.g., [0.32, 0.72, 0.0, 1.0])
|
||||||
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::SeqAccess<'de>,
|
||||||
|
{
|
||||||
|
let x1 = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
|
||||||
|
let y1 = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
|
||||||
|
let x2 = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
|
||||||
|
let y2 = seq
|
||||||
|
.next_element()?
|
||||||
|
.ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
|
||||||
|
|
||||||
|
// Ensure no extra elements
|
||||||
|
if seq.next_element::<serde::de::IgnoredAny>()?.is_some() {
|
||||||
|
return Err(serde::de::Error::invalid_length(5, &self));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(AnimationStyle::CubicBezier(x1, y1, x2, y2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(AnimationStyleVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for AnimationStyle {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
// Serialize CubicBezier as an array
|
||||||
|
AnimationStyle::CubicBezier(x1, y1, x2, y2) => {
|
||||||
|
let mut seq = serializer.serialize_seq(Some(4))?;
|
||||||
|
seq.serialize_element(x1)?;
|
||||||
|
seq.serialize_element(y1)?;
|
||||||
|
seq.serialize_element(x2)?;
|
||||||
|
seq.serialize_element(y2)?;
|
||||||
|
seq.end()
|
||||||
|
}
|
||||||
|
// Serialize all other variants as strings
|
||||||
|
_ => serializer.serialize_str(&self.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
200
schema.json
200
schema.json
@@ -55,74 +55,146 @@
|
|||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": {
|
"additionalProperties": {
|
||||||
"type": "string",
|
"oneOf": [
|
||||||
"enum": [
|
{
|
||||||
"Linear",
|
"type": "string",
|
||||||
"EaseInSine",
|
"enum": [
|
||||||
"EaseOutSine",
|
"Linear",
|
||||||
"EaseInOutSine",
|
"EaseInSine",
|
||||||
"EaseInQuad",
|
"EaseOutSine",
|
||||||
"EaseOutQuad",
|
"EaseInOutSine",
|
||||||
"EaseInOutQuad",
|
"EaseInQuad",
|
||||||
"EaseInCubic",
|
"EaseOutQuad",
|
||||||
"EaseInOutCubic",
|
"EaseInOutQuad",
|
||||||
"EaseInQuart",
|
"EaseInCubic",
|
||||||
"EaseOutQuart",
|
"EaseInOutCubic",
|
||||||
"EaseInOutQuart",
|
"EaseInQuart",
|
||||||
"EaseInQuint",
|
"EaseOutQuart",
|
||||||
"EaseOutQuint",
|
"EaseInOutQuart",
|
||||||
"EaseInOutQuint",
|
"EaseInQuint",
|
||||||
"EaseInExpo",
|
"EaseOutQuint",
|
||||||
"EaseOutExpo",
|
"EaseInOutQuint",
|
||||||
"EaseInOutExpo",
|
"EaseInExpo",
|
||||||
"EaseInCirc",
|
"EaseOutExpo",
|
||||||
"EaseOutCirc",
|
"EaseInOutExpo",
|
||||||
"EaseInOutCirc",
|
"EaseInCirc",
|
||||||
"EaseInBack",
|
"EaseOutCirc",
|
||||||
"EaseOutBack",
|
"EaseInOutCirc",
|
||||||
"EaseInOutBack",
|
"EaseInBack",
|
||||||
"EaseInElastic",
|
"EaseOutBack",
|
||||||
"EaseOutElastic",
|
"EaseInOutBack",
|
||||||
"EaseInOutElastic",
|
"EaseInElastic",
|
||||||
"EaseInBounce",
|
"EaseOutElastic",
|
||||||
"EaseOutBounce",
|
"EaseInOutElastic",
|
||||||
"EaseInOutBounce"
|
"EaseInBounce",
|
||||||
|
"EaseOutBounce",
|
||||||
|
"EaseInOutBounce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"CubicBezier"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"CubicBezier": {
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxItems": 4,
|
||||||
|
"minItems": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "string",
|
"oneOf": [
|
||||||
"enum": [
|
{
|
||||||
"Linear",
|
"type": "string",
|
||||||
"EaseInSine",
|
"enum": [
|
||||||
"EaseOutSine",
|
"Linear",
|
||||||
"EaseInOutSine",
|
"EaseInSine",
|
||||||
"EaseInQuad",
|
"EaseOutSine",
|
||||||
"EaseOutQuad",
|
"EaseInOutSine",
|
||||||
"EaseInOutQuad",
|
"EaseInQuad",
|
||||||
"EaseInCubic",
|
"EaseOutQuad",
|
||||||
"EaseInOutCubic",
|
"EaseInOutQuad",
|
||||||
"EaseInQuart",
|
"EaseInCubic",
|
||||||
"EaseOutQuart",
|
"EaseInOutCubic",
|
||||||
"EaseInOutQuart",
|
"EaseInQuart",
|
||||||
"EaseInQuint",
|
"EaseOutQuart",
|
||||||
"EaseOutQuint",
|
"EaseInOutQuart",
|
||||||
"EaseInOutQuint",
|
"EaseInQuint",
|
||||||
"EaseInExpo",
|
"EaseOutQuint",
|
||||||
"EaseOutExpo",
|
"EaseInOutQuint",
|
||||||
"EaseInOutExpo",
|
"EaseInExpo",
|
||||||
"EaseInCirc",
|
"EaseOutExpo",
|
||||||
"EaseOutCirc",
|
"EaseInOutExpo",
|
||||||
"EaseInOutCirc",
|
"EaseInCirc",
|
||||||
"EaseInBack",
|
"EaseOutCirc",
|
||||||
"EaseOutBack",
|
"EaseInOutCirc",
|
||||||
"EaseInOutBack",
|
"EaseInBack",
|
||||||
"EaseInElastic",
|
"EaseOutBack",
|
||||||
"EaseOutElastic",
|
"EaseInOutBack",
|
||||||
"EaseInOutElastic",
|
"EaseInElastic",
|
||||||
"EaseInBounce",
|
"EaseOutElastic",
|
||||||
"EaseOutBounce",
|
"EaseInOutElastic",
|
||||||
"EaseInOutBounce"
|
"EaseInBounce",
|
||||||
|
"EaseOutBounce",
|
||||||
|
"EaseInOutBounce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"CubicBezier"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"CubicBezier": {
|
||||||
|
"type": "array",
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number",
|
||||||
|
"format": "double"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxItems": 4,
|
||||||
|
"minItems": 4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user