Skip to content

Commit

Permalink
amp-animation: polyfill partial keyframes from CSS (ampproject#9689)
Browse files Browse the repository at this point in the history
  • Loading branch information
aghassemi authored and Aaron Turner committed Jun 6, 2017
1 parent c3f75ab commit d83d9f4
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 25 deletions.
48 changes: 32 additions & 16 deletions extensions/amp-animation/0.1/test/test-web-animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,25 @@ describes.realWin('MeasureScanner', {amp: 1}, env => {
return scan(def)[0].timing;
}

function writeAndWaitForStyleKeyframes(name, css) {
const style = doc.createElement('style');
style.setAttribute('amp-custom', '');
style.textContent =
`@-ms-keyframes ${name} {${css}}` +
`@-moz-keyframes ${name} {${css}}` +
`@-webkit-keyframes ${name} {${css}}` +
`@keyframes ${name} {${css}}`;
doc.head.appendChild(style);
return poll('wait for style', () => {
for (let i = 0; i < doc.styleSheets.length; i++) {
if (doc.styleSheets[i].ownerNode == style) {
return true;
}
}
return false;
});
}

it('should parse/validate timing duration', () => {
expect(scanTiming({}).duration).to.equal(0);
expect(scanTiming({duration: 0}).duration).to.equal(0);
Expand Down Expand Up @@ -598,22 +617,7 @@ describes.realWin('MeasureScanner', {amp: 1}, env => {
it('should discover style keyframes', () => {
const name = 'keyframes1';
const css = 'from{opacity: 0} to{opacity: 1}';
const style = doc.createElement('style');
style.setAttribute('amp-custom', '');
style.textContent =
`@-ms-keyframes ${name} {${css}}` +
`@-moz-keyframes ${name} {${css}}` +
`@-webkit-keyframes ${name} {${css}}` +
`@keyframes ${name} {${css}}`;
doc.head.appendChild(style);
return poll('wait for style', () => {
for (let i = 0; i < doc.styleSheets.length; i++) {
if (doc.styleSheets[i].ownerNode == style) {
return true;
}
}
return false;
}).then(() => {
return writeAndWaitForStyleKeyframes(name, css).then(() => {
const keyframes = scan({target: target1, keyframes: name})[0].keyframes;
expect(keyframes).to.jsonEqual([
{offset: 0, opacity: '0'},
Expand All @@ -622,6 +626,18 @@ describes.realWin('MeasureScanner', {amp: 1}, env => {
});
});

it('should polyfill partial style keyframes', () => {
const name = 'keyframes2';
const css = 'to{opacity: 0}';
return writeAndWaitForStyleKeyframes(name, css).then(() => {
const keyframes = scan({target: target1, keyframes: name})[0].keyframes;
expect(keyframes).to.jsonEqual([
{opacity: '1'},
{offset: 1, opacity: '0'},
]);
});
});

it('should check media in top animation', () => {
const requests = scan({
duration: 500,
Expand Down
19 changes: 10 additions & 9 deletions extensions/amp-animation/0.1/web-animations.js
Original file line number Diff line number Diff line change
Expand Up @@ -584,21 +584,22 @@ export class MeasureScanner extends Scanner {
* @private
*/
createKeyframes_(target, spec) {
if (typeof spec.keyframes == 'string') {
let specKeyframes = spec.keyframes;
if (typeof specKeyframes == 'string') {
// Keyframes name to be extracted from `<style>`.
const keyframes = extractKeyframes(this.css_.rootNode_, spec.keyframes);
const keyframes = extractKeyframes(this.css_.rootNode_, specKeyframes);
user().assert(keyframes,
`Keyframes not found in stylesheet: "${spec.keyframes}"`);
return /** @type {!WebKeyframesDef} */ (keyframes);
`Keyframes not found in stylesheet: "${specKeyframes}"`);
specKeyframes = keyframes;
}

if (isObject(spec.keyframes)) {
if (isObject(specKeyframes)) {
// Property -> keyframes form.
// The object is cloned, while properties are verified to be
// whitelisted. Additionally, the `offset:0` frames are inserted
// to polyfill partial keyframes per spec.
// See https://github.com/w3c/web-animations/issues/187
const object = /** {!Object<string, *>} */ (spec.keyframes);
const object = /** {!Object<string, *>} */ (specKeyframes);
/** @type {!WebKeyframesDef} */
const keyframes = {};
for (const prop in object) {
Expand All @@ -620,14 +621,14 @@ export class MeasureScanner extends Scanner {
return keyframes;
}

if (isArray(spec.keyframes) && spec.keyframes.length > 0) {
if (isArray(specKeyframes) && specKeyframes.length > 0) {
// Keyframes -> property form.
// The array is cloned, while properties are verified to be whitelisted.
// Additionally, if the `offset:0` properties are inserted when absent
// to polyfill partial keyframes per spec.
// See https://github.com/w3c/web-animations/issues/187 and
// https://github.com/web-animations/web-animations-js/issues/14
const array = /** {!Array<!Object<string, *>>} */ (spec.keyframes);
const array = /** {!Array<!Object<string, *>>} */ (specKeyframes);
/** @type {!WebKeyframesDef} */
const keyframes = [];
const addStartFrame = array.length == 1 || array[0].offset > 0;
Expand All @@ -654,7 +655,7 @@ export class MeasureScanner extends Scanner {

// TODO(dvoytenko): support CSS keyframes per https://github.com/w3c/web-animations/issues/189
// Unknown form of keyframes spec.
throw user().createError('keyframes not found', spec.keyframes);
throw user().createError('keyframes not found', specKeyframes);
}

/** @override */
Expand Down

0 comments on commit d83d9f4

Please sign in to comment.