mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 08:45:46 +00:00
Bug 1260572 - Use 50% switch behavior if StyleAnimationValue::Interpolate fails; r=heycam
In KeyframeEffectReadOnly::ComposeStyle we call StyleAnimationValue::Interpolate but assume that it always passes. That was true when that code was only used for CSS animations and CSS transitions since they check that their animation values can be interpolated before setting up segments. However, when we set up animations using the Web Animations API we don't perform that check so it is possible for this call to fail. In that case, we could just bail, but, according to CSS Transitions we should apply a 50% switch in this case: https://drafts.csswg.org/css-transitions/#step-types (In Web Animations, specifying this is an open issue. See: https://w3c.github.io/web-animations/#specific-animation-behaviors). Bug 1064937 tracks doing this in general (we'll likely need to mark various properties as being no longer unanimatable but instead as supporting discrete animation) but we can start to introduce it now. Later in bug 1245748, CSS animations and transitions will likely start using the same code path as the Web Animations API for setting up keyframes. As a result, unless we take care to add checks that the values we set are interpolable, the 50% switch behavior will begin to apply to CSS animations and transitions too at that point. Some concerns have been raised about possible web compatibility issues around the 50% switch behavior (see [1] and [2]). For CSS animations, Chrome already supports this behavior so it should be ok at least for CSS animations. When we switch CSS transitions over to the same code path, however, we will need to be careful to add checks that the transition endpoints are interpolable (we can investigate introducing this behavior to transitions as a separate bug that can be easily backed out / preffed off). Regarding the naming of the test added here, going forward we would like to restructure the tests under web-platform-tests to better match the structure of the Web Animations since that seems to be the convention there. However, this doesn't *quite* match the structure of the spec since there are upcoming changes to the spec in this area (e.g. renaming animation behaviors to animation types). However, it should be close enough that we don't have to move it around too much in future. [1] https://drafts.csswg.org/css-transitions/#step-types [2] https://bugzilla.mozilla.org/show_bug.cgi?id=1064937#c0 MozReview-Commit-ID: KcxILrckJg9
This commit is contained in:
parent
a9d4b99d8d
commit
99a1a632e3
@ -617,16 +617,16 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
|||||||
computedTiming.mBeforeFlag);
|
computedTiming.mBeforeFlag);
|
||||||
|
|
||||||
StyleAnimationValue val;
|
StyleAnimationValue val;
|
||||||
#ifdef DEBUG
|
if (StyleAnimationValue::Interpolate(prop.mProperty,
|
||||||
bool result =
|
segment->mFromValue,
|
||||||
#endif
|
segment->mToValue,
|
||||||
StyleAnimationValue::Interpolate(prop.mProperty,
|
valuePosition, val)) {
|
||||||
segment->mFromValue,
|
aStyleRule->AddValue(prop.mProperty, Move(val));
|
||||||
segment->mToValue,
|
} else if (valuePosition < 0.5) {
|
||||||
valuePosition, val);
|
aStyleRule->AddValue(prop.mProperty, segment->mFromValue);
|
||||||
MOZ_ASSERT(result, "interpolate must succeed now");
|
} else {
|
||||||
|
aStyleRule->AddValue(prop.mProperty, segment->mToValue);
|
||||||
aStyleRule->AddValue(prop.mProperty, Move(val));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28389,10 +28389,6 @@
|
|||||||
"path": "web-animations/animatable/animate.html",
|
"path": "web-animations/animatable/animate.html",
|
||||||
"url": "/web-animations/animatable/animate.html"
|
"url": "/web-animations/animatable/animate.html"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "web-animations/animation-effect-timing/delay.html",
|
|
||||||
"url": "/web-animations/animation-effect-timing/delay.html"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "web-animations/animation-effect-timing/direction.html",
|
"path": "web-animations/animation-effect-timing/direction.html",
|
||||||
"url": "/web-animations/animation-effect-timing/direction.html"
|
"url": "/web-animations/animation-effect-timing/direction.html"
|
||||||
@ -28421,6 +28417,10 @@
|
|||||||
"path": "web-animations/animation-effect-timing/iterations.html",
|
"path": "web-animations/animation-effect-timing/iterations.html",
|
||||||
"url": "/web-animations/animation-effect-timing/iterations.html"
|
"url": "/web-animations/animation-effect-timing/iterations.html"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "web-animations/animation-model/animation-types/discrete-animation.html",
|
||||||
|
"url": "/web-animations/animation-model/animation-types/discrete-animation.html"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "web-animations/animation-timeline/document-timeline.html",
|
"path": "web-animations/animation-timeline/document-timeline.html",
|
||||||
"url": "/web-animations/animation-timeline/document-timeline.html"
|
"url": "/web-animations/animation-timeline/document-timeline.html"
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Tests for discrete animation</title>
|
||||||
|
<link rel="help" href="http://w3c.github.io/web-animations/#animatable-as-string-section">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="../../testcommon.js"></script>
|
||||||
|
<link rel="stylesheet" href="/resources/testharness.css">
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(function(t) {
|
||||||
|
var div = createDiv(t);
|
||||||
|
|
||||||
|
var anim = div.animate({ fontStyle: [ 'normal', 'italic' ] },
|
||||||
|
{ duration: 1000, fill: 'forwards' });
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'normal',
|
||||||
|
'Animation produces \'from\' value at start of interval');
|
||||||
|
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'normal',
|
||||||
|
'Animation produces \'from\' value just before the middle of'
|
||||||
|
+ ' the interval');
|
||||||
|
anim.currentTime++;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'to\' value at exact middle of'
|
||||||
|
+ ' the interval');
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'to\' value during forwards fill');
|
||||||
|
}, 'Test animating discrete values');
|
||||||
|
|
||||||
|
test(function(t) {
|
||||||
|
var div = createDiv(t);
|
||||||
|
var originalHeight = getComputedStyle(div).height;
|
||||||
|
|
||||||
|
var anim = div.animate({ height: [ 'auto', '200px' ] },
|
||||||
|
{ duration: 1000, fill: 'forwards' });
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).height, originalHeight,
|
||||||
|
'Animation produces \'from\' value at start of interval');
|
||||||
|
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
|
||||||
|
assert_equals(getComputedStyle(div).height, originalHeight,
|
||||||
|
'Animation produces \'from\' value just before the middle of'
|
||||||
|
+ ' the interval');
|
||||||
|
anim.currentTime++;
|
||||||
|
assert_equals(getComputedStyle(div).height, '200px',
|
||||||
|
'Animation produces \'to\' value at exact middle of'
|
||||||
|
+ ' the interval');
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(getComputedStyle(div).height, '200px',
|
||||||
|
'Animation produces \'to\' value during forwards fill');
|
||||||
|
}, 'Test discrete animation is used when interpolation fails');
|
||||||
|
|
||||||
|
test(function(t) {
|
||||||
|
var div = createDiv(t);
|
||||||
|
var originalHeight = getComputedStyle(div).height;
|
||||||
|
|
||||||
|
var anim = div.animate({ height: [ 'auto',
|
||||||
|
'200px',
|
||||||
|
'300px',
|
||||||
|
'auto',
|
||||||
|
'400px' ] },
|
||||||
|
{ duration: 1000, fill: 'forwards' });
|
||||||
|
|
||||||
|
// There are five values, so there are four pairs to try to interpolate.
|
||||||
|
// We test at the middle of each pair.
|
||||||
|
assert_equals(getComputedStyle(div).height, originalHeight,
|
||||||
|
'Animation produces \'from\' value at start of interval');
|
||||||
|
anim.currentTime = 125;
|
||||||
|
assert_equals(getComputedStyle(div).height, '200px',
|
||||||
|
'First non-interpolable pair uses discrete interpolation');
|
||||||
|
anim.currentTime += 250;
|
||||||
|
assert_equals(getComputedStyle(div).height, '250px',
|
||||||
|
'Second interpolable pair uses linear interpolation');
|
||||||
|
anim.currentTime += 250;
|
||||||
|
assert_equals(getComputedStyle(div).height, originalHeight,
|
||||||
|
'Third non-interpolable pair uses discrete interpolation');
|
||||||
|
anim.currentTime += 250;
|
||||||
|
assert_equals(getComputedStyle(div).height, '400px',
|
||||||
|
'Fourth non-interpolable pair uses discrete interpolation');
|
||||||
|
}, 'Test discrete animation is used only for pairs of values that cannot'
|
||||||
|
+ ' be interpolated');
|
||||||
|
|
||||||
|
test(function(t) {
|
||||||
|
var div = createDiv(t);
|
||||||
|
var originalHeight = getComputedStyle(div).height;
|
||||||
|
|
||||||
|
// Easing: http://cubic-bezier.com/#.68,0,1,.01
|
||||||
|
// With this curve, we don't reach the 50% point until about 95% of
|
||||||
|
// the time has expired.
|
||||||
|
var anim = div.animate({ fontStyle: [ 'italic', 'oblique' ] },
|
||||||
|
{ duration: 1000, fill: 'forwards',
|
||||||
|
easing: 'cubic-bezier(0.68,0,1,0.01)' });
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'from\' value at start of interval');
|
||||||
|
anim.currentTime = 940;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'from\' value at 94% of the iteration'
|
||||||
|
+ ' time');
|
||||||
|
anim.currentTime = 960;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'oblique',
|
||||||
|
'Animation produces \'to\' value at 96% of the iteration'
|
||||||
|
+ ' time');
|
||||||
|
}, 'Test the 50% switch point for discrete animation is based on the'
|
||||||
|
+ ' effect easing');
|
||||||
|
|
||||||
|
test(function(t) {
|
||||||
|
var div = createDiv(t);
|
||||||
|
var originalHeight = getComputedStyle(div).height;
|
||||||
|
|
||||||
|
// Easing: http://cubic-bezier.com/#.68,0,1,.01
|
||||||
|
// With this curve, we don't reach the 50% point until about 95% of
|
||||||
|
// the time has expired.
|
||||||
|
var anim = div.animate([ { fontStyle: 'italic',
|
||||||
|
easing: 'cubic-bezier(0.68,0,1,0.01)' },
|
||||||
|
{ fontStyle: 'oblique' } ],
|
||||||
|
{ duration: 1000, fill: 'forwards' });
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'from\' value at start of interval');
|
||||||
|
anim.currentTime = 940;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'italic',
|
||||||
|
'Animation produces \'from\' value at 94% of the iteration'
|
||||||
|
+ ' time');
|
||||||
|
anim.currentTime = 960;
|
||||||
|
assert_equals(getComputedStyle(div).fontStyle, 'oblique',
|
||||||
|
'Animation produces \'to\' value at 96% of the iteration'
|
||||||
|
+ ' time');
|
||||||
|
}, 'Test the 50% switch point for discrete animation is based on the'
|
||||||
|
+ ' keyframe easing');
|
||||||
|
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user