From 3d5e4254709f1a59a43bd3f59bdb03c8a425b3ff Mon Sep 17 00:00:00 2001 From: Mario Nebl Date: Sun, 15 Nov 2015 23:45:29 +0100 Subject: [PATCH] fix: handle timing-function, iteration-count properly - #76 --- documentation/api.md | 20 ----- source/library/get-animation-properties.js | 27 +++++++ source/library/get-keyframes.js | 2 + source/library/get-player.js | 46 ++++++----- source/test/unit/fixtures/filled-animation.js | 8 ++ source/test/unit/fixtures/paused-animation.js | 2 +- .../test/unit/fixtures/running-animation.js | 2 +- source/test/unit/fixtures/slow-animation.js | 2 +- source/test/unit/get-animation-properties.js | 77 +++++++++++++++++++ source/test/unit/index.js | 1 + 10 files changed, 145 insertions(+), 42 deletions(-) create mode 100644 source/library/get-animation-properties.js create mode 100644 source/test/unit/fixtures/filled-animation.js create mode 100644 source/test/unit/get-animation-properties.js diff --git a/documentation/api.md b/documentation/api.md index dee2bc6..64556e7 100644 --- a/documentation/api.md +++ b/documentation/api.md @@ -145,26 +145,6 @@ wheel.play(); Returns **JogWheel** JogWheel instance -# getDefinedStyles - -Gets map of defined styles from CSS2Properties object - -**Parameters** - -- `properties` **CSS2Properties** CSS2Properties object to return defined styles from - -Returns **object** plain object containing defined styles as key value pairs - -# toArray - -Cast array-like objects and collections to Array - -**Parameters** - -- `arrayLike` **Object** array-like to cast to Array - -Returns **Array** Array cast from arrayLike - --- diff --git a/source/library/get-animation-properties.js b/source/library/get-animation-properties.js new file mode 100644 index 0000000..455de4c --- /dev/null +++ b/source/library/get-animation-properties.js @@ -0,0 +1,27 @@ +import {prefix} from './get-vendor-prefix'; + +const propertyNames = [ + 'name', + 'duration', + 'iterationCount', + 'timingFunction', + 'fillMode', + 'playState' +]; + +/** + * Returns applicable animation properties for a given node + * @param {Node} node Node to read animation properties from + * @param {Window} window Global context to use + * @return {Object} Applicable animation properties for node in window + * @private + */ +export default function getAnimationProperties(node, window = global.window, document = global.document) { + const styles = window.getComputedStyle(node); + + return propertyNames.reduce((properties, propertyName) => { + const cssName = `animation${propertyName[0].toUpperCase()}${propertyName.slice(1)}`; + properties[propertyName] = styles[prefix(cssName, window, document)]; + return properties; + }, {}); +} diff --git a/source/library/get-keyframes.js b/source/library/get-keyframes.js index a3d3c54..116c41b 100644 --- a/source/library/get-keyframes.js +++ b/source/library/get-keyframes.js @@ -24,6 +24,7 @@ const empty = []; * Cast array-like objects and collections to Array * @param {Object} arrayLike array-like to cast to Array * @return {Array} Array cast from arrayLike + * @private */ function toArray(arrayLike) { return empty.slice.call(arrayLike); // eslint-disable-line prefer-reflect @@ -95,6 +96,7 @@ export function parseKeyframeKey(keyText) { * Gets map of defined styles from CSS2Properties object * @param {CSS2Properties} properties CSS2Properties object to return defined styles from * @return {object} plain object containing defined styles as key value pairs + * @private */ export function getDefinedStyles(properties) { const styles = {}; diff --git a/source/library/get-player.js b/source/library/get-player.js index b601669..0659a41 100644 --- a/source/library/get-player.js +++ b/source/library/get-player.js @@ -1,5 +1,6 @@ +import {prefix} from './get-vendor-prefix'; import getKeyframes from './get-keyframes'; -import {prefix as getPrefixed} from './get-vendor-prefix'; +import getAnimationProperties from './get-animation-properties'; /** * Converts CSS animation duration string to integer holding duration in milliseconds @@ -14,6 +15,16 @@ function getAnimationDuration(CSSAnimationDuration = '0s') { return parseFloat(duration, 10) * factor; } +/** + * Converts CSS animation iteration count string to integer + * @param CSSIterationCount {string} [CSSIterationCount='1'] CSS animation iteration count + * @return {integer} + * @private + */ +function getAnimationIterations(CSSIterationCount = '1') { + return parseInt(CSSIterationCount, 10); +} + /** * Gets a web animation player based on the currently assigned CSS animation * @param element {HTMLElement} DOM element to scan for an applied CSS animation @@ -23,41 +34,38 @@ function getAnimationDuration(CSSAnimationDuration = '0s') { * @private */ export default function getPlayer(element, window = global.window, document = global.document) { - function prefix(propertyName) { - return getPrefixed(propertyName, window, document); - } - - // TODO: Make this a function, test it // Read all animation related styles from element, respect prefixes const { - [prefix('animationName')]: animationName, - [prefix('animationDuration')]: CSSDuration, - [prefix('animationIterations')]: iterations, - [prefix('animationEasing')]: easing, - [prefix('animationFillMode')]: fill, - [prefix('animationPlayState')]: playState - } = window.getComputedStyle(element); + name, + duration, + iterationCount, + timingFunction, + fillMode, + playState + } = getAnimationProperties(element, window, document); // Generate keyframes based on the assigned animationName - const keyframes = getKeyframes(animationName, window, document); + const keyframes = getKeyframes(name, window, document); // TODO: Should bail/stub? here if no keyframes are found // Construct options for the webanimation player instance const options = { - duration: getAnimationDuration(CSSDuration), - iterations, easing, fill + duration: getAnimationDuration(duration), + iterations: getAnimationIterations(iterationCount), + fill: fillMode, + easing: timingFunction }; - // Sort by offset and remove duplicates // TODO: Test get-keyframes for sorting and duplication + // Sort by offset and remove duplicates keyframes.sort((a, b) => a.offset - b.offset); // Construct webanimation player instance with keyframes and options const player = element.animate(keyframes, options); - element.style[prefix('animationName')] = 'jogwheel-none'; // Detach the former animation to prevent problems with polyfill - player.__jogwheelName = animationName; + element.style[prefix('animationName', window, document)] = `__jogwheelName-${name}`; + player.__jogwheelName = name; // Pause or play the webanimation player instance based on CSS animationPlayState if (playState === 'paused') { diff --git a/source/test/unit/fixtures/filled-animation.js b/source/test/unit/fixtures/filled-animation.js new file mode 100644 index 0000000..9132768 --- /dev/null +++ b/source/test/unit/fixtures/filled-animation.js @@ -0,0 +1,8 @@ +export default { + animationName: 'filled-animation', + animationPlayState: 'running', + animationTimingFunction: 'linear', + animationDuration: '1s', + animationFillMode: 'both', + animationIterationCount: '10' +}; diff --git a/source/test/unit/fixtures/paused-animation.js b/source/test/unit/fixtures/paused-animation.js index f479a01..9d56d7b 100644 --- a/source/test/unit/fixtures/paused-animation.js +++ b/source/test/unit/fixtures/paused-animation.js @@ -1,6 +1,6 @@ export default { animationName: 'default-animation', animationPlayState: 'paused', - animationEasing: 'linear', + animationTimingFunction: 'linear', animationDuration: '300ms' }; diff --git a/source/test/unit/fixtures/running-animation.js b/source/test/unit/fixtures/running-animation.js index 98a8bf3..c2404fc 100644 --- a/source/test/unit/fixtures/running-animation.js +++ b/source/test/unit/fixtures/running-animation.js @@ -1,6 +1,6 @@ export default { animationName: 'default-animation', animationPlayState: 'running', - animationEasing: 'linear', + animationTimingFunction: 'linear', animationDuration: '.3s' }; diff --git a/source/test/unit/fixtures/slow-animation.js b/source/test/unit/fixtures/slow-animation.js index b9cc1a3..8159b17 100644 --- a/source/test/unit/fixtures/slow-animation.js +++ b/source/test/unit/fixtures/slow-animation.js @@ -1,6 +1,6 @@ export default { animationName: 'default-animation', animationPlayState: 'running', - animationEasing: 'linear', + animationTimingFunction: 'linear', animationDuration: '1s' }; diff --git a/source/test/unit/get-animation-properties.js b/source/test/unit/get-animation-properties.js new file mode 100644 index 0000000..bd60e8f --- /dev/null +++ b/source/test/unit/get-animation-properties.js @@ -0,0 +1,77 @@ +import tape from 'tape'; +import getAnimationProperties from '../../library/get-animation-properties.js'; + +import elementStub from './stubs/element.js'; +import windowStub from './stubs/window.js'; +import documentStub from './stubs/document.js'; + +import pausedAnimation from './fixtures/paused-animation'; +import filledAnimation from './fixtures/filled-animation'; + +tape('get-animation-properties', t => { + t.throws(() => { + getAnimationProperties(); + }, 'should throw when called without arguments'); + + t.doesNotThrow(() => { + getAnimationProperties(elementStub, windowStub, documentStub); + }, 'should not throw when called with arguments'); + + const pausedElement = { + ...elementStub, + style: { + ...elementStub.style, + ...pausedAnimation + } + }; + + const pausedProperties = getAnimationProperties(pausedElement, windowStub, documentStub); + + t.deepEqual( + Object.keys(pausedProperties), + [ + 'name', + 'duration', + 'iterationCount', + 'timingFunction', + 'fillMode', + 'playState' + ], + 'should return an object with the expected property values'); + + t.deepEqual( + pausedProperties, + { + name: 'default-animation', + duration: '300ms', + iterationCount: undefined, + timingFunction: 'linear', + fillMode: undefined, + playState: 'paused' + }, + 'should return an object with the expected property values'); + + const filledElement = { + ...elementStub, + style: { + ...elementStub.style, + ...filledAnimation + } + }; + + const filledProperties = getAnimationProperties(filledElement, windowStub, documentStub); + + t.deepEqual( + filledProperties, + { + name: 'filled-animation', + duration: '1s', + iterationCount: '10', + timingFunction: 'linear', + fillMode: 'both', + playState: 'running' + }, + 'should return an object with the expected property values'); + + t.end(); +}); diff --git a/source/test/unit/index.js b/source/test/unit/index.js index 91fa7ee..f697c20 100644 --- a/source/test/unit/index.js +++ b/source/test/unit/index.js @@ -1,5 +1,6 @@ import 'babel-polyfill'; +import './get-animation-properties'; import './get-defined-styles'; import './get-keyframe-declarations'; import './get-keyframes';