mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-14 18:12:09 +08:00
134 lines
4.0 KiB
TypeScript
134 lines
4.0 KiB
TypeScript
import { Pair, Transformation, TransformationType, TransformationValue, TransformFunctionsInfo } from '../animation';
|
|
import { radiansToDegrees } from '../../utils/number-utils';
|
|
import { decompose2DTransformMatrix, getTransformMatrix, matrixArrayToCssMatrix, multiplyAffine2d } from '../../matrix';
|
|
import { hasDuplicates } from '../../utils';
|
|
|
|
type TransformationStyleMap = {
|
|
[key: string]: (value: TransformationValue) => Transformation;
|
|
};
|
|
|
|
const IDENTITY_TRANSFORMATION = {
|
|
translate: { x: 0, y: 0 },
|
|
rotate: { x: 0, y: 0, z: 0 },
|
|
scale: { x: 1, y: 1 },
|
|
};
|
|
|
|
const TRANSFORM_SPLITTER = new RegExp(/\s*(.+?)\((.*?)\)/g);
|
|
const TRANSFORMATIONS = Object.freeze<TransformationType[]>(['rotate', 'rotateX', 'rotateY', 'rotate3d', 'translate', 'translate3d', 'translateX', 'translateY', 'scale', 'scale3d', 'scaleX', 'scaleY']);
|
|
|
|
const STYLE_TRANSFORMATION_MAP: TransformationStyleMap = Object.freeze<TransformationStyleMap>({
|
|
scale: (value: number) => ({ property: 'scale', value }),
|
|
scale3d: (value: number) => ({ property: 'scale', value }),
|
|
scaleX: ({ x }: Pair) => ({
|
|
property: 'scale',
|
|
value: { x, y: IDENTITY_TRANSFORMATION.scale.y },
|
|
}),
|
|
scaleY: ({ y }: Pair) => ({
|
|
property: 'scale',
|
|
value: { y, x: IDENTITY_TRANSFORMATION.scale.x },
|
|
}),
|
|
translate: (value) => ({ property: 'translate', value }),
|
|
translate3d: (value) => ({ property: 'translate', value }),
|
|
translateX: ({ x }: Pair) => ({
|
|
property: 'translate',
|
|
value: { x, y: IDENTITY_TRANSFORMATION.translate.y },
|
|
}),
|
|
translateY: ({ y }: Pair) => ({
|
|
property: 'translate',
|
|
value: { y, x: IDENTITY_TRANSFORMATION.translate.x },
|
|
}),
|
|
|
|
rotate3d: (value) => ({ property: 'rotate', value }),
|
|
rotateX: (x: number) => ({
|
|
property: 'rotate',
|
|
value: {
|
|
x,
|
|
y: IDENTITY_TRANSFORMATION.rotate.y,
|
|
z: IDENTITY_TRANSFORMATION.rotate.z,
|
|
},
|
|
}),
|
|
rotateY: (y: number) => ({
|
|
property: 'rotate',
|
|
value: {
|
|
x: IDENTITY_TRANSFORMATION.rotate.x,
|
|
y,
|
|
z: IDENTITY_TRANSFORMATION.rotate.z,
|
|
},
|
|
}),
|
|
rotate: (z: number) => ({
|
|
property: 'rotate',
|
|
value: {
|
|
x: IDENTITY_TRANSFORMATION.rotate.x,
|
|
y: IDENTITY_TRANSFORMATION.rotate.y,
|
|
z,
|
|
},
|
|
}),
|
|
});
|
|
|
|
export function transformConverter(text: string): TransformFunctionsInfo {
|
|
const transformations = parseTransformString(text);
|
|
|
|
if (text === 'none' || text === '' || !transformations.length) {
|
|
return IDENTITY_TRANSFORMATION;
|
|
}
|
|
|
|
const usedTransforms = transformations.map((t) => t.property);
|
|
if (!hasDuplicates(usedTransforms)) {
|
|
const fullTransformations = { ...IDENTITY_TRANSFORMATION };
|
|
transformations.forEach((transform) => {
|
|
fullTransformations[transform.property] = transform.value;
|
|
});
|
|
|
|
return fullTransformations;
|
|
}
|
|
|
|
const affineMatrix = transformations.map(getTransformMatrix).reduce(multiplyAffine2d);
|
|
const cssMatrix = matrixArrayToCssMatrix(affineMatrix);
|
|
|
|
return decompose2DTransformMatrix(cssMatrix);
|
|
}
|
|
|
|
function isTransformType(propertyName: string): propertyName is TransformationType {
|
|
return (TRANSFORMATIONS as string[]).indexOf(propertyName) !== -1;
|
|
}
|
|
|
|
// using general regex and manually checking the matched
|
|
// properties is faster than using more specific regex
|
|
// https://jsperf.com/cssparse
|
|
function parseTransformString(text: string): Transformation[] {
|
|
const matches: Transformation[] = [];
|
|
let match: RegExpExecArray;
|
|
|
|
while ((match = TRANSFORM_SPLITTER.exec(text)) !== null) {
|
|
const property = match[1];
|
|
|
|
if (isTransformType(property)) {
|
|
const value = convertTransformValue(property, match[2]);
|
|
matches.push(STYLE_TRANSFORMATION_MAP[property](value));
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
function convertTransformValue(property: TransformationType, rawValue: string): TransformationValue {
|
|
const values = rawValue.split(',').map(parseFloat);
|
|
const x = values[0];
|
|
|
|
let y = values[1];
|
|
let z = values[2];
|
|
|
|
if (property === 'translate') {
|
|
y ??= IDENTITY_TRANSFORMATION.translate.y;
|
|
} else {
|
|
y ??= x;
|
|
z ??= y;
|
|
}
|
|
|
|
if (property === 'rotate' || property === 'rotateX' || property === 'rotateY') {
|
|
return rawValue.slice(-3) === 'rad' ? radiansToDegrees(x) : x;
|
|
}
|
|
|
|
return { x, y, z };
|
|
}
|