深入理解CSS 中 transform matrix矩陣變換問題

  發布時間:2021-08-25 14:56:08   作者:恩恩先生   我要評論
css中transform屬性中的translate、scale、rotate、skew變換屬性均是通過matrix矩陣變換實現的。接下來通過本文給大家介紹CSS 中 transform matrix矩陣變換問題,感興趣的朋友一起看看吧

一、概述

css中transform屬性中的translate、scale、rotate、skew變換屬性均是通過matrix矩陣變換實現的。直接使用矩陣變換,實現位移、縮放、旋轉、傾斜等動畫,不夠直觀;實際開發中還是使用變換屬性多一點。但這一點都不影響matrix屬性的重要性;理解matrix屬性定義的參數,需要一些線性代數的基礎。

二、矩陣

1、矩陣的定義

將一些元素排列成若干行,每行放上相同數量的元素,就是一個矩陣。如:

2、矩陣的基本運算

加(減)法:

兩個矩陣的加減法,取每個元素的對應的和(差)

數乘:

數字與矩陣元素之間的對應的乘積

矩陣乘法:

僅當第一個矩陣的列數(column)和第二個矩陣的行數(row)相等時才能定義。運算規則是,第一個矩陣行元素 與 第二個矩陣中的列元素,分別相乘求和,得到對應元素。例如 第一個矩陣的第一行元素[1 0 2],與第二個矩陣的第一列元素[3,2,1],分別相乘求和,即:1 x 3 + 0 x 2 + 2 x 1 = 5;得到運算后矩陣的左上第一個元素。

3、向量

物理學稱為矢量,指具有大小(magnitude)和方向的量。它可以形象化地表示為帶箭頭的線段。

向量的分解:

直角坐標系中,任意向量可表示為:其中,i、j為單位向量。

4、矩陣與向量

矩陣中的元素可以看做是一組坐標,而在平面直角坐標系中,一組向量可以使用坐標表示,因此可以使用矩陣表示一組向量,而對矩陣的運算,可以看做是對一組坐標的變換。來看具體的例子。

三、矩陣變換

1、矩陣縮放

建立一個特殊的平面直角坐標系,坐標系的特殊之處在于x軸和y軸是橡皮筋構成的,可以進行任意拉伸和收縮。坐標系中存在向量A(-1,1);考慮如何將其放大一倍。

坐標系是彈性的,顯然只需要將坐標系拉伸一倍就可以使得向量A放大一倍。根據公式有:

拉伸坐標系實際上改變的是單位向量,拉伸后的坐標系單位向量均為原先的兩倍,即:

通過縮放單位向量,使得坐標系中的向量發生縮放,這是對于單一向量而言的。對于頁面上的由多個坐標構成的塊狀 div 而言,縮放單位向量最直觀的效果就是長度和寬度的縮放。

2、矩陣的旋轉

如何使坐標系中的向量發生旋轉,考慮將向量A(-1,1),順時針旋轉45度。

很顯然需要旋轉單位向量。有:

結果:

對A向量旋轉可以通過旋轉單位向量實現,通過對單位向量的旋轉實現對任意向量的旋轉。常見的變換操作,如縮放,旋轉,傾斜,都可以通過對單位向量的操作進行實現。css matrix函數提供的參數就是描述一組單位向量的矩陣。

四、css中的矩陣變換

css 中 transform matrix 2d變換的參數一共有6個:

matrix(a, b, c, d, e, f)

其中默認參數為:

matrix(1, 0, 0, 1, 0, 0)

其中前4個參數,就是單位向量i(1,0)、j(0,1)。但注意,web頁面中的坐標系原點在左上角,向右和向下對應平面直角坐標系的x軸和y軸正向;因此與平面直角坐標系的y軸的方向是相反的。

觀察 transform 屬性中的scale、rotate、skew是如何通過matrix矩陣來實現。

1、scale

使用scale縮放一個div的css是這樣描述的:表示將寬和高同時放大兩倍。

transform: scale(2); //同transform: scale(2,2)

使用矩陣達到上述效果,如果使用matrix實現,將單位向量放大兩倍即可

transform: matrix(2,0,0,2,0,0);

2、rotate

使用rotate順時針旋轉div 30deg:

transform: rotate(30deg);

使用matrix實現,只需旋轉單位向量即可

 transform: matrix(cos30°, sin30°, -sin30°, cos30°, 0,  0);
  transform: matrix(0.866, 0.5, -0.5, 0.886 ,0 , 0);

3、skew

使用skew 傾斜30度:

transform: skew(30deg, 0);

使用matrix實現:

transform: matrix(1,0,0.5773502691896257,1,0,0);

4、translate

上述3中變換均通過變換單位向量產生,原點都未發生變化。位移變化需要增加矩陣元素,和函數的參數,也是matrix(a, b, c, d, e, f)中,e、f參數的作用,矩陣的形式可以變現為以下形式:

如,坐標[-1,-1],向右向上平移2個單位,得到變換后的坐標[1, 1]。

下面的css效果是一樣的:

transform: translate(50px,0);
transform: matrix(1,0,0,1,50,0);

使用matrix的形式,可以一次性定義上述4種變換。但使用transform, 需要將變換表達式寫在一行,使用空格分隔有助于閱讀,但不是必須的。

transform: skew(30deg,0) scale(2) rotate(30deg) translate(50px);

5、matrix3d

matrix3d(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p)    //定義 3D 轉換,使用 16 個值的 4x4 矩陣。

矩陣的形式為:

6、2D變換的矩陣形式

給出一組transform變換(rotate、skew、translate、scale),將其轉換成matrix函數的參數形式,需要注意以下幾點:

1、rotate與skew變換是相互影響的,試圖直接通過三角函數的計算得出相應的參數是不正確的,需要將其帶入矩陣進行運算;

2、scale與translate變換理論上可以直接修改矩陣元素,也可以帶入矩陣運算,但反復測試發現,translate帶入矩陣后運算結果有誤,這里直接修改矩陣元素。

3、矩陣運算比較復雜,使用的 Sylvester.js 庫。以下是代碼

let transformString = 'skew(15deg) rotate(30deg) scale(1.5) translate(30px)'
function stringToMatrix(transformString) {
    try {
        // 參數檢查
        if (typeof transformString !== 'string') {
            console.error('params must be string');
            return;
        }
        if (transformString.length === 0) {
            console.error('wrong transform format');
            return
        }
        if (['X', 'Y', '3d'].some(item => transformString.includes(item))) {
            console.error('3d transform unsupported yet');
            return;
        }
        let a = 1, b = 0, c = 0, d = 1, e = 0, f = 0;
        let reg = /(scale|rotate|skew|translate){1}\(.*?\)/g;

        let matrixDefault = $M([
            [1, 0, 0],
            [0, 1, 0],
            [0, 0, 1]
        ]);
        let matrixScale, matrixRotate, matrixSkew, matrixTranslate, matrixResult;

        transformString.match(reg).forEach(item => {
            let re = /\d+(.\d+)?/g;
            if (item.includes('rotate')) {
                let params = item.match(re);
                if (params) {
                    // a = Math.cos(params[0] / 180 * Math.PI) * a; //需要帶入矩陣
                    // b = Math.sin(params[0] / 180 * Math.PI) * a;
                    // c = -Math.sin(params[0] / 180 * Math.PI) * d;
                    // d = Math.cos(params[0] / 180 * Math.PI) * d;

                    const i = params[0] / 180 * Math.PI;
                    matrixRotate = $M([
                        [Math.cos(i), -Math.sin(i), 0],
                        [Math.sin(i), Math.cos(i), 0],
                        [0, 0, 1]
                    ])
                    matrixResult = matrixResult ? matrixResult.x(matrixRotate) : matrixDefault.x(matrixRotate);
                }
            }
            if (item.includes('skew')) {
                let params = item.match(re);
                // to fix 角度不可超過90度
                // params[0] ? c = Math.tan(params[0] / 180 * Math.PI) : '';
                // params[1] ? b = Math.tan(params[1] / 180 * Math.PI) : '';

                const [i = 0, j = 0] = params.map(a => parseFloat(a));
                // matrixSkew = [1, Math.tan(j), Math.tan(i), 1, 0, 0];//需要帶入矩陣
                matrixSkew = $M([
                    [1, Math.tan(i / 180 * Math.PI), 0],
                    [Math.tan(j / 180 * Math.PI), 1, 0],
                    [0, 0, 1]
                ]);
                matrixResult = matrixResult ? matrixResult.x(matrixSkew) : matrixDefault.x(matrixSkew);
            }
            if (item.includes('scale')) {
                let params = item.match(re);
                // a = params[0] ? params[0] * a : a;
                // d = params[1] ? params[1] * d : params[0] * d;

                let [i, j = i] = params.map(a => parseFloat(a));
                matrixScale = $M([
                    [i, 0, 0],
                    [0, j, 0],
                    [0, 0, 1]
                ]);

                matrixResult = matrixResult ? matrixResult.x(matrixScale) : matrixDefault.x(matrixScale);
                // if(matrixResult) {
                //     matrixResult.elements[0][0] = i * matrixResult.elements[0][0];
                //     matrixResult.elements[1][1] = i * matrixResult.elements[1][1];
                // }else {
                //     matrixDefault.elements[0][0] = i * matrixDefault.elements[0][0];
                //     matrixDefault.elements[1][1] = i * matrixDefault.elements[1][1];
                // }
            }
            if (item.includes('translate')) {
                let params = item.match(re);
                let [x = 0, y = 0] = params.map(a => parseFloat(a));
                e = x;
                f = y;

                matrixTranslate = $M([
                    [1, 0, x],
                    [0, 1, y],
                    [0, 0, 1]
                ]);

                if (matrixResult) {
                    matrixResult.elements[0][2] = x;
                    matrixResult.elements[1][2] = y;
                } else {
                    matrixDefault.elements[0][2] = x;
                    matrixDefault.elements[1][2] = y;
                }
                // matrixResult = matrixResult ? matrixResult.x(matrixTranslate): matrixDefault.x(matrixTranslate);
            }
        })

        const [[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]] = matrixResult.elements;

        return `matrix(${a1}, ${b1}, ${a2}, ${b2}, ${a3}, ${b3})`;


    } catch (error) {
        console.log(error)
    }
}
stringToMatrix(transformString) // matrix(1.5, 0.7499999999999999, -0.401923788646684, 1.299038105676658, 30, 0)

參考連接:

1、https://zh.wikipedia.org/wiki/%E7%9F%A9%E9%98%B5#%E6%A0%87%E8%AE%B0

2、http://www.ruanyifeng.com/blog/2015/09/matrix-multiplication.html

3、https://www.jianshu.com/p/dcf189998ae2

到此這篇關于深入理解CSS 中 transform matrix矩陣變換的文章就介紹到這了,更多相關CSS 中 transform matrix矩陣變換內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章,希望大家以后多多支持腳本之家!

相關文章

  • css3 矩陣的使用詳解

    這篇文章主要介紹了css3 矩陣的使用詳解的相關資料,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-20

最新評論

精品国内自产拍在线观看