1 | import { CssAnimationProperty } from '../core/properties';
|
2 | import { KeyframeAnimationInfo } from '../animation/keyframe-animation';
|
3 | import { timeConverter, animationTimingFunctionConverter } from '../styling/converters';
|
4 | import { transformConverter } from '../styling/style-properties';
|
5 | import { cleanupImportantFlags } from './css-utils';
|
6 | const ANIMATION_PROPERTY_HANDLERS = Object.freeze({
|
7 | 'animation-name': (info, value) => (info.name = value.replace(/['"]/g, '')),
|
8 | 'animation-duration': (info, value) => (info.duration = timeConverter(value)),
|
9 | 'animation-delay': (info, value) => (info.delay = timeConverter(value)),
|
10 | 'animation-timing-function': (info, value) => (info.curve = animationTimingFunctionConverter(value)),
|
11 | 'animation-iteration-count': (info, value) => (info.iterations = value === 'infinite' ? Number.POSITIVE_INFINITY : parseFloat(value)),
|
12 | 'animation-direction': (info, value) => (info.isReverse = value === 'reverse'),
|
13 | 'animation-fill-mode': (info, value) => (info.isForwards = value === 'forwards' || value === 'both'),
|
14 | });
|
15 | export class CssAnimationParser {
|
16 | static keyframeAnimationsFromCSSDeclarations(declarations) {
|
17 | if (declarations === null || declarations === undefined) {
|
18 | return undefined;
|
19 | }
|
20 | const animations = new Array();
|
21 | let animationInfo = undefined;
|
22 | declarations.forEach(({ property, value }) => {
|
23 | if (property === 'animation') {
|
24 | keyframeAnimationsFromCSSProperty(value, animations);
|
25 | }
|
26 | else {
|
27 | const propertyHandler = ANIMATION_PROPERTY_HANDLERS[property];
|
28 | if (propertyHandler) {
|
29 | if (animationInfo === undefined) {
|
30 | animationInfo = new KeyframeAnimationInfo();
|
31 | animations.push(animationInfo);
|
32 | }
|
33 | propertyHandler(animationInfo, value);
|
34 | }
|
35 | }
|
36 | });
|
37 | return animations.length === 0 ? undefined : animations;
|
38 | }
|
39 | static keyframesArrayFromCSS(keyframes) {
|
40 | const parsedKeyframes = new Array();
|
41 | for (const keyframe of keyframes) {
|
42 | const declarations = parseKeyframeDeclarations(keyframe.declarations);
|
43 | for (let time of keyframe.values) {
|
44 | if (time === 'from') {
|
45 | time = 0;
|
46 | }
|
47 | else if (time === 'to') {
|
48 | time = 1;
|
49 | }
|
50 | else {
|
51 | time = parseFloat(time) / 100;
|
52 | if (time < 0) {
|
53 | time = 0;
|
54 | }
|
55 | if (time > 100) {
|
56 | time = 100;
|
57 | }
|
58 | }
|
59 | let current = parsedKeyframes[time];
|
60 | if (current === undefined) {
|
61 | current = {};
|
62 | current.duration = time;
|
63 | current.declarations = [];
|
64 | parsedKeyframes[time] = current;
|
65 | }
|
66 | for (const declaration of keyframe.declarations) {
|
67 | if (declaration.property === 'animation-timing-function') {
|
68 | current.curve = animationTimingFunctionConverter(declaration.value);
|
69 | }
|
70 | }
|
71 | current.declarations = current.declarations.concat(declarations);
|
72 | }
|
73 | }
|
74 | const array = [];
|
75 | for (const parsedKeyframe in parsedKeyframes) {
|
76 | array.push(parsedKeyframes[parsedKeyframe]);
|
77 | }
|
78 | array.sort(function (a, b) {
|
79 | return a.duration - b.duration;
|
80 | });
|
81 | return array;
|
82 | }
|
83 | }
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 | export function keyframeAnimationsFromCSSProperty(value, animations) {
|
92 | if (typeof value !== 'string') {
|
93 | return;
|
94 | }
|
95 | if (value.trim().length === 0) {
|
96 | return;
|
97 | }
|
98 | |
99 |
|
100 |
|
101 | const VALUE_SPLIT_RE = /\s(?![^(]*\))/;
|
102 | |
103 |
|
104 |
|
105 | const MULTIPLE_SPLIT_RE = /,(?![^(]*\))/;
|
106 | const isTime = (v) => !!v.match(/\dm?s$/g);
|
107 | const isTimingFunction = (v) => !!v.match(/ease|linear|ease-in|ease-out|ease-in-out|spring|cubic-bezier/g);
|
108 | const isIterationCount = (v) => !!v.match(/infinite|[\d.]+$/g);
|
109 | const isDirection = (v) => !!v.match(/normal|reverse|alternate|alternate-reverse/g);
|
110 | const isFillMode = (v) => !!v.match(/none|forwards|backwards|both/g);
|
111 | const isPlayState = (v) => !!v.match(/running|paused/g);
|
112 | const values = value.split(MULTIPLE_SPLIT_RE);
|
113 | for (const parsedValue of values) {
|
114 | const animationInfo = new KeyframeAnimationInfo();
|
115 | const parts = parsedValue.trim().split(VALUE_SPLIT_RE);
|
116 | const [duration, delay] = parts.filter(isTime);
|
117 | const [timing] = parts.filter(isTimingFunction);
|
118 | const [iterationCount] = parts.filter(isIterationCount);
|
119 | const [direction] = parts.filter(isDirection);
|
120 | const [fillMode] = parts.filter(isFillMode);
|
121 | const [playState] = parts.filter(isPlayState);
|
122 | const [name] = parts.filter((v) => {
|
123 |
|
124 | return ![duration, delay, timing, iterationCount, direction, fillMode, playState].filter(Boolean).includes(v);
|
125 | });
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 |
|
136 | if (duration) {
|
137 | ANIMATION_PROPERTY_HANDLERS['animation-duration'](animationInfo, duration);
|
138 | }
|
139 | if (delay) {
|
140 | ANIMATION_PROPERTY_HANDLERS['animation-delay'](animationInfo, delay);
|
141 | }
|
142 | if (timing) {
|
143 | ANIMATION_PROPERTY_HANDLERS['animation-timing-function'](animationInfo, timing);
|
144 | }
|
145 | if (iterationCount) {
|
146 | ANIMATION_PROPERTY_HANDLERS['animation-iteration-count'](animationInfo, iterationCount);
|
147 | }
|
148 | if (direction) {
|
149 | ANIMATION_PROPERTY_HANDLERS['animation-direction'](animationInfo, direction);
|
150 | }
|
151 | if (fillMode) {
|
152 | ANIMATION_PROPERTY_HANDLERS['animation-fill-mode'](animationInfo, fillMode);
|
153 | }
|
154 | if (playState) {
|
155 |
|
156 | }
|
157 | if (name) {
|
158 | ANIMATION_PROPERTY_HANDLERS['animation-name'](animationInfo, name);
|
159 | }
|
160 | else {
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | }
|
166 | animations.push(animationInfo);
|
167 | }
|
168 | }
|
169 | export function parseKeyframeDeclarations(unparsedKeyframeDeclarations) {
|
170 | const declarations = unparsedKeyframeDeclarations.reduce((declarations, { property: unparsedProperty, value: unparsedValue }) => {
|
171 | const property = CssAnimationProperty._getByCssName(unparsedProperty);
|
172 | unparsedValue = cleanupImportantFlags(unparsedValue, property?.cssLocalName);
|
173 | if (typeof unparsedProperty === 'string' && property?._valueConverter) {
|
174 | declarations[property.name] = property._valueConverter(unparsedValue);
|
175 | }
|
176 | else if (unparsedProperty === 'transform') {
|
177 | const transformations = transformConverter(unparsedValue);
|
178 | Object.assign(declarations, transformations);
|
179 | }
|
180 | return declarations;
|
181 | }, {});
|
182 | return Object.keys(declarations).map((property) => ({
|
183 | property,
|
184 | value: declarations[property],
|
185 | }));
|
186 | }
|
187 |
|
\ | No newline at end of file |