render: apply settled animation end states

This commit is contained in:
SGavrl committed 2026-07-26 14:38:24 +00:00
1 parent ec5950da06
commit b03c663952
5 files changed
+253

No files matched your search

+87
View File
@@ -649,6 +649,24 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
}
"visibility" => style.visibility_hidden = Some(value.eq_ignore_ascii_case("hidden")),
"opacity" => style.opacity = value.trim().parse::<f32>().ok(),
"animation" => apply_animation_shorthand(style, value),
"animation-name" => {
let first = split_top_level(value, ',').into_iter().next().unwrap_or("").trim();
style.animation_name = if first.is_empty() || first.eq_ignore_ascii_case("none") {
None
} else {
Some(first.to_string())
};
}
"animation-fill-mode" => {
let first = split_top_level(value, ',').into_iter().next().unwrap_or("").trim();
style.animation_fill_forwards =
first.eq_ignore_ascii_case("forwards") || first.eq_ignore_ascii_case("both");
}
"animation-iteration-count" => {
let first = split_top_level(value, ',').into_iter().next().unwrap_or("").trim();
style.animation_iteration_infinite = first.eq_ignore_ascii_case("infinite");
}
"z-index" => {
style.z_index = match value.trim() {
"auto" | "inherit" | "initial" => None,
@@ -827,6 +845,56 @@ fn apply_value(style: &mut LayoutStyle, name: &str, value: &str) {
}
}
fn apply_animation_shorthand(style: &mut LayoutStyle, value: &str) {
style.animation_name = None;
style.animation_fill_forwards = false;
style.animation_iteration_infinite = false;
let first = split_top_level(value, ',').into_iter().next().unwrap_or("").trim();
if first.is_empty() || first.eq_ignore_ascii_case("none") {
return;
}
for token in split_ws_paren(first) {
let lower = token.to_ascii_lowercase();
let timing_keyword = matches!(
lower.as_str(),
"linear" | "ease" | "ease-in" | "ease-out" | "ease-in-out"
) || lower.starts_with("cubic-bezier(")
|| lower.starts_with("steps(")
|| lower.starts_with("linear(");
let time = lower
.strip_suffix("ms")
.or_else(|| lower.strip_suffix('s'))
.and_then(|number| number.parse::<f32>().ok())
.is_some();
let control_keyword = matches!(
lower.as_str(),
"normal"
| "reverse"
| "alternate"
| "alternate-reverse"
| "backwards"
| "running"
| "paused"
);
if lower == "forwards" || lower == "both" {
style.animation_fill_forwards = true;
} else if lower == "infinite" {
style.animation_iteration_infinite = true;
} else if time
|| timing_keyword
|| control_keyword
|| lower.parse::<f32>().is_ok()
|| lower == "none"
{
continue;
} else if style.animation_name.is_none() {
style.animation_name = Some(token.to_string());
}
}
}
/// Parse the common CSS Box Alignment values that taffy can represent.
///
/// The outer option distinguishes an invalid declaration from `auto`, which
@@ -3189,6 +3257,25 @@ mod tests {
assert!(flow_root.flow_root);
}
#[test]
fn animation_shorthand_preserves_settled_forward_fill_contract() {
let finite = compute_style(
"div",
Some("animation: dismiss-overlay .6s ease-out forwards"),
);
assert_eq!(finite.animation_name.as_deref(), Some("dismiss-overlay"));
assert!(finite.animation_fill_forwards);
assert!(!finite.animation_iteration_infinite);
let infinite = compute_style(
"div",
Some("animation: pulse 1s linear infinite"),
);
assert_eq!(infinite.animation_name.as_deref(), Some("pulse"));
assert!(!infinite.animation_fill_forwards);
assert!(infinite.animation_iteration_infinite);
}
#[test]
fn authored_display_replaces_internal_flex_provenance() {
let native_cell = compute_style("td", None);