mirror of
				https://github.com/NativeScript/NativeScript.git
				synced 2025-11-04 21:06:45 +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 };
 | 
						|
}
 |