mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-01 14:37:05 -06:00
Animations: Darken and Desaturate use PowerPoint's preset ids and HSL shift, and 14 effects get its default durations
This commit is contained in:
+16
-16
@@ -367,37 +367,37 @@ pub static ANIMATIONS: &[(&str, &str, AnimClass, u32, u32, &[&str])] = &[
|
||||
("shape", "Shape", AnimClass::Entrance, 6, 2000, &["circleIn", "circleOut", "boxIn", "boxOut", "diamondIn", "diamondOut", "plusIn", "plusOut"]),
|
||||
("wheel", "Wheel", AnimClass::Entrance, 21, 2000, &["1", "2", "3", "4", "8"]),
|
||||
("randomBars", "Random Bars", AnimClass::Entrance, 14, 500, &["horz", "vert"]),
|
||||
("grow", "Grow & Turn", AnimClass::Entrance, 31, 500, &[]),
|
||||
("grow", "Grow & Turn", AnimClass::Entrance, 31, 1000, &[]),
|
||||
("zoom", "Zoom", AnimClass::Entrance, 53, 500, &["in", "out"]),
|
||||
("swivel", "Swivel", AnimClass::Entrance, 45, 2000, &["horz", "vert"]),
|
||||
("bounce", "Bounce", AnimClass::Entrance, 26, 2000, &[]),
|
||||
("blinds", "Blinds", AnimClass::Entrance, 3, 500, &["horz", "vert"]),
|
||||
("box", "Box", AnimClass::Entrance, 4, 500, &["in", "out"]),
|
||||
("box", "Box", AnimClass::Entrance, 4, 2000, &["in", "out"]),
|
||||
("checkerboard", "Checkerboard", AnimClass::Entrance, 5, 500, &["across", "down"]),
|
||||
("circle", "Circle", AnimClass::Entrance, 6, 500, &["in", "out"]),
|
||||
("diamond", "Diamond", AnimClass::Entrance, 8, 500, &["in", "out"]),
|
||||
("circle", "Circle", AnimClass::Entrance, 6, 2000, &["in", "out"]),
|
||||
("diamond", "Diamond", AnimClass::Entrance, 8, 2000, &["in", "out"]),
|
||||
("dissolve", "Dissolve In", AnimClass::Entrance, 9, 500, &[]),
|
||||
("peek", "Peek In", AnimClass::Entrance, 12, 500, &["b", "l", "t", "r"]),
|
||||
("strips", "Strips", AnimClass::Entrance, 18, 500, &["lu", "ru", "ld", "rd"]),
|
||||
("expand", "Expand", AnimClass::Entrance, 55, 500, &[]),
|
||||
("expand", "Expand", AnimClass::Entrance, 55, 1000, &[]),
|
||||
("rise", "Rise Up", AnimClass::Entrance, 37, 1000, &[]),
|
||||
("spinner", "Spinner", AnimClass::Entrance, 49, 1000, &[]),
|
||||
("spinner", "Spinner", AnimClass::Entrance, 49, 500, &[]),
|
||||
("pulse", "Pulse", AnimClass::Emphasis, 26, 500, &[]),
|
||||
("colorPulse", "Color Pulse", AnimClass::Emphasis, 27, 500, &[]),
|
||||
("colorPulse", "Color Pulse", AnimClass::Emphasis, 27, 250, &[]),
|
||||
("teeter", "Teeter", AnimClass::Emphasis, 32, 1000, &[]),
|
||||
("spin", "Spin", AnimClass::Emphasis, 8, 2000, &["clockwise", "counterClockwise"]),
|
||||
("growShrink", "Grow/Shrink", AnimClass::Emphasis, 6, 2000, &["both", "horz", "vert"]),
|
||||
("desaturate", "Desaturate", AnimClass::Emphasis, 35, 500, &[]),
|
||||
("darken", "Darken", AnimClass::Emphasis, 25, 500, &[]),
|
||||
("desaturate", "Desaturate", AnimClass::Emphasis, 25, 500, &[]),
|
||||
("darken", "Darken", AnimClass::Emphasis, 24, 500, &[]),
|
||||
("lighten", "Lighten", AnimClass::Emphasis, 30, 500, &[]),
|
||||
("transparency", "Transparency", AnimClass::Emphasis, 9, 500, &[]),
|
||||
("objectColor", "Object Color", AnimClass::Emphasis, 19, 2000, &[]),
|
||||
("objectColor", "Object Color", AnimClass::Emphasis, 19, 500, &[]),
|
||||
("complementaryColor", "Complementary Color", AnimClass::Emphasis, 21, 500, &[]),
|
||||
("lineColor", "Line Color", AnimClass::Emphasis, 7, 2000, &[]),
|
||||
("fillColor", "Fill Color", AnimClass::Emphasis, 1, 2000, &[]),
|
||||
("fontColor", "Font Color", AnimClass::Emphasis, 3, 2000, &[]),
|
||||
("underline", "Underline", AnimClass::Emphasis, 18, 500, &[]),
|
||||
("boldFlash", "Bold Flash", AnimClass::Emphasis, 10, 500, &[]),
|
||||
("boldFlash", "Bold Flash", AnimClass::Emphasis, 10, 2000, &[]),
|
||||
("wave", "Wave", AnimClass::Emphasis, 34, 500, &[]),
|
||||
("disappear", "Disappear", AnimClass::Exit, 1, 0, &[]),
|
||||
("fadeOut", "Fade", AnimClass::Exit, 10, 500, &[]),
|
||||
@@ -408,20 +408,20 @@ pub static ANIMATIONS: &[(&str, &str, AnimClass, u32, u32, &[&str])] = &[
|
||||
("shapeOut", "Shape", AnimClass::Exit, 6, 2000, &["circleIn", "circleOut"]),
|
||||
("wheelOut", "Wheel", AnimClass::Exit, 21, 2000, &["1", "2", "3", "4", "8"]),
|
||||
("randomBarsOut", "Random Bars", AnimClass::Exit, 14, 500, &["horz", "vert"]),
|
||||
("shrinkTurn", "Shrink & Turn", AnimClass::Exit, 31, 500, &[]),
|
||||
("shrinkTurn", "Shrink & Turn", AnimClass::Exit, 31, 1000, &[]),
|
||||
("zoomOut", "Zoom", AnimClass::Exit, 53, 500, &["in", "out"]),
|
||||
("swivelOut", "Swivel", AnimClass::Exit, 45, 2000, &["horz", "vert"]),
|
||||
("bounceOut", "Bounce", AnimClass::Exit, 26, 2000, &[]),
|
||||
("dissolveOut", "Dissolve Out", AnimClass::Exit, 9, 500, &[]),
|
||||
("blindsOut", "Blinds", AnimClass::Exit, 3, 500, &["horz", "vert"]),
|
||||
("boxOut", "Box", AnimClass::Exit, 4, 500, &["in", "out"]),
|
||||
("boxOut", "Box", AnimClass::Exit, 4, 2000, &["in", "out"]),
|
||||
("checkerboardOut", "Checkerboard", AnimClass::Exit, 5, 500, &["across", "down"]),
|
||||
("diamondOut", "Diamond", AnimClass::Exit, 8, 500, &["in", "out"]),
|
||||
("diamondOut", "Diamond", AnimClass::Exit, 8, 2000, &["in", "out"]),
|
||||
("peekOut", "Peek Out", AnimClass::Exit, 12, 500, &["b", "l", "t", "r"]),
|
||||
("stripsOut", "Strips", AnimClass::Exit, 18, 500, &["lu", "ru", "ld", "rd"]),
|
||||
("sinkDown", "Sink Down", AnimClass::Exit, 37, 1000, &[]),
|
||||
("spinnerOut", "Spinner", AnimClass::Exit, 49, 1000, &[]),
|
||||
("contract", "Contract", AnimClass::Exit, 55, 500, &[]),
|
||||
("spinnerOut", "Spinner", AnimClass::Exit, 49, 500, &[]),
|
||||
("contract", "Contract", AnimClass::Exit, 55, 1000, &[]),
|
||||
("lines", "Lines", AnimClass::Path, 64, 2000, &["down", "up", "right", "left"]),
|
||||
("arcs", "Arcs", AnimClass::Path, 37, 2000, &["down", "up", "right", "left"]),
|
||||
("turns", "Turns", AnimClass::Path, 43, 2000, &["down", "up", "right", "left"]),
|
||||
|
||||
@@ -435,4 +435,15 @@ mod tests {
|
||||
assert_eq!(effect_for_preset(AnimClass::Entrance, 2), Some("fly"));
|
||||
assert_eq!(effect_for_preset(AnimClass::Emphasis, 8), Some("spin"));
|
||||
}
|
||||
|
||||
/// PowerPoint's own ids: Darken is 24 and Desaturate 25 (read back from its saved file).
|
||||
#[test]
|
||||
fn darken_and_desaturate_use_powerpoints_ids() {
|
||||
assert_eq!(effect_for_preset(AnimClass::Emphasis, 24), Some("darken"));
|
||||
assert_eq!(effect_for_preset(AnimClass::Emphasis, 25), Some("desaturate"));
|
||||
for (effect, id) in [("darken", 24), ("desaturate", 25)] {
|
||||
let a = deckcraft_model::Animation { class: AnimClass::Emphasis, effect: effect.into(), ..Default::default() };
|
||||
assert_eq!(preset_for(&a), id, "{effect}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +383,19 @@ fn behaviors(w: &mut W, x: &mut Exp, ids: &mut Ids, e: &Eff) {
|
||||
w.close("p:animClr");
|
||||
}
|
||||
}
|
||||
"fillColor" | "objectColor" | "fontColor" | "lineColor" | "complementaryColor" | "darken" | "lighten" => {
|
||||
// As PowerPoint writes them: a relative HSL shift of text, fill and line colour.
|
||||
"darken" | "desaturate" => {
|
||||
let (s, l) = if a.effect == "darken" { (0, -25_098) } else { (-70_588, 0) };
|
||||
for attr in ["style.color", "fillcolor", "stroke.color"] {
|
||||
w.open("p:animClr", A::new().a("clrSpc", "hsl").a("dir", "cw"));
|
||||
cbhvr(w, ids, e, dur, None, &[attr], None);
|
||||
w.open0("p:by");
|
||||
w.empty("p:hsl", A::new().a("h", 0).a("s", s).a("l", l));
|
||||
w.close("p:by");
|
||||
w.close("p:animClr");
|
||||
}
|
||||
}
|
||||
"fillColor" | "objectColor" | "fontColor" | "lineColor" | "complementaryColor" | "lighten" => {
|
||||
let attr = match a.effect.as_str() {
|
||||
"fontColor" => "style.color",
|
||||
"lineColor" => "stroke.color",
|
||||
|
||||
@@ -1925,3 +1925,47 @@ fn zoom_inserts_linked_slide_pictures() {
|
||||
assert!(s.execute("insert.zoom", &json!({"slides": [42]})).is_err());
|
||||
assert!(s.execute("insert.zoom", &json!({"kind": "slide"})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn darken_and_desaturate_write_powerpoints_hsl_shift() {
|
||||
for (effect, preset, shift) in [("darken", 24, r#"s="0" l="-25098""#), ("desaturate", 25, r#"s="-70588" l="0""#)] {
|
||||
let mut p = defaults::new_presentation(None);
|
||||
let mut s = p.slides[0].as_ref().clone();
|
||||
let sh = shape(&mut p, "Target", 100.0, 100.0, 100.0, 100.0);
|
||||
let id = sh.id;
|
||||
s.shapes.push(sh);
|
||||
s.animations.push(Animation { shape: id, class: AnimClass::Emphasis, effect: effect.into(), duration_ms: 500, ..Default::default() });
|
||||
p.slides[0] = Arc::new(s);
|
||||
let bytes = deckcraft_pptx::export(&p).expect("export");
|
||||
let mut zip = zip::ZipArchive::new(std::io::Cursor::new(bytes)).unwrap();
|
||||
let mut xml = String::new();
|
||||
std::io::Read::read_to_string(&mut zip.by_name("ppt/slides/slide1.xml").unwrap(), &mut xml).unwrap();
|
||||
assert!(xml.contains(&format!(r#"presetID="{preset}""#)), "{effect}");
|
||||
assert!(xml.contains(r#"clrSpc="hsl""#) && xml.contains(shift), "{effect}: {xml}");
|
||||
assert_eq!(round(&p).slides[0].animations[0].effect, effect);
|
||||
}
|
||||
}
|
||||
|
||||
/// PowerPoint's default durations (COM `AddEffect`), which differ from the 0.5 s most effects get.
|
||||
#[test]
|
||||
fn default_durations_match_powerpoint() {
|
||||
for (effect, ms) in [
|
||||
("grow", 1000),
|
||||
("shrinkTurn", 1000),
|
||||
("box", 2000),
|
||||
("circle", 2000),
|
||||
("diamond", 2000),
|
||||
("boxOut", 2000),
|
||||
("diamondOut", 2000),
|
||||
("expand", 1000),
|
||||
("contract", 1000),
|
||||
("spinner", 500),
|
||||
("spinnerOut", 500),
|
||||
("colorPulse", 250),
|
||||
("objectColor", 500),
|
||||
("boldFlash", 2000),
|
||||
] {
|
||||
let a = deckcraft_model::anim::ANIMATIONS.iter().find(|a| a.0 == effect).unwrap();
|
||||
assert_eq!(a.4, ms, "{effect}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user