UNPKG

web-animations-js

Version:

JavaScript implementation of the Web Animations API

322 lines (292 loc) 8.97 kB
// Copyright 2014 Google Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. (function(shared, testing) { var shorthandToLonghand = { background: [ 'backgroundImage', 'backgroundPosition', 'backgroundSize', 'backgroundRepeat', 'backgroundAttachment', 'backgroundOrigin', 'backgroundClip', 'backgroundColor' ], border: [ 'borderTopColor', 'borderTopStyle', 'borderTopWidth', 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderBottomColor', 'borderBottomStyle', 'borderBottomWidth', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth' ], borderBottom: [ 'borderBottomWidth', 'borderBottomStyle', 'borderBottomColor' ], borderColor: [ 'borderTopColor', 'borderRightColor', 'borderBottomColor', 'borderLeftColor' ], borderLeft: [ 'borderLeftWidth', 'borderLeftStyle', 'borderLeftColor' ], borderRadius: [ 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius' ], borderRight: [ 'borderRightWidth', 'borderRightStyle', 'borderRightColor' ], borderTop: [ 'borderTopWidth', 'borderTopStyle', 'borderTopColor' ], borderWidth: [ 'borderTopWidth', 'borderRightWidth', 'borderBottomWidth', 'borderLeftWidth' ], flex: [ 'flexGrow', 'flexShrink', 'flexBasis' ], font: [ 'fontFamily', 'fontSize', 'fontStyle', 'fontVariant', 'fontWeight', 'lineHeight' ], margin: [ 'marginTop', 'marginRight', 'marginBottom', 'marginLeft' ], outline: [ 'outlineColor', 'outlineStyle', 'outlineWidth' ], padding: [ 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingLeft' ] }; var shorthandExpanderElem = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); var borderWidthAliases = { thin: '1px', medium: '3px', thick: '5px' }; var aliases = { borderBottomWidth: borderWidthAliases, borderLeftWidth: borderWidthAliases, borderRightWidth: borderWidthAliases, borderTopWidth: borderWidthAliases, fontSize: { 'xx-small': '60%', 'x-small': '75%', 'small': '89%', 'medium': '100%', 'large': '120%', 'x-large': '150%', 'xx-large': '200%' }, fontWeight: { normal: '400', bold: '700' }, outlineWidth: borderWidthAliases, textShadow: { none: '0px 0px 0px transparent' }, boxShadow: { none: '0px 0px 0px 0px transparent' } }; function antiAlias(property, value) { if (property in aliases) { return aliases[property][value] || value; } return value; } function isNotAnimatable(property) { // https://drafts.csswg.org/web-animations/#concept-not-animatable return property === 'display' || property.lastIndexOf('animation', 0) === 0 || property.lastIndexOf('transition', 0) === 0; } // This delegates parsing shorthand value syntax to the browser. function expandShorthandAndAntiAlias(property, value, result) { if (isNotAnimatable(property)) { return; } var longProperties = shorthandToLonghand[property]; if (longProperties) { shorthandExpanderElem.style[property] = value; for (var i in longProperties) { var longProperty = longProperties[i]; var longhandValue = shorthandExpanderElem.style[longProperty]; result[longProperty] = antiAlias(longProperty, longhandValue); } } else { result[property] = antiAlias(property, value); } }; function convertToArrayForm(effectInput) { var normalizedEffectInput = []; for (var property in effectInput) { if (property in ['easing', 'offset', 'composite']) { continue; } var values = effectInput[property]; if (!Array.isArray(values)) { values = [values]; } var keyframe; var numKeyframes = values.length; for (var i = 0; i < numKeyframes; i++) { keyframe = {}; if ('offset' in effectInput) { keyframe.offset = effectInput.offset; } else if (numKeyframes == 1) { keyframe.offset = 1.0; } else { keyframe.offset = i / (numKeyframes - 1.0); } if ('easing' in effectInput) { keyframe.easing = effectInput.easing; } if ('composite' in effectInput) { keyframe.composite = effectInput.composite; } keyframe[property] = values[i]; normalizedEffectInput.push(keyframe); } } normalizedEffectInput.sort(function(a, b) { return a.offset - b.offset; }); return normalizedEffectInput; }; function normalizeKeyframes(effectInput) { if (effectInput == null) { return []; } if (window.Symbol && Symbol.iterator && Array.prototype.from && effectInput[Symbol.iterator]) { // Handle custom iterables in most browsers by converting to an array effectInput = Array.from(effectInput); } if (!Array.isArray(effectInput)) { effectInput = convertToArrayForm(effectInput); } var keyframes = effectInput.map(function(originalKeyframe) { var keyframe = {}; for (var member in originalKeyframe) { var memberValue = originalKeyframe[member]; if (member == 'offset') { if (memberValue != null) { memberValue = Number(memberValue); if (!isFinite(memberValue)) throw new TypeError('Keyframe offsets must be numbers.'); if (memberValue < 0 || memberValue > 1) throw new TypeError('Keyframe offsets must be between 0 and 1.'); } } else if (member == 'composite') { if (memberValue == 'add' || memberValue == 'accumulate') { throw { type: DOMException.NOT_SUPPORTED_ERR, name: 'NotSupportedError', message: 'add compositing is not supported' }; } else if (memberValue != 'replace') { throw new TypeError('Invalid composite mode ' + memberValue + '.'); } } else if (member == 'easing') { memberValue = shared.normalizeEasing(memberValue); } else { memberValue = '' + memberValue; } expandShorthandAndAntiAlias(member, memberValue, keyframe); } if (keyframe.offset == undefined) keyframe.offset = null; if (keyframe.easing == undefined) keyframe.easing = 'linear'; return keyframe; }); var everyFrameHasOffset = true; var looselySortedByOffset = true; var previousOffset = -Infinity; for (var i = 0; i < keyframes.length; i++) { var offset = keyframes[i].offset; if (offset != null) { if (offset < previousOffset) { throw new TypeError('Keyframes are not loosely sorted by offset. Sort or specify offsets.'); } previousOffset = offset; } else { everyFrameHasOffset = false; } } keyframes = keyframes.filter(function(keyframe) { return keyframe.offset >= 0 && keyframe.offset <= 1; }); function spaceKeyframes() { var length = keyframes.length; if (keyframes[length - 1].offset == null) keyframes[length - 1].offset = 1; if (length > 1 && keyframes[0].offset == null) keyframes[0].offset = 0; var previousIndex = 0; var previousOffset = keyframes[0].offset; for (var i = 1; i < length; i++) { var offset = keyframes[i].offset; if (offset != null) { for (var j = 1; j < i - previousIndex; j++) keyframes[previousIndex + j].offset = previousOffset + (offset - previousOffset) * j / (i - previousIndex); previousIndex = i; previousOffset = offset; } } } if (!everyFrameHasOffset) spaceKeyframes(); return keyframes; } shared.convertToArrayForm = convertToArrayForm; shared.normalizeKeyframes = normalizeKeyframes; if (WEB_ANIMATIONS_TESTING) { testing.normalizeKeyframes = normalizeKeyframes; } })(webAnimationsShared, webAnimationsTesting);