mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #16626 - BorisChiou:animation/timing_function/frames, r=emilio
Implement frames timing function A frames timing function is a type of timing function that divides the input time into a specified number of intervals of equal length, each of which is associated with an output progress value of increasing value. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #15740. - [X] There are tests for these changes, and web-platform-tests/css-timing-1 also provides tests.
This commit is contained in:
commit
15cf1acc72
6 changed files with 111 additions and 5 deletions
|
@ -352,6 +352,22 @@ impl PropertyAnimation {
|
|||
TransitionTimingFunction::Steps(steps, StartEnd::End) => {
|
||||
(time * (steps as f64)).floor() / (steps as f64)
|
||||
}
|
||||
TransitionTimingFunction::Frames(frames) => {
|
||||
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
||||
let mut out = (time * (frames as f64)).floor() / ((frames - 1) as f64);
|
||||
if out > 1.0 {
|
||||
// FIXME: Basically, during the animation sampling process, the input progress
|
||||
// should be in the range of [0, 1]. However, |time| is not accurate enough
|
||||
// here, which means |time| could be larger than 1.0 in the last animation
|
||||
// frame. (It should be equal to 1.0 exactly.) This makes the output of frames
|
||||
// timing function jumps to the next frame/level.
|
||||
// However, this solution is still not correct because |time| is possible
|
||||
// outside the range of [0, 1] after introducing Web Animations. We should fix
|
||||
// this problem when implementing web animations.
|
||||
out = 1.0;
|
||||
}
|
||||
out
|
||||
}
|
||||
};
|
||||
|
||||
self.property.update(style, progress);
|
||||
|
|
|
@ -21,6 +21,13 @@ impl nsTimingFunction {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_as_frames(&mut self, frames: u32) {
|
||||
self.mType = nsTimingFunction_Type::Frames;
|
||||
unsafe {
|
||||
self.__bindgen_anon_1.__bindgen_anon_1.as_mut().mStepsOrFrames = frames;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_as_bezier(&mut self,
|
||||
function_type: nsTimingFunction_Type,
|
||||
p1: Point2D<f32>,
|
||||
|
@ -48,6 +55,9 @@ impl From<ComputedTimingFunction> for nsTimingFunction {
|
|||
ComputedTimingFunction::Steps(steps, StartEnd::End) => {
|
||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
|
||||
},
|
||||
ComputedTimingFunction::Frames(frames) => {
|
||||
tf.set_as_frames(frames);
|
||||
},
|
||||
ComputedTimingFunction::CubicBezier(p1, p2) => {
|
||||
tf.set_as_bezier(nsTimingFunction_Type::CubicBezier, p1, p2);
|
||||
},
|
||||
|
@ -69,6 +79,10 @@ impl From<SpecifiedTimingFunction> for nsTimingFunction {
|
|||
debug_assert!(steps.value() >= 0);
|
||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps.value() as u32);
|
||||
},
|
||||
SpecifiedTimingFunction::Frames(frames) => {
|
||||
debug_assert!(frames.value() >= 2);
|
||||
tf.set_as_frames(frames.value() as u32);
|
||||
},
|
||||
SpecifiedTimingFunction::CubicBezier(p1, p2) => {
|
||||
tf.set_as_bezier(nsTimingFunction_Type::CubicBezier,
|
||||
Point2D::new(p1.x.get(), p1.y.get()),
|
||||
|
@ -104,6 +118,9 @@ impl From<SpecifiedTimingFunction> for nsTimingFunction {
|
|||
debug_assert!(keyword == FunctionKeyword::StepEnd && steps == 1);
|
||||
tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
|
||||
},
|
||||
ComputedTimingFunction::Frames(frames) => {
|
||||
tf.set_as_frames(frames)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -125,8 +142,8 @@ impl From<nsTimingFunction> for ComputedTimingFunction {
|
|||
StartEnd::End)
|
||||
},
|
||||
nsTimingFunction_Type::Frames => {
|
||||
// https://github.com/servo/servo/issues/15740
|
||||
panic!("Frames timing function is not support yet");
|
||||
ComputedTimingFunction::Frames(
|
||||
unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mStepsOrFrames })
|
||||
}
|
||||
nsTimingFunction_Type::Ease |
|
||||
nsTimingFunction_Type::Linear |
|
||||
|
|
|
@ -511,6 +511,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
pub enum T {
|
||||
CubicBezier(Point2D<f32>, Point2D<f32>),
|
||||
Steps(u32, StartEnd),
|
||||
Frames(u32),
|
||||
}
|
||||
|
||||
impl ToCss for T {
|
||||
|
@ -528,10 +529,15 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
try!(dest.write_str(", "));
|
||||
try!(p2.y.to_css(dest));
|
||||
dest.write_str(")")
|
||||
}
|
||||
},
|
||||
T::Steps(steps, start_end) => {
|
||||
super::serialize_steps(dest, specified::Integer::new(steps as i32), start_end)
|
||||
}
|
||||
},
|
||||
T::Frames(frames) => {
|
||||
try!(dest.write_str("frames("));
|
||||
try!(frames.to_css(dest));
|
||||
dest.write_str(")")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -569,6 +575,7 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
pub enum SpecifiedValue {
|
||||
CubicBezier(Point2D<Number>, Point2D<Number>),
|
||||
Steps(specified::Integer, StartEnd),
|
||||
Frames(specified::Integer),
|
||||
Keyword(FunctionKeyword),
|
||||
}
|
||||
|
||||
|
@ -617,6 +624,13 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
}));
|
||||
Ok(SpecifiedValue::Steps(step_count, start_end))
|
||||
},
|
||||
"frames" => {
|
||||
// https://drafts.csswg.org/css-timing/#frames-timing-functions
|
||||
let frames = try!(input.parse_nested_block(|input| {
|
||||
specified::Integer::parse_with_minimum(context, input, 2)
|
||||
}));
|
||||
Ok(SpecifiedValue::Frames(frames))
|
||||
},
|
||||
_ => Err(())
|
||||
}
|
||||
}
|
||||
|
@ -655,6 +669,11 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
SpecifiedValue::Steps(steps, start_end) => {
|
||||
serialize_steps(dest, steps, start_end)
|
||||
},
|
||||
SpecifiedValue::Frames(frames) => {
|
||||
try!(dest.write_str("frames("));
|
||||
try!(frames.to_css(dest));
|
||||
dest.write_str(")")
|
||||
},
|
||||
SpecifiedValue::Keyword(keyword) => {
|
||||
match keyword {
|
||||
FunctionKeyword::StepStart => {
|
||||
|
@ -686,6 +705,9 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
SpecifiedValue::Steps(count, start_end) => {
|
||||
computed_value::T::Steps(count.to_computed_value(context) as u32, start_end)
|
||||
},
|
||||
SpecifiedValue::Frames(frames) => {
|
||||
computed_value::T::Frames(frames.to_computed_value(context) as u32)
|
||||
},
|
||||
SpecifiedValue::Keyword(keyword) => keyword.to_computed_value(),
|
||||
}
|
||||
}
|
||||
|
@ -703,6 +725,10 @@ ${helpers.single_keyword("overflow-x", "visible hidden scroll auto",
|
|||
let int_count = count as i32;
|
||||
SpecifiedValue::Steps(specified::Integer::from_computed_value(&int_count), start_end)
|
||||
},
|
||||
computed_value::T::Frames(frames) => {
|
||||
let frames = frames as i32;
|
||||
SpecifiedValue::Frames(specified::Integer::from_computed_value(&frames))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -845,7 +845,8 @@ impl Parse for Integer {
|
|||
}
|
||||
|
||||
impl Integer {
|
||||
fn parse_with_minimum(context: &ParserContext, input: &mut Parser, min: i32) -> Result<Integer, ()> {
|
||||
#[allow(missing_docs)]
|
||||
pub fn parse_with_minimum(context: &ParserContext, input: &mut Parser, min: i32) -> Result<Integer, ()> {
|
||||
match parse_integer(context, input) {
|
||||
Ok(value) if value.value() >= min => Ok(value),
|
||||
_ => Err(()),
|
||||
|
|
|
@ -25,9 +25,29 @@ fn test_cubic_bezier() {
|
|||
#[test]
|
||||
fn test_steps() {
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "steps(1)");
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "steps( 1)", "steps(1)");
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "steps(1, start)");
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "steps(2, end) ", "steps(2)");
|
||||
|
||||
// Step interval value must be an integer greater than 0
|
||||
assert!(parse(transition_timing_function::parse, "steps(0)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "steps(0.5)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "steps(-1)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "steps(1, middle)").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_frames() {
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "frames( 2 )", "frames(2)");
|
||||
assert_roundtrip_with_context!(transition_timing_function::parse, "frames(10000)");
|
||||
|
||||
// Frames number must be an integer greater than 1
|
||||
assert!(parse(transition_timing_function::parse, "frames(1)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(-2)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames()").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(,)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(a)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(2.0)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(2.5)").is_err());
|
||||
assert!(parse(transition_timing_function::parse, "frames(2 3)").is_err());
|
||||
}
|
||||
|
|
|
@ -1209,6 +1209,32 @@ mod shorthand_serialization {
|
|||
|
||||
assert_eq!(serialization, block_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_should_serialize_acceptable_step_timing_function() {
|
||||
let block_text = "transition-property: margin-left; \
|
||||
transition-duration: 3s; \
|
||||
transition-delay: 4s; \
|
||||
transition-timing-function: steps(2, start);";
|
||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
||||
|
||||
let serialization = block.to_css_string();
|
||||
|
||||
assert_eq!(serialization, "transition: margin-left 3s steps(2, start) 4s;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition_should_serialize_acceptable_frames_timing_function() {
|
||||
let block_text = "transition-property: margin-left; \
|
||||
transition-duration: 3s; \
|
||||
transition-delay: 4s; \
|
||||
transition-timing-function: frames(2);";
|
||||
let block = parse(|c, i| Ok(parse_property_declaration_list(c, i)), block_text).unwrap();
|
||||
|
||||
let serialization = block.to_css_string();
|
||||
|
||||
assert_eq!(serialization, "transition: margin-left 3s frames(2) 4s;");
|
||||
}
|
||||
}
|
||||
|
||||
mod keywords {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue