242 lines
6.8 KiB
HTML
242 lines
6.8 KiB
HTML
<!doctype html>
|
|
<meta charset=utf-8>
|
|
<title>KeyframeEffect interface: style change events</title>
|
|
<link rel="help"
|
|
href="https://drafts.csswg.org/web-animations-1/#model-liveness">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="../../testcommon.js"></script>
|
|
<body>
|
|
<div id="log"></div>
|
|
<script>
|
|
'use strict';
|
|
|
|
// Test that each property defined in the KeyframeEffect interface does not
|
|
// produce style change events.
|
|
//
|
|
// There are two types of tests:
|
|
//
|
|
// MakeInEffectTest
|
|
//
|
|
// For properties that are able to cause the KeyframeEffect to start
|
|
// affecting the CSS 'opacity' property.
|
|
//
|
|
// This function takes either:
|
|
//
|
|
// (a) A function that makes the passed-in KeyframeEffect affect the
|
|
// 'opacity' property.
|
|
//
|
|
// (b) An object with the following format:
|
|
//
|
|
// {
|
|
// setup: elem => { /* return Animation */ }
|
|
// test: effect => { /* make |effect| affect 'opacity' */ }
|
|
// }
|
|
//
|
|
// If the latter form is used, the setup function should return an Animation
|
|
// whose KeyframeEffect does NOT (yet) affect the 'opacity' property (or is
|
|
// NOT yet in-effect). Otherwise, the transition we use to detect if a style
|
|
// change event has occurred will never have a chance to be triggered (since
|
|
// the animated style will clobber both before-change and after-change
|
|
// style).
|
|
//
|
|
// UsePropertyTest
|
|
//
|
|
// For properties that cannot cause the KeyframeEffect to start affecting the
|
|
// CSS 'opacity' property.
|
|
//
|
|
// The shape of the parameter to the UsePropertyTest is identical to the
|
|
// MakeInEffectTest. The only difference is that the function (or 'test'
|
|
// function of the object format is used) does not need to make the
|
|
// KeyframeEffect affect the CSS 'opacity' property, but simply needs to
|
|
// get/set the property under test.
|
|
|
|
const MakeInEffectTest = testFuncOrObj => {
|
|
let test, setup;
|
|
|
|
if (typeof testFuncOrObj === 'function') {
|
|
test = testFuncOrObj;
|
|
} else {
|
|
test = testFuncOrObj.test;
|
|
if (typeof testFuncOrObj.setup === 'function') {
|
|
setup = testFuncOrObj.setup;
|
|
}
|
|
}
|
|
|
|
if (!setup) {
|
|
setup = elem =>
|
|
elem.animate({ color: ['blue', 'green'] }, 100 * MS_PER_SEC);
|
|
}
|
|
|
|
return { test, setup };
|
|
};
|
|
|
|
const UsePropertyTest = testFuncOrObj => {
|
|
const { test, setup } = MakeInEffectTest(testFuncOrObj);
|
|
|
|
let coveringAnimation;
|
|
return {
|
|
setup: elem => {
|
|
coveringAnimation = new Animation(
|
|
new KeyframeEffect(elem, { opacity: [0, 1] }, 100 * MS_PER_SEC)
|
|
);
|
|
|
|
return setup(elem);
|
|
},
|
|
test: effect => {
|
|
test(effect);
|
|
coveringAnimation.play();
|
|
},
|
|
};
|
|
};
|
|
|
|
const tests = {
|
|
getTiming: UsePropertyTest(effect => effect.getTiming()),
|
|
getComputedTiming: UsePropertyTest(effect => effect.getComputedTiming()),
|
|
updateTiming: MakeInEffectTest({
|
|
// Initially put the effect in its before phase (with no fill mode)...
|
|
setup: elem =>
|
|
elem.animate(
|
|
{ opacity: [0.5, 1] },
|
|
{
|
|
duration: 100 * MS_PER_SEC,
|
|
delay: 100 * MS_PER_SEC,
|
|
}
|
|
),
|
|
// ... so that when the delay is removed, it begins to affect the opacity.
|
|
test: effect => {
|
|
effect.updateTiming({ delay: 0 });
|
|
},
|
|
}),
|
|
get target() {
|
|
let targetElem;
|
|
return MakeInEffectTest({
|
|
setup: (elem, t) => {
|
|
targetElem = elem;
|
|
const targetB = createDiv(t);
|
|
return targetB.animate({ opacity: [0.5, 1] }, 100 * MS_PER_SEC);
|
|
},
|
|
test: effect => {
|
|
effect.target = targetElem;
|
|
},
|
|
});
|
|
},
|
|
pseudoElement: MakeInEffectTest({
|
|
setup: elem => elem.animate(
|
|
{opacity: [0.5, 1]},
|
|
{duration: 100 * MS_PER_SEC, pseudoElement: '::before'}
|
|
),
|
|
test: effect => {
|
|
effect.pseudoElement = null;
|
|
},
|
|
}),
|
|
iterationComposite: UsePropertyTest(effect => {
|
|
// Get iterationComposite
|
|
effect.iterationComposite;
|
|
|
|
// Set iterationComposite
|
|
effect.iterationComposite = 'accumulate';
|
|
}),
|
|
composite: UsePropertyTest(effect => {
|
|
// Get composite
|
|
effect.composite;
|
|
|
|
// Set composite
|
|
effect.composite = 'add';
|
|
}),
|
|
getKeyframes: UsePropertyTest(effect => effect.getKeyframes()),
|
|
setKeyframes: MakeInEffectTest(effect =>
|
|
effect.setKeyframes({ opacity: [0.5, 1] })
|
|
),
|
|
get ['KeyframeEffect constructor']() {
|
|
let originalElem;
|
|
let animation;
|
|
return UsePropertyTest({
|
|
setup: elem => {
|
|
originalElem = elem;
|
|
// Return a dummy animation so the caller has something to wait on
|
|
return elem.animate(null);
|
|
},
|
|
test: () =>
|
|
new KeyframeEffect(
|
|
originalElem,
|
|
{ opacity: [0.5, 1] },
|
|
100 * MS_PER_SEC
|
|
),
|
|
});
|
|
},
|
|
get ['KeyframeEffect copy constructor']() {
|
|
let effectToClone;
|
|
return UsePropertyTest({
|
|
setup: elem => {
|
|
effectToClone = new KeyframeEffect(
|
|
elem,
|
|
{ opacity: [0.5, 1] },
|
|
100 * MS_PER_SEC
|
|
);
|
|
// Return a dummy animation so the caller has something to wait on
|
|
return elem.animate(null);
|
|
},
|
|
test: () => new KeyframeEffect(effectToClone),
|
|
});
|
|
},
|
|
};
|
|
|
|
// Check that each enumerable property and the constructors follow the
|
|
// expected behavior with regards to triggering style change events.
|
|
const properties = [
|
|
...Object.keys(AnimationEffect.prototype),
|
|
...Object.keys(KeyframeEffect.prototype),
|
|
'KeyframeEffect constructor',
|
|
'KeyframeEffect copy constructor',
|
|
];
|
|
|
|
test(() => {
|
|
for (const property of Object.keys(tests)) {
|
|
assert_in_array(
|
|
property,
|
|
properties,
|
|
`Test property '${property}' should be one of the properties on ` +
|
|
' KeyframeEffect'
|
|
);
|
|
}
|
|
}, 'All property keys are recognized');
|
|
|
|
for (const key of properties) {
|
|
promise_test(async t => {
|
|
assert_own_property(tests, key, `Should have a test for '${key}' property`);
|
|
const { setup, test } = tests[key];
|
|
|
|
// Setup target element
|
|
const div = createDiv(t);
|
|
let gotTransition = false;
|
|
div.addEventListener('transitionrun', () => {
|
|
gotTransition = true;
|
|
});
|
|
|
|
// Setup animation
|
|
const animation = setup(div, t);
|
|
|
|
// Setup transition start point
|
|
div.style.transition = 'opacity 100s';
|
|
getComputedStyle(div).opacity;
|
|
|
|
// Update specified style but don't flush
|
|
div.style.opacity = '0.5';
|
|
|
|
// Trigger the property
|
|
test(animation.effect);
|
|
|
|
// If the test function produced a style change event it will have triggered
|
|
// a transition.
|
|
|
|
// Wait for the animation to start and then for at least one animation
|
|
// frame to give the transitionrun event a chance to be dispatched.
|
|
await animation.ready;
|
|
await waitForAnimationFrames(1);
|
|
|
|
assert_false(gotTransition, 'A transition should NOT have been triggered');
|
|
}, `KeyframeEffect.${key} does NOT trigger a style change event`);
|
|
}
|
|
</script>
|
|
</body>
|