繁体   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