css-transform-matrix-plugin
Version:
A webpack plugin that automatically converts CSS transform properties to matrix3d for GPU acceleration
402 lines (395 loc) • 13.1 kB
JavaScript
'use strict';
var valueParser = require('postcss-value-parser');
class TransformParser {
// 解析 transform 属性值
static parseTransformValue(value) {
const parsed = valueParser(value);
const functions = [];
parsed.walk((node) => {
if (node.type === 'function') {
const functionNode = node;
const args = this.extractFunctionArgs(functionNode);
functions.push({
name: functionNode.value,
args,
});
}
});
return functions;
}
// 提取函数参数并转换为数值
static extractFunctionArgs(node) {
const args = [];
if (node.nodes) {
node.nodes.forEach((arg) => {
if (arg.type === 'word') {
const numValue = this.parseNumericValue(arg.value);
if (numValue !== null) {
args.push(numValue);
}
}
});
}
return args;
}
// 解析数值(支持 px, deg, % 等单位)
static parseNumericValue(value) {
// 移除单位,只保留数值
const match = value.match(/^(-?\d*\.?\d+)(px|deg|%|em|rem)?$/);
if (match) {
const num = parseFloat(match[1]);
const unit = match[2];
// 角度转弧度
if (unit === 'deg') {
return (num * Math.PI) / 180;
}
return num;
}
return null;
}
}
// 角度转换函数
// 创建单位矩阵
function createIdentityMatrix() {
return {
m11: 1,
m12: 0,
m13: 0,
m14: 0,
m21: 0,
m22: 1,
m23: 0,
m24: 0,
m31: 0,
m32: 0,
m33: 1,
m34: 0,
m41: 0,
m42: 0,
m43: 0,
m44: 1,
};
}
// 矩阵乘法 - 将两个 4x4 矩阵相乘
function multiplyMatrices(a, b) {
return {
// 第一行
m11: a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 + a.m14 * b.m41,
m12: a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 + a.m14 * b.m42,
m13: a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 + a.m14 * b.m43,
m14: a.m11 * b.m14 + a.m12 * b.m24 + a.m13 * b.m34 + a.m14 * b.m44,
// 第二行
m21: a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 + a.m24 * b.m41,
m22: a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 + a.m24 * b.m42,
m23: a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 + a.m24 * b.m43,
m24: a.m21 * b.m14 + a.m22 * b.m24 + a.m23 * b.m34 + a.m24 * b.m44,
// 第三行
m31: a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 + a.m34 * b.m41,
m32: a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 + a.m34 * b.m42,
m33: a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 + a.m34 * b.m43,
m34: a.m31 * b.m14 + a.m32 * b.m24 + a.m33 * b.m34 + a.m34 * b.m44,
// 第四行
m41: a.m41 * b.m11 + a.m42 * b.m21 + a.m43 * b.m31 + a.m44 * b.m41,
m42: a.m41 * b.m12 + a.m42 * b.m22 + a.m43 * b.m32 + a.m44 * b.m42,
m43: a.m41 * b.m13 + a.m42 * b.m23 + a.m43 * b.m33 + a.m44 * b.m43,
m44: a.m41 * b.m14 + a.m42 * b.m24 + a.m43 * b.m34 + a.m44 * b.m44,
};
}
// CSS Transform 矩阵转换器
class MatrixTransformer {
// 将transform函数数组抓暖胃3D矩阵
static transformsToMatrix3D(functions) {
let result = createIdentityMatrix();
for (const func of functions) {
const matrix = this.createMatrixFromFunction(func);
result = multiplyMatrices(result, matrix);
}
return result;
}
// 根据单个transform函数创建矩阵
static createMatrixFromFunction(func) {
const { name, args } = func;
switch (name) {
case 'translateX':
return this.createTranslateXMatrix(args[0] || 0);
case 'translateY':
return this.createTranslateYMatrix(args[0] || 0);
case 'translateZ':
return this.createTranslateZMatrix(args[0] || 0);
case 'translate':
return this.createTranslateMatrix(args[0] || 0, args[1] || 0);
case 'translate3d':
return this.createTranslate3DMatrix(args[0] || 0, args[1] || 0, args[2] || 0);
case 'scaleX':
return this.createScaleXMatrix(args[0] || 1);
case 'scaleY':
return this.createScaleYMatrix(args[0] || 1);
case 'scaleZ':
return this.createScaleZMatrix(args[0] || 1);
case 'scale':
return this.createScaleMatrix(args[0] || 1, args[1] || args[0] || 1);
case 'scale3d':
return this.createScale3DMatrix(args[0] || 1, args[1] || 1, args[2] || 1);
case 'rotate':
case 'rotateZ':
return this.createRotateZMatrix(args[0] || 0);
case 'rotateX':
return this.createRotateXMatrix(args[0] || 0);
case 'rotateY':
return this.createRotateYMatrix(args[0] || 0);
case 'skewX':
return this.createSkewXMatrix(args[0] || 0);
case 'skewY':
return this.createSkewYMatrix(args[0] || 0);
case 'skew':
return this.createSkewMatrix(args[0] || 0, args[1] || 0);
case 'matrix3d':
if (args.length >= 16) {
return this.createMatrix3DFromArray(args);
}
else {
console.warn(`matrix3d requires 16 arguments, got ${args.length}`);
return createIdentityMatrix();
}
case 'matrix':
if (args.length >= 6) {
return this.createMatrixFrom2D(args);
}
else {
console.warn(`matrix requires 6 arguments, got ${args.length}`);
return createIdentityMatrix();
}
default:
console.warn(`Unsupported transform function: ${name}`);
return createIdentityMatrix();
}
}
// 位移矩阵
static createTranslateXMatrix(x) {
const matrix = createIdentityMatrix();
matrix.m41 = x;
return matrix;
}
static createTranslateYMatrix(y) {
const matrix = createIdentityMatrix();
matrix.m42 = y;
return matrix;
}
static createTranslateZMatrix(z) {
const matrix = createIdentityMatrix();
matrix.m43 = z;
return matrix;
}
static createTranslateMatrix(x, y) {
const matrix = createIdentityMatrix();
matrix.m41 = x;
matrix.m42 = y;
return matrix;
}
static createTranslate3DMatrix(x, y, z) {
const matrix = createIdentityMatrix();
matrix.m41 = x;
matrix.m42 = y;
matrix.m43 = z;
return matrix;
}
// 缩放矩阵
static createScaleXMatrix(sx) {
const matrix = createIdentityMatrix();
matrix.m11 = sx;
return matrix;
}
static createScaleYMatrix(sy) {
const matrix = createIdentityMatrix();
matrix.m22 = sy;
return matrix;
}
static createScaleZMatrix(sz) {
const matrix = createIdentityMatrix();
matrix.m33 = sz;
return matrix;
}
static createScaleMatrix(sx, sy) {
const matrix = createIdentityMatrix();
matrix.m11 = sx;
matrix.m22 = sy;
return matrix;
}
static createScale3DMatrix(sx, sy, sz) {
const matrix = createIdentityMatrix();
matrix.m11 = sx;
matrix.m22 = sy;
matrix.m33 = sz;
return matrix;
}
// 旋转矩阵
static createRotateXMatrix(angle) {
const matrix = createIdentityMatrix();
const cos = Math.cos(angle);
const sin = Math.sin(angle);
matrix.m22 = cos;
matrix.m23 = -sin;
matrix.m32 = sin;
matrix.m33 = cos;
return matrix;
}
static createRotateYMatrix(angle) {
const matrix = createIdentityMatrix();
const cos = Math.cos(angle);
const sin = Math.sin(angle);
matrix.m11 = cos;
matrix.m13 = sin;
matrix.m31 = -sin;
matrix.m33 = cos;
return matrix;
}
static createRotateZMatrix(angle) {
const matrix = createIdentityMatrix();
const cos = Math.cos(angle);
const sin = Math.sin(angle);
matrix.m11 = cos;
matrix.m12 = -sin;
matrix.m21 = sin;
matrix.m22 = cos;
return matrix;
}
// 倾斜矩阵
static createSkewXMatrix(angle) {
const matrix = createIdentityMatrix();
matrix.m21 = Math.tan(angle);
return matrix;
}
static createSkewYMatrix(angle) {
const matrix = createIdentityMatrix();
matrix.m12 = Math.tan(angle);
return matrix;
}
static createSkewMatrix(angleX, angleY) {
const matrix = createIdentityMatrix();
matrix.m12 = Math.tan(angleY);
matrix.m21 = Math.tan(angleX);
return matrix;
}
/**
* 将矩阵转换为 CSS matrix3d 字符串
*/
static matrixToCSS(matrix) {
const values = [
matrix.m11,
matrix.m12,
matrix.m13,
matrix.m14,
matrix.m21,
matrix.m22,
matrix.m23,
matrix.m24,
matrix.m31,
matrix.m32,
matrix.m33,
matrix.m34,
matrix.m41,
matrix.m42,
matrix.m43,
matrix.m44,
];
// 保留6位小数,移除尾随零
const formattedValues = values.map((v) => parseFloat(v.toFixed(6)).toString());
return `matrix3d(${formattedValues.join(', ')})`;
}
// 从 matrix3d 的 16 个参数创建矩阵
static createMatrix3DFromArray(args) {
return {
m11: args[0],
m12: args[1],
m13: args[2],
m14: args[3],
m21: args[4],
m22: args[5],
m23: args[6],
m24: args[7],
m31: args[8],
m32: args[9],
m33: args[10],
m34: args[11],
m41: args[12],
m42: args[13],
m43: args[14],
m44: args[15],
};
}
// 从 2D matrix 的 6 个参数创建 3D 矩阵
static createMatrixFrom2D(args) {
return {
m11: args[0],
m12: args[1],
m13: 0,
m14: 0,
m21: args[2],
m22: args[3],
m23: 0,
m24: 0,
m31: 0,
m32: 0,
m33: 1,
m34: 0,
m41: args[4],
m42: args[5],
m43: 0,
m44: 1,
};
}
}
function createPostCSSPlugin(options = {}) {
const config = {
enabled: true,
keepOriginal: false,
verbose: false,
...options,
};
return {
postcssPlugin: 'css-transform-matrix',
Declaration: (decl) => {
if (!config.enabled ||
decl.prop !== 'transform' ||
decl.value === 'none') {
return;
}
try {
const originalValue = decl.value;
// 跳过已经处理的 matrix3d
if (originalValue.includes('matrix3d(')) {
if (config.verbose) {
console.log(`[CSS Transform Matrix] Skipping: ${originalValue}`);
}
return;
}
const functions = TransformParser.parseTransformValue(originalValue);
if (functions.length === 0) {
return;
}
const matrix = MatrixTransformer.transformsToMatrix3D(functions);
const matrixCSS = MatrixTransformer.matrixToCSS(matrix);
if (config.keepOriginal) {
decl.cloneBefore({
prop: `/* original-transform */`,
value: originalValue,
});
}
decl.value = matrixCSS;
if (config.verbose) {
console.log(`[CSS Transform Matrix] ${originalValue} -> ${matrixCSS}`);
}
}
catch (error) {
if (config.verbose) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.warn(`[CSS Transform Matrix] Failed: ${decl.value}`, errorMessage);
}
}
},
};
}
// PostCSS 插件标识
createPostCSSPlugin.postcss = true;
exports.createPostCSSPlugin = createPostCSSPlugin;
//# sourceMappingURL=postcss.js.map