import { CssAnimationProperty } from "../core/properties"; import { KeyframeAnimationInfo, KeyframeDeclaration, KeyframeInfo, UnparsedKeyframe, } from "../animation/keyframe-animation"; import { timeConverter, animationTimingFunctionConverter } from "../styling/converters"; import { transformConverter } from "../styling/style-properties"; const ANIMATION_PROPERTY_HANDLERS = Object.freeze({ "animation-name": (info, value) => info.name = value, "animation-duration": (info, value) => info.duration = timeConverter(value), "animation-delay": (info, value) => info.delay = timeConverter(value), "animation-timing-function": (info, value) => info.curve = animationTimingFunctionConverter(value), "animation-iteration-count": (info, value) => info.iterations = value === "infinite" ? Number.MAX_VALUE : parseFloat(value), "animation-direction": (info, value) => info.isReverse = value === "reverse", "animation-fill-mode": (info, value) => info.isForwards = value === "forwards" }); export class CssAnimationParser { public static keyframeAnimationsFromCSSDeclarations(declarations: KeyframeDeclaration[]) : KeyframeAnimationInfo[] { if (declarations === null || declarations === undefined) { return undefined; } let animations = new Array(); let animationInfo: KeyframeAnimationInfo = undefined; declarations.forEach(({ property, value }) => { if (property === "animation") { keyframeAnimationsFromCSSProperty(value, animations); } else { const propertyHandler = ANIMATION_PROPERTY_HANDLERS[property]; if (propertyHandler) { if (animationInfo === undefined) { animationInfo = new KeyframeAnimationInfo(); animations.push(animationInfo); } propertyHandler(animationInfo, value); } } }); return animations.length === 0 ? undefined : animations; } public static keyframesArrayFromCSS(keyframes: UnparsedKeyframe[]): KeyframeInfo[] { let parsedKeyframes = new Array(); for (let keyframe of keyframes) { let declarations = parseKeyframeDeclarations(keyframe.declarations); for (let time of keyframe.values) { if (time === "from") { time = 0; } else if (time === "to") { time = 1; } else { time = parseFloat(time) / 100; if (time < 0) { time = 0; } if (time > 100) { time = 100; } } let current = parsedKeyframes[time]; if (current === undefined) { current = {}; current.duration = time; parsedKeyframes[time] = current; } for (let declaration of keyframe.declarations) { if (declaration.property === "animation-timing-function") { current.curve = animationTimingFunctionConverter(declaration.value); } } current.declarations = declarations; } } let array = new Array(); for (let parsedKeyframe in parsedKeyframes) { array.push(parsedKeyframes[parsedKeyframe]); } array.sort(function (a, b) { return a.duration - b.duration; }); return array; } } function keyframeAnimationsFromCSSProperty(value: any, animations: KeyframeAnimationInfo[]) { if (typeof value === "string") { let values = value.split(/[,]+/); for (let parsedValue of values) { let animationInfo = new KeyframeAnimationInfo(); let arr = (parsedValue).trim().split(/[ ]+/); if (arr.length > 0) { animationInfo.name = arr[0]; } if (arr.length > 1) { animationInfo.duration = timeConverter(arr[1]); } if (arr.length > 2) { animationInfo.curve = animationTimingFunctionConverter(arr[2]); } if (arr.length > 3) { animationInfo.delay = timeConverter(arr[3]); } if (arr.length > 4) { animationInfo.iterations = parseInt(arr[4]); } if (arr.length > 5) { animationInfo.isReverse = arr[4] === "reverse"; } if (arr.length > 6) { animationInfo.isForwards = arr[5] === "forwards"; } if (arr.length > 7) { throw new Error("Invalid value for animation: " + value); } animations.push(animationInfo); } } } export function parseKeyframeDeclarations(unparsedKeyframeDeclarations: KeyframeDeclaration[]): KeyframeDeclaration[] { const declarations = unparsedKeyframeDeclarations .reduce((declarations, { property: unparsedProperty, value: unparsedValue }) => { const property = CssAnimationProperty._getByCssName(unparsedProperty); if (typeof unparsedProperty === "string" && property && property._valueConverter) { declarations[property.name] = property._valueConverter(unparsedValue); } else if (typeof unparsedValue === "string" && unparsedProperty === "transform") { const transformations = transformConverter(unparsedValue); Object.assign(declarations, transformations); } return declarations; }, {}); return Object.keys(declarations).map(property => ({ property, value: declarations[property] })); }