簡體   English   中英

css3 從方形圖像到沒有 x 旋轉的梯形

[英]css3 from squared image to trapezoid without x rotation

我正在嘗試將帶有背景圖像的方形 div 轉換為梯形。

我想以 2D 的方式制作它,就像 Photoshop 的“扭曲”工具所做的一樣。

基本上,我想要的只是縮小正方形的頂部並使圖像相應地變形。

3D 轉換“似乎”可以解決問題:

transform: rotateX(30deg);

它適用於大多數用例,但不是所有用例。 事實上,它是正方形旋轉 30 度,從正面/背面看時“看起來”像一個梯形,但從任何其他側面看仍然是旋轉 30 度的正方形。

我想要的是得到一個真正的梯形。 我希望以 2D 方式扭曲方形圖像,以便實際改變形狀和圖像,而不涉及旋轉。

我嘗試了這個並且它在形狀(梯形)方面起作用:

border-style: solid;
height: 0;
border-color: transparent transparent red transparent;
border-width: 0 100px 100px 100px;

但是我不能用會跟隨失真的背景圖像替換紅色區域。 這違背了我的目的。 我嘗試過的任何嘗試都會使圖片保持不變形。

是否有任何 css/html5/javascript 技巧可以實現我想要的?

謝謝。

您可以通過對偽元素(您還設置了background-image )應用 3D 變換並確保它在其原始平面(其父元素的平面)中展平來獲得效果。 這意味着如果你想旋轉 3D 中的東西,你必須旋轉父級。

步驟#1 :創建一個正方形div ,添加一個具有完全相同尺寸的偽(或子)並在此偽上設置background-image

div {
    display: grid; /* makes pseudo stretch all across */
    width: 28em; /* whatever edge value we want */
    aspect-ratio: 1; /* make it square */
    /* just to highlight div boundaries */
    box-shadow: 0 0 0 3px;
    
    &::after {
        background: url(image.jpg) 50%/ cover;
        content: ''
    }
}

第 2 步:將偽上的transform-origin設置為底部邊緣的中間 ( 100% 50% ) - 這確保在應用 3D 變換后底部邊緣將保持在原位。

步驟#3 :沿z y延長邊緣。

是的,我們在CSS中沒有3D個傾斜函數。但是我們有matrix3d() ,可以用來表示任何旋轉,縮放,傾斜,平移!

因此,讓我們首先了解傾斜是如何工作的。

傾斜發生在一個軸上。

這是一個交互式演示,說明 2D 傾斜函數的工作原理。

考慮這個例子,我們沿x軸傾斜,隨着 y 軸旋轉遠離其初始 position,沿y軸的邊緣變長 - 這個角度是傾斜角。 z軸垂直於我們傾斜的平面(本例中為xOy )並且不受影響:

skewX 示例

嗯,在我們的例子中,我們做了類似的事情,但是傾斜發生在yOz平面,而不是xOy平面,因為我們沿着z軸而不是沿着x軸傾斜。

坐標系

因為我們已經用transform-origin錨定了我們的 pseudo 底部邊緣的中間,並且這種傾斜發生在z軸上(垂直於屏幕),結果我們基本上是在向后拉和拉伸我們的 pseudo 背部,朝向屏幕背面,保留每個點的xy坐標,但更改z坐標。

基本上,如果我們在 3D 中查看它而不展平到父平面(父平面以輪廓為界),它看起來會像下面這樣。

結果 z 偏斜后的 3D 視圖,沒有展平到父平面

您可以看到頂部的水平參考線如何顯示傾斜偽模型的頂部如何保留其xy坐標,它只是沿z軸拉回。

好吧,我們CSS這個怎么辦?

如前所述,沒有 3D 偏斜,但我們可以自己構建變換矩陣。 由於這是沿z軸(第 3 軸)的傾斜,沿y軸(第 2 軸)拉伸邊緣,因此矩陣中唯一與單位矩陣不同的 position(主對角線上為1 ,其他位置為0 )將是在第 3 行,第 2 列。 我們將在那里獲得傾斜角的切線。 在 MDN 上,您也可以在skewX()skewY()中看到這一點。

這是因為沿傾斜軸的每個點都會被其沿延長軸的坐標乘以傾斜角的切線而移位 - 如果您繪制平行於軸( x軸, y軸前和后傾斜)通過其原始 position(灰色)和最終 position(黑色)中的示例點。 繪制這些平行線會創建一個直角三角形,其中y坐標上的x位移是傾斜角的正切。

好的,回到矩陣,它看起來像這樣。

1   0    0
0   1    0
0 tan(a) 1

要獲得matrix3d()值,我們再添加一行和一列,與它們在4x4單位矩陣中的內容相同,然后逐列(而不是逐行)列出值,到目前為止:我們有:

@use 'sass:math'; // allows us to use trigonometric functions
$a: 60deg; // the skew angle

div {
    display: grid;
    width: 28em;
    aspect-ratio: 1;
    perspective: 25em;
    box-shadow: 0 0 0 3px;
    
    &::after {
        transform-origin: 50% 100%;
        transform: matrix3d(1, 0, 0, 0, /* 1st column */
                            0, 1, math.tan($a), 0, /* 2nd column */
                            0, 0, 1, 0, /* 3rd column */
                            0, 0, 0, 1);
        background: url(image.jpg) 50%/ cover;
        content: ''
    }
}

請注意,我們還添加了一個perspective來獲得扭曲的視圖(頂部較小/更靠后)。

到目前為止的代碼為我們提供了我們在上面的 gif 中看到的扁平化版本。 我說扁平化版本是因為,根據我們這里的內容,偽類總是在其父級平面中被扁平化。

當parent div沒有3D變換的時候,我們從正面看,pseudo明顯看起來扁平化了。

當父div確實有一個 3D 變換時,它的 3D 變換偽會被展平到它的平面中,因為默認的transform-style值是flat 這意味着 3D 轉換父對象的任何 3D 轉換子對象/偽對象在父對象的平面中被展平。 如果我們將 div 的transform-style設置為preserve-3d ,這可以改變。 但我們不想在這里。

第 4 步:修復頂部邊緣!

還有一件事看起來仍然不對: transform后的上邊緣現在低於原始邊緣。

轉換后結果

這是因為我們已經設定了一個perspective及其運作方式。 默認情況下, perspective-origin位於我們設置它的元素的中間(在本例中為我們的div ),水平方向為50% 50% ,垂直方向為 50%。

讓我們只考慮屏幕平面后面的點,因為那是我們整個 3D 傾斜偽像所在的位置。

使用默認的perspective-origin ( 50% 50% ),只有位於div正中間的垂直於屏幕平面的直線上的點將在具有相同x 的點處投影到屏幕平面上,考慮到透視后, y坐標為自己的坐標。 考慮到透視后,只有垂直於屏幕並沿div的水平中線與屏幕相交的平面中的點將被投影到這條水平中線上。

你知道這是怎么回事嗎? 如果我們移動perspective-origin使其位於 div 頂部邊緣的中間 ( 50% 0 ),那么平面中沿該頂部邊緣垂直於屏幕的點將沿着該頂部邊緣投影 - 即, 3D 傾斜偽對象的上邊緣將與其父對象的上邊緣在同一條線上。

所以我們最終的代碼是:

@use 'sass:math'; // allows us to use trigonometric functions
$a: 60deg; // the skew angle

div {
    display: grid;
    width: 28em;
    aspect-ratio: 1;
    perspective-origin: 50% 0;
    perspective: 25em;
    box-shadow: 0 0 0 3px;
    
    &::after {
        transform-origin: 50% 100%;
        transform: matrix3d(1, 0, 0, 0, /* 1st column */
                            0, 1, math.tan($a), 0, /* 2nd column */
                            0, 0, 1, 0, /* 3rd column */
                            0, 0, 0, 1);
        background: url(image.jpg) 50%/ cover;
        content: ''
    }
}

這是結果與其預轉換版本之間的實時比較視圖,因為兩個 div 在 3D 中旋轉以顯示它們在xOy平面中是平坦的。

比較視圖動畫


不想對正切值使用預處理器? Firefox 和 Safari 默認已經支持三角函數,Chrome 111+ 通過在chrome://flags中啟用的實驗性 Web 平台功能標志支持它們。

也不想等待 Chromium 支持? 你甚至不需要在那里使用切線計算,你可以使用任何正數——這個數字越大,頂邊就越小。 我使用切線值來說明它的來源,但您不必這樣做。 我們的切線值是針對90°的角度計算的。 這給了我們從0+Infinity的切線值。 所以是的,矩陣中的任何正數都可以。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM