зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1459536 - Allow CSS animation timing properties to be overridden using the Web Animations API; r=boris
Differential Revision: https://phabricator.services.mozilla.com/D65098 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
c6444a7c4f
Коммит
ef42e66fc8
|
@ -45,7 +45,8 @@ class AnimationEffect : public nsISupports, public nsWrapperCache {
|
|||
// AnimationEffect interface
|
||||
void GetTiming(EffectTiming& aRetVal) const;
|
||||
void GetComputedTimingAsDict(ComputedEffectTiming& aRetVal) const;
|
||||
void UpdateTiming(const OptionalEffectTiming& aTiming, ErrorResult& aRv);
|
||||
virtual void UpdateTiming(const OptionalEffectTiming& aTiming,
|
||||
ErrorResult& aRv);
|
||||
|
||||
const TimingParams& SpecifiedTiming() const { return mTiming; }
|
||||
void SetSpecifiedTiming(TimingParams&& aTiming);
|
||||
|
|
|
@ -154,6 +154,10 @@ struct TimingParams {
|
|||
mDuration = std::move(aDuration);
|
||||
Update();
|
||||
}
|
||||
void SetDuration(const Maybe<StickyTimeDuration>& aDuration) {
|
||||
mDuration = aDuration;
|
||||
Update();
|
||||
}
|
||||
const Maybe<StickyTimeDuration>& Duration() const { return mDuration; }
|
||||
|
||||
void SetDelay(const TimeDuration& aDelay) {
|
||||
|
|
|
@ -290,6 +290,36 @@ void CSSAnimation::UpdateTiming(SeekFlag aSeekFlag,
|
|||
|
||||
/////////////////////// CSSAnimationKeyframeEffect ////////////////////////
|
||||
|
||||
void CSSAnimationKeyframeEffect::UpdateTiming(
|
||||
const OptionalEffectTiming& aTiming, ErrorResult& aRv) {
|
||||
KeyframeEffect::UpdateTiming(aTiming, aRv);
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mAnimation && mAnimation->AsCSSAnimation()) {
|
||||
CSSAnimationProperties updatedProperties = CSSAnimationProperties::None;
|
||||
if (aTiming.mDuration.WasPassed()) {
|
||||
updatedProperties |= CSSAnimationProperties::Duration;
|
||||
}
|
||||
if (aTiming.mIterations.WasPassed()) {
|
||||
updatedProperties |= CSSAnimationProperties::IterationCount;
|
||||
}
|
||||
if (aTiming.mDirection.WasPassed()) {
|
||||
updatedProperties |= CSSAnimationProperties::Direction;
|
||||
}
|
||||
if (aTiming.mDelay.WasPassed()) {
|
||||
updatedProperties |= CSSAnimationProperties::Delay;
|
||||
}
|
||||
if (aTiming.mFill.WasPassed()) {
|
||||
updatedProperties |= CSSAnimationProperties::FillMode;
|
||||
}
|
||||
|
||||
mAnimation->AsCSSAnimation()->AddOverriddenProperties(updatedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
void CSSAnimationKeyframeEffect::SetKeyframes(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aKeyframes,
|
||||
ErrorResult& aRv) {
|
||||
|
@ -403,8 +433,27 @@ static void UpdateOldAnimationPropertiesWithNew(
|
|||
// identity (and any expando properties attached to it).
|
||||
if (aOld.GetEffect()) {
|
||||
dom::AnimationEffect* oldEffect = aOld.GetEffect();
|
||||
animationChanged = oldEffect->SpecifiedTiming() != aNewTiming;
|
||||
oldEffect->SetSpecifiedTiming(std::move(aNewTiming));
|
||||
|
||||
// Copy across the changes that are not overridden
|
||||
TimingParams updatedTiming = oldEffect->SpecifiedTiming();
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::Duration) {
|
||||
updatedTiming.SetDuration(aNewTiming.Duration());
|
||||
}
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::IterationCount) {
|
||||
updatedTiming.SetIterations(aNewTiming.Iterations());
|
||||
}
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::Direction) {
|
||||
updatedTiming.SetDirection(aNewTiming.Direction());
|
||||
}
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::Delay) {
|
||||
updatedTiming.SetDelay(aNewTiming.Delay());
|
||||
}
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::FillMode) {
|
||||
updatedTiming.SetFill(aNewTiming.Fill());
|
||||
}
|
||||
|
||||
animationChanged = oldEffect->SpecifiedTiming() != updatedTiming;
|
||||
oldEffect->SetSpecifiedTiming(std::move(updatedTiming));
|
||||
|
||||
KeyframeEffect* oldKeyframeEffect = oldEffect->AsKeyframeEffect();
|
||||
if (~aOverriddenProperties & CSSAnimationProperties::Keyframes &&
|
||||
|
|
|
@ -286,6 +286,8 @@ class CSSAnimationKeyframeEffect : public dom::KeyframeEffect {
|
|||
: KeyframeEffect(aDocument, std::move(aTarget), std::move(aTiming),
|
||||
aOptions) {}
|
||||
|
||||
void UpdateTiming(const dom::OptionalEffectTiming& aTiming,
|
||||
ErrorResult& aRv) override;
|
||||
void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
|
||||
ErrorResult& aRv) override;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>AnimationEffect.updateTiming() for CSS animations</title>
|
||||
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="support/testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes anim-empty { }
|
||||
</style>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
test(t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim-empty 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.effect.updateTiming({ duration: 2 * MS_PER_SEC });
|
||||
|
||||
div.style.animationDuration = '4s';
|
||||
div.style.animationDelay = '6s';
|
||||
getComputedStyle(div).animationDuration;
|
||||
|
||||
assert_equals(
|
||||
animation.effect.getTiming().duration,
|
||||
2 * MS_PER_SEC,
|
||||
'Duration should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().delay,
|
||||
6 * MS_PER_SEC,
|
||||
'Delay should be the value set by style'
|
||||
);
|
||||
}, 'AnimationEffect.updateTiming({ duration }) causes changes to the'
|
||||
+ ' animation-duration to be ignored');
|
||||
|
||||
// The above test covers duration (with delay as a control). The remaining
|
||||
// properties are:
|
||||
//
|
||||
// iterations - animation-iteration-count
|
||||
// direction - animation-direction
|
||||
// delay - animation-delay
|
||||
// fill - animation-fill-mode
|
||||
//
|
||||
// Which we test in two batches, overriding two properties each time and using
|
||||
// the remaining two properties as control values to check they are NOT
|
||||
// overridden.
|
||||
|
||||
test(t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim-empty 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.effect.updateTiming({ iterations: 2, direction: 'reverse' });
|
||||
|
||||
div.style.animationIterationCount = '4';
|
||||
div.style.animationDirection = 'alternate';
|
||||
div.style.animationDelay = '6s';
|
||||
div.style.animationFillMode = 'both';
|
||||
getComputedStyle(div).animationIterationCount;
|
||||
|
||||
assert_equals(
|
||||
animation.effect.getTiming().iterations,
|
||||
2,
|
||||
'Iterations should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().direction,
|
||||
'reverse',
|
||||
'Direction should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().delay,
|
||||
6 * MS_PER_SEC,
|
||||
'Delay should be the value set by style'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().fill,
|
||||
'both',
|
||||
'Fill should be the value set by style'
|
||||
);
|
||||
}, 'AnimationEffect.updateTiming({ iterations, direction }) causes changes to'
|
||||
+ ' the animation-iteration-count and animation-direction to be ignored');
|
||||
|
||||
test(t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim-empty 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.effect.updateTiming({ delay: 2 * MS_PER_SEC, fill: 'both' });
|
||||
|
||||
div.style.animationDelay = '4s';
|
||||
div.style.animationFillMode = 'forwards';
|
||||
div.style.animationIterationCount = '6';
|
||||
div.style.animationDirection = 'reverse';
|
||||
getComputedStyle(div).animationDelay;
|
||||
|
||||
assert_equals(
|
||||
animation.effect.getTiming().delay,
|
||||
2 * MS_PER_SEC,
|
||||
'Delay should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().fill,
|
||||
'both',
|
||||
'Fill should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().iterations,
|
||||
6,
|
||||
'Iterations should be the value set by style'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().direction,
|
||||
'reverse',
|
||||
'Direction should be the value set by style'
|
||||
);
|
||||
}, 'AnimationEffect.updateTiming({ delay, fill }) causes changes to'
|
||||
+ ' the animation-delay and animation-fill-mode to be ignored');
|
||||
|
||||
test(t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim-empty 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
assert_throws_js(TypeError, () => {
|
||||
animation.effect.updateTiming({ duration: 2 * MS_PER_SEC, iterations: -1 });
|
||||
}, 'Negative iteration count should cause an error to be thrown');
|
||||
|
||||
div.style.animationDuration = '4s';
|
||||
getComputedStyle(div).animationDuration;
|
||||
|
||||
assert_equals(
|
||||
animation.effect.getTiming().duration,
|
||||
4 * MS_PER_SEC,
|
||||
'Duration should be the value set by style'
|
||||
);
|
||||
}, 'AnimationEffect.updateTiming() does override to changes from animation-*'
|
||||
+ ' properties if there is an error');
|
||||
|
||||
test(t => {
|
||||
const div = addDiv(t);
|
||||
div.style.animation = 'anim-empty 100s';
|
||||
|
||||
const animation = div.getAnimations()[0];
|
||||
animation.effect.updateTiming({
|
||||
easing: 'steps(4)',
|
||||
endDelay: 2 * MS_PER_SEC,
|
||||
iterationStart: 4,
|
||||
});
|
||||
|
||||
div.style.animationDuration = '6s';
|
||||
div.style.animationTimingFunction = 'ease-in';
|
||||
getComputedStyle(div).animationDuration;
|
||||
|
||||
assert_equals(
|
||||
animation.effect.getTiming().easing,
|
||||
'steps(4)',
|
||||
'endDelay should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().endDelay,
|
||||
2 * MS_PER_SEC,
|
||||
'endDelay should be the value set by the API'
|
||||
);
|
||||
assert_equals(
|
||||
animation.effect.getTiming().iterationStart,
|
||||
4,
|
||||
'iterationStart should be the value set by style'
|
||||
);
|
||||
}, 'AnimationEffect properties that do not map to animation-* properties'
|
||||
+ ' should not be changed when animation-* style is updated');
|
||||
|
||||
</script>
|
||||
</body>
|
Загрузка…
Ссылка в новой задаче