mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-01-11 22:12:53 +01: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 {
|
||||
match style {
|
||||
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::EaseOutBounce => EaseOutBounce::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 serde::ser::SerializeSeq;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use strum::Display;
|
||||
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))]
|
||||
pub enum AnimationStyle {
|
||||
Linear,
|
||||
@@ -38,4 +39,81 @@ pub enum AnimationStyle {
|
||||
EaseInBounce,
|
||||
EaseOutBounce,
|
||||
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",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Linear",
|
||||
"EaseInSine",
|
||||
"EaseOutSine",
|
||||
"EaseInOutSine",
|
||||
"EaseInQuad",
|
||||
"EaseOutQuad",
|
||||
"EaseInOutQuad",
|
||||
"EaseInCubic",
|
||||
"EaseInOutCubic",
|
||||
"EaseInQuart",
|
||||
"EaseOutQuart",
|
||||
"EaseInOutQuart",
|
||||
"EaseInQuint",
|
||||
"EaseOutQuint",
|
||||
"EaseInOutQuint",
|
||||
"EaseInExpo",
|
||||
"EaseOutExpo",
|
||||
"EaseInOutExpo",
|
||||
"EaseInCirc",
|
||||
"EaseOutCirc",
|
||||
"EaseInOutCirc",
|
||||
"EaseInBack",
|
||||
"EaseOutBack",
|
||||
"EaseInOutBack",
|
||||
"EaseInElastic",
|
||||
"EaseOutElastic",
|
||||
"EaseInOutElastic",
|
||||
"EaseInBounce",
|
||||
"EaseOutBounce",
|
||||
"EaseInOutBounce"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Linear",
|
||||
"EaseInSine",
|
||||
"EaseOutSine",
|
||||
"EaseInOutSine",
|
||||
"EaseInQuad",
|
||||
"EaseOutQuad",
|
||||
"EaseInOutQuad",
|
||||
"EaseInCubic",
|
||||
"EaseInOutCubic",
|
||||
"EaseInQuart",
|
||||
"EaseOutQuart",
|
||||
"EaseInOutQuart",
|
||||
"EaseInQuint",
|
||||
"EaseOutQuint",
|
||||
"EaseInOutQuint",
|
||||
"EaseInExpo",
|
||||
"EaseOutExpo",
|
||||
"EaseInOutExpo",
|
||||
"EaseInCirc",
|
||||
"EaseOutCirc",
|
||||
"EaseInOutCirc",
|
||||
"EaseInBack",
|
||||
"EaseOutBack",
|
||||
"EaseInOutBack",
|
||||
"EaseInElastic",
|
||||
"EaseOutElastic",
|
||||
"EaseInOutElastic",
|
||||
"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",
|
||||
"enum": [
|
||||
"Linear",
|
||||
"EaseInSine",
|
||||
"EaseOutSine",
|
||||
"EaseInOutSine",
|
||||
"EaseInQuad",
|
||||
"EaseOutQuad",
|
||||
"EaseInOutQuad",
|
||||
"EaseInCubic",
|
||||
"EaseInOutCubic",
|
||||
"EaseInQuart",
|
||||
"EaseOutQuart",
|
||||
"EaseInOutQuart",
|
||||
"EaseInQuint",
|
||||
"EaseOutQuint",
|
||||
"EaseInOutQuint",
|
||||
"EaseInExpo",
|
||||
"EaseOutExpo",
|
||||
"EaseInOutExpo",
|
||||
"EaseInCirc",
|
||||
"EaseOutCirc",
|
||||
"EaseInOutCirc",
|
||||
"EaseInBack",
|
||||
"EaseOutBack",
|
||||
"EaseInOutBack",
|
||||
"EaseInElastic",
|
||||
"EaseOutElastic",
|
||||
"EaseInOutElastic",
|
||||
"EaseInBounce",
|
||||
"EaseOutBounce",
|
||||
"EaseInOutBounce"
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Linear",
|
||||
"EaseInSine",
|
||||
"EaseOutSine",
|
||||
"EaseInOutSine",
|
||||
"EaseInQuad",
|
||||
"EaseOutQuad",
|
||||
"EaseInOutQuad",
|
||||
"EaseInCubic",
|
||||
"EaseInOutCubic",
|
||||
"EaseInQuart",
|
||||
"EaseOutQuart",
|
||||
"EaseInOutQuart",
|
||||
"EaseInQuint",
|
||||
"EaseOutQuint",
|
||||
"EaseInOutQuint",
|
||||
"EaseInExpo",
|
||||
"EaseOutExpo",
|
||||
"EaseInOutExpo",
|
||||
"EaseInCirc",
|
||||
"EaseOutCirc",
|
||||
"EaseInOutCirc",
|
||||
"EaseInBack",
|
||||
"EaseOutBack",
|
||||
"EaseInOutBack",
|
||||
"EaseInElastic",
|
||||
"EaseOutElastic",
|
||||
"EaseInOutElastic",
|
||||
"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