搜索

首页  >  问答  >  正文

css3从方形图像到不带x旋转的梯形

我正在尝试将带有背景图像的方形 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 技巧可以实现我想要的?

谢谢。

P粉604669414P粉604669414240 天前344

全部回复(1)我来回复

  • P粉131455722

    P粉1314557222024-03-29 14:57:26

    您可以通过对伪元素应用 3D 变换(您还可以在其上设置 background-image)并确保它在其原始平面(其父元素的平面)上展平来获得效果。这意味着如果您想要以 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 轴应用 3D 倾斜,沿 y 轴延长边缘。

    是的,我们在 CSS 中没有 3D 倾斜函数。但我们有matrix3d(),可以用来表达任何旋转、缩放、倾斜、平移!

    所以让我们首先了解倾斜是如何工作的。

    沿轴发生倾斜。

    这是一个交互式演示,说明了 2D 倾斜函数的工作原理。

    考虑这个例子,我们沿着x轴倾斜,当y轴旋转远离其初始位置时,沿着y轴的边缘被拉长 - 这个角度是倾斜角度。 z 轴垂直于我们倾斜的平面(本例中为xOy)并且不受影响:

    好吧,在我们的例子中,我们做了类似的事情,但是倾斜发生在 yOz 平面中,而不是 xOy 平面中,因为我们沿着 yOz 平面倾斜>z 轴而不是沿 x 轴。

    由于我们已经使用 transform-origin 将伪值底部边缘的中间锚定到位,并且这种倾斜沿着 z 轴(垂直于屏幕)发生,因此我们基本上将我们的伪背部拉向屏幕背面,保留每个点的 xy 坐标,但更改 z坐标。

    基本上,如果我们以 3D 方式查看它而不展平到父级平面(父级受轮廓限制),它会如下所示。

    您可以看到顶部的水平指导线如何显示倾斜伪值的顶部如何保留其 xy 坐标,它只是沿着z 轴。

    好吧,我们如何使用 CSS 来实现它?

    如上所述,没有 3D 倾斜,但我们可以自己构建变换矩阵。由于这是沿 z 轴(第三轴)的倾斜,沿 y 轴(第二轴)拉伸边缘,因此矩阵中唯一与 单位矩阵(沿着主对角线的 1,其他地方的 0)将位于第 3 行第 2 行柱子。我们将在那里得到倾斜角的正切。在 MDN 上,您也可以在 skewX()skewY() 中看到这一点。

    这是因为沿倾斜轴的每个点都会被其沿延长轴的坐标乘以倾斜角的正切值所位移 - 如果您绘制与轴平行的线,您可以在第一个插图中看到这一点(x< /em> 轴,y 轴(前后倾斜)穿过示例点的原始位置(灰色)和最终位置(黑色)。绘制这些平行线会创建一个直角三角形,其中 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 中所看到的扁平化版本。我说扁平版本是因为,就我们这里所拥有的而言,伪对象总是在其父级的平面上被扁平化。

    当父级div没有3D变换时,我们从正面看,伪明显看起来是扁平的。

    当父级 div 确实具有 3D 变换时,其 3D 变换伪值会展平到其平面中,因为默认 transform-style 值为 flat。这意味着 3D 变换父级的任何 3D 变换子级/伪对象都会在父级平面中展平。如果我们将div的transform-style设置为preserve-3d,则可以更改此设置。但我们不希望这样。

    第 4 步:固定顶部边缘!

    还有一件事看起来仍然不正确:transform 顶部边缘现在位于原始边缘下方。

    这是因为我们设置了 perspective 以及它的工作原理。默认情况下,perspective-origin 死在我们设置它的元素的中间(在本例中是我们的 div),水平方向为 50%,垂直方向为 50%

    让我们只考虑屏幕平面后面的点,因为那是我们整个 3D 倾斜伪值所在的位置。

    使用默认的 perspective-origin (50% 50%),只有 div 正中间垂直于屏幕平面的线上的点才会被投影到屏幕平面上的某个点:考虑视角后,与自己的 x,y 坐标相同的 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 的切线值。所以是的,任何正数都可以在矩阵中出现。

    回复
    0
  • 取消回复