mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-11-05 13:26:48 +08:00
fix(core): CSS animation parsing (#10245)
This commit is contained in:
@@ -7,13 +7,13 @@ import { transformConverter } from '../styling/style-properties';
|
||||
import { cleanupImportantFlags } from './css-utils';
|
||||
|
||||
const ANIMATION_PROPERTY_HANDLERS = Object.freeze({
|
||||
'animation-name': (info: any, value: any) => (info.name = value),
|
||||
'animation-name': (info: any, value: any) => (info.name = value.replace(/['"]/g, '')),
|
||||
'animation-duration': (info: any, value: any) => (info.duration = timeConverter(value)),
|
||||
'animation-delay': (info: any, value: any) => (info.delay = timeConverter(value)),
|
||||
'animation-timing-function': (info: any, value: any) => (info.curve = animationTimingFunctionConverter(value)),
|
||||
'animation-iteration-count': (info: any, value: any) => (info.iterations = value === 'infinite' ? Number.POSITIVE_INFINITY : parseFloat(value)),
|
||||
'animation-direction': (info: any, value: any) => (info.isReverse = value === 'reverse'),
|
||||
'animation-fill-mode': (info: any, value: any) => (info.isForwards = value === 'forwards'),
|
||||
'animation-fill-mode': (info: any, value: any) => (info.isForwards = value === 'forwards' || value === 'both'),
|
||||
});
|
||||
|
||||
export class CssAnimationParser {
|
||||
@@ -65,6 +65,7 @@ export class CssAnimationParser {
|
||||
if (current === undefined) {
|
||||
current = <KeyframeInfo>{};
|
||||
current.duration = time;
|
||||
current.declarations = [];
|
||||
parsedKeyframes[time] = current;
|
||||
}
|
||||
for (const declaration of keyframe.declarations) {
|
||||
@@ -72,7 +73,7 @@ export class CssAnimationParser {
|
||||
current.curve = animationTimingFunctionConverter(declaration.value);
|
||||
}
|
||||
}
|
||||
current.declarations = declarations;
|
||||
current.declarations = current.declarations.concat(declarations);
|
||||
}
|
||||
}
|
||||
const array = [];
|
||||
@@ -87,39 +88,97 @@ export class CssAnimationParser {
|
||||
}
|
||||
}
|
||||
|
||||
function keyframeAnimationsFromCSSProperty(value: any, animations: KeyframeAnimationInfo[]) {
|
||||
if (typeof value === 'string') {
|
||||
const values = value.split(/[,]+/);
|
||||
for (const parsedValue of values) {
|
||||
const animationInfo = new KeyframeAnimationInfo();
|
||||
const arr = (<string>parsedValue).trim().split(/[ ]+/);
|
||||
/**
|
||||
* @see https://w3c.github.io/csswg-drafts/css-animations/#propdef-animation
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/animation
|
||||
* @internal - exported for testing
|
||||
* @param value
|
||||
* @param animations
|
||||
*/
|
||||
export function keyframeAnimationsFromCSSProperty(value: any, animations: KeyframeAnimationInfo[]) {
|
||||
if (typeof value !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (value.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches whitespace except if the whitespace is contained in parenthesis - ex. cubic-bezier(1, 1, 1, 1).
|
||||
*/
|
||||
const VALUE_SPLIT_RE = /\s(?![^(]*\))/;
|
||||
|
||||
/**
|
||||
* Matches commas except if the comma is contained in parenthesis - ex. cubic-bezier(1, 1, 1, 1).
|
||||
*/
|
||||
const MULTIPLE_SPLIT_RE = /,(?![^(]*\))/;
|
||||
|
||||
const isTime = (v: string) => !!v.match(/\dm?s$/g);
|
||||
const isTimingFunction = (v: string) => !!v.match(/ease|linear|ease-in|ease-out|ease-in-out|spring|cubic-bezier/g);
|
||||
const isIterationCount = (v: string) => !!v.match(/infinite|[\d.]+$/g);
|
||||
const isDirection = (v: string) => !!v.match(/normal|reverse|alternate|alternate-reverse/g);
|
||||
const isFillMode = (v: string) => !!v.match(/none|forwards|backwards|both/g);
|
||||
const isPlayState = (v: string) => !!v.match(/running|paused/g);
|
||||
|
||||
const values = value.split(MULTIPLE_SPLIT_RE);
|
||||
for (const parsedValue of values) {
|
||||
const animationInfo = new KeyframeAnimationInfo();
|
||||
const parts = (<string>parsedValue).trim().split(VALUE_SPLIT_RE);
|
||||
|
||||
const [duration, delay] = parts.filter(isTime);
|
||||
const [timing] = parts.filter(isTimingFunction);
|
||||
const [iterationCount] = parts.filter(isIterationCount);
|
||||
const [direction] = parts.filter(isDirection);
|
||||
const [fillMode] = parts.filter(isFillMode);
|
||||
const [playState] = parts.filter(isPlayState);
|
||||
const [name] = parts.filter((v) => {
|
||||
// filter out "consumed" values
|
||||
return ![duration, delay, timing, iterationCount, direction, fillMode, playState].filter(Boolean).includes(v);
|
||||
});
|
||||
|
||||
// console.log({
|
||||
// duration,
|
||||
// delay,
|
||||
// timing,
|
||||
// iterationCount,
|
||||
// direction,
|
||||
// fillMode,
|
||||
// playState,
|
||||
// name,
|
||||
// });
|
||||
|
||||
if (duration) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-duration'](animationInfo, duration);
|
||||
}
|
||||
if (delay) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-delay'](animationInfo, delay);
|
||||
}
|
||||
if (timing) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-timing-function'](animationInfo, timing);
|
||||
}
|
||||
if (iterationCount) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-iteration-count'](animationInfo, iterationCount);
|
||||
}
|
||||
if (direction) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-direction'](animationInfo, direction);
|
||||
}
|
||||
if (fillMode) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-fill-mode'](animationInfo, fillMode);
|
||||
}
|
||||
if (playState) {
|
||||
// TODO: implement play state? Currently not supported...
|
||||
}
|
||||
if (name) {
|
||||
ANIMATION_PROPERTY_HANDLERS['animation-name'](animationInfo, name);
|
||||
} else {
|
||||
// based on the SPEC we should set the name to 'none' if no name is provided
|
||||
// however we just don't set the name at all.
|
||||
// perhaps we should set it to 'none' and handle it accordingly.
|
||||
// animationInfo.name = 'none'
|
||||
}
|
||||
|
||||
animations.push(animationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,9 +187,9 @@ export function parseKeyframeDeclarations(unparsedKeyframeDeclarations: Keyframe
|
||||
const property = CssAnimationProperty._getByCssName(unparsedProperty);
|
||||
unparsedValue = cleanupImportantFlags(unparsedValue, property?.cssLocalName);
|
||||
|
||||
if (typeof unparsedProperty === 'string' && property && property._valueConverter) {
|
||||
if (typeof unparsedProperty === 'string' && property?._valueConverter) {
|
||||
declarations[property.name] = property._valueConverter(<string>unparsedValue);
|
||||
} else if (typeof unparsedValue === 'string' && unparsedProperty === 'transform') {
|
||||
} else if (unparsedProperty === 'transform') {
|
||||
const transformations = transformConverter(unparsedValue);
|
||||
Object.assign(declarations, transformations);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user