在我永无止境地编码疯狂的3D几何形状时,我一直幻想和理论上对地球球的理论 - 近似是由怪异,难以理解的角度连接的三角形制成的球。
现在的噩梦终于发生了:
小心触摸它,这是真正的CPU猪 - 有些浏览器可能会窒息,花了几秒钟的时间加载,并且要注意播放按钮。根据我的经验,在Firefox上,它运行良好,但至少在一开始,Chrome和Edge都在挣扎。我的猜测是CSS三角学。当我用PHP或SCSS进行TRIG时,Chrome通常是更快的,并且浏览器获得了预烘烤的值。
因此,我最好不要理会这里正在进行的计算。他们把我的大脑炸成清脆 - 我已经习惯了这些东西。您不需要掉下这个兔子洞 - 除非您真的,真的
但是这里有一个更简单,更咬合的课程:
使用CSS创建三角形的奇怪方法
如果您希望标准的矩形容器成为一个不同的多边形,那么如今,您的首选将是剪辑路径:
div {
clip-path: polygon(0 100%, 50% 0, 100% 100%);
}
将通过将正方形切片从左下到顶部中心,然后右下:
来产生三角形:
这是一个快速,优雅的解决方案,是为此特定目的而设计的 - 与涉及边界修补的旧方法不同:
.triangle {
width: 0;
aspect-ratio: 1;
border: 50em solid; /* percentages won't work, you need concrete units: px, em, ch, etc. */
border-color: red yellow blue green;
}
通过在容器上设置非常厚的边界来创建一个三角形,然后在其中的三个颜色上设置为透明的颜色:
但是这些不是我今天的主题。我需要提及它们以给您一些历史背景 - 我将在其他方法的背景下谈论它们 - 但是,如果您想了解更多信息,这里是我们博客作者Alvaro Montoro的comprehensive article。
我今天想谈论的是CSS如何为您提供许多创建非标准形状的方法。这些方法本质上将与基于边界的方法更接近 - 即,并非专门设计用于剪切形状,只是表现出可用于形成它们的某些属性。
例如,看看立方体:
这是一组六个正方形的墙壁,每个墙都垂直于其直接邻居 - 但透视图会扭曲这些角度。距观众越远,出现的越小。左,右,顶部和底部的墙似乎是梯形 - 因为一个边比另一个边缘更靠近我们。
如果墙壁更高并延伸到我们距离我们的远处 - 在某个时候,它们的边缘更远的边缘会缩小太大,几乎看起来像一个点:
如果您缩短了视角:
也会发生同样的事情。
在这两种情况下,都必须达到极端的长度才能收敛。在拉伸墙壁的情况下,即使它们超过了透视的指定距离,他们还有很长的路要走。
同样,缩短视角将使您更接近融合点,但从来没有完全 。
,除非,否则也许,您将其一直向下拉到零...?这永远无法正常工作,对
但是,它确实可以 - 透视:0与角度不同:无:
在视觉结果方面,它们恰恰相反。有了透视:我们的顶部,底部,左侧和右墙对我们来说都是看不见的,因为它们垂直于前面。从透视图中:零消失点就像一个黑洞:它以令人难以置信的力量使所有逻辑都吸引了一切。墙壁的高度和角度不再重要,一切都扩展到其极限,物理似乎不再适用。
因此,无论您是否愿意,您都会得到四个美丽,完美的三角形 - 或在非常轻微的倾向的情况下至少非常非常接近完美(为什么您甚至会使用它们,将在下面讨论)。
,但到目前为止,基于透视的三角形的最令人敬畏,最有用的属性是梯度。
<div class="cube">
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
<div class="wall"></div>
</div>
.cube {
position: relative;
width: 100em;
aspect-ratio: 1;
perspective: 0;
}
.wall {
position: absolute;
left: 50em;
height: 100em;
aspect-ratio: 1;
transform-origin: 0 50%;
transform: rotate(calc(90deg*(var(--nth)))) translateX(50em) rotateY(90deg);
background-image: linear-gradient(hsl(calc(var(--nth)*90deg) 100% 50%), hsl(calc((var(--nth) + 1)*90deg) 100% 50%));
transition: all .3s ease-in-out;
}
.wall:nth-child(1) {
--nth: 1;
}
.wall:nth-child(2) {
--nth: 2;
}
.wall:nth-child(3) {
--nth: 3;
}
.wall:nth-child(4) {
--nth: 4;
}
您所得到的看起来就像是普通的圆锥梯度 - 实际上,这是我在使用之前使用的(我们不那么古老的时候 - 我们仍然没有SVG中的圆锥。但是,今天是否有理由使用基于透视的梯度?它们看起来相同 - 即使圆锥旋转并且线性仅假装,实际上只是沿着一条线滑动。
/* replacing the background-image definition */
.wall {
background-size: 100% 400%;
background-position-y: calc(var(--nth)*-100%);
animation: slide 18s linear infinite;
}
@keyframes slide {
100% {
background-position-y: calc(var(--nth)*-100% - 400%);
}
}
但是有些事情是锥体梯度做不到的 - 那是动画。如果您尝试将动画应用于它们,则它们会单个步骤跳过密钥帧之间 - 无论是关于颜色过渡还是角度的变化。同时,线性梯度(仍然就颜色而言仍然受到限制),在定义背景大小和背景位置方面确实提供了一些自由,并且在关键框架之间进行平稳过渡:
您也可以轻松伸展和弯曲三角形 - 通过更改透视 - 原始(您可能需要双击)
为什么,如果基于透视的三角形如此出色,我是否仅在笔中只用三个顶点使用它们,其中有数百个?也没有在球体本身中使用它们 - 只是播放按钮。
性能。
您的浏览器和您的CPU/GPU不喜欢数百个物体飞行。我已经知道多年了 - 无论如何,我还是强迫自己的路。尽管如此,透视图仍需要两个容器:一个设定视角的容器,而另一个则比受到该容器的影响。这使多边形数量增加了一倍 - 我什至还没有开始!他们通常也不喜欢太多的梯度。动画,变换,过滤器和混合模式(我喜欢碎片)也很难。最重要的是,现在我们在CSS中进行了三角学,这迫使浏览器实时处理数字。如果您正在阅读铬,那么您可能不得不等待10秒钟才能加载。
因为这样 - 还有很少的故障和特质 - 我需要遏制自己的野心。我不能拥有 动画 - 而且由于我不能,因此使用更多资源的解决方案没有意义。它仍然是我的最爱,但需要很少使用。就像VHS vs betamax-或DVD与蓝光。高级技术之所以失去,是因为它更昂贵 - 在处理能力方面。
还有一个区域,梯度均超出了夹子路径和透视图 - 这就是嵌套。
-
夹子路径将切断其范围内的任何事物 - 其中包括任何嵌套内容。它还使透视图/覆盖3D变换。
-
透视图会扭曲嵌套内容 - 无论您是否想要。
有一些方法可以解决此问题,但它需要额外的容器 - 我通常不想要。
那么,如何使一个三角形类似于我们以圆锥级别的方式所做的类似?让我们以1个为1(即,高度等于宽度)的最基本容器:
:
<div class="triangle"></div>
body {
background: #000;
}
.triangle {
width: 40%; /* roughtly half of the viewport's width - using this insead of my usual em for simplicity */
aspect-ratio: 1;
border: 3px #fff dashed;
margin: 5em;
background-image: conic-gradient(red,blue);
}
由于梯度在上半部分开始和结束,为简单起见,让我们的起源处于50%100%(中心,底部)。
.triangle {
background-image: conic-gradient(at 50% 100%, red, yellow, green, blue);
}
现在,我们需要梯度以红色的颜色从左上角开始,并通过黄色,绿色和蓝色逐渐发展,并在右角完成。您可以通过反复试验和错误使角度正确地进行手动执行此操作。幸运的是,您不必!三角学可以为您做到这一点。您只需要提供两种信息:
-
您的容器的高度 - 等于宽度,ergo:40%(忽略单元,我们只需要一个数字)。
-
宽度的一半:40/2 =20。
以此,我们可以计算从梯度起源(50%100%)到容器的任何一个角的线的角度。
.triangle {
--angle: atan(calc(20/40));
}
首先要计算宽度的一半与容器高度的比率。那是0.5。然后,它发现了什么角度的切线。由于完成后不会告诉您,所以让我:它是22.5度。
现在我们需要使用该数据。我将整个属性分为单独的行,以便我可以对所提供的每条信息发表评论。
让我们通过将起始点倾斜22.5度来检查是否正确:
.triangle {
--angle: atan(calc(20/40));
background-image: conic-gradient(
from calc(-1*var(--angle)) /* the starting angle: -22.5deg */
at 50% 100%, /* gradient's center=point: bottom center */
red, yellow, green, blue);
}
有一个原因,需要将其旋转为负22.5度。据我所知,圆锥分子的方向无法改变。它仅顺时针方向进行。
让我们知道下一步要做什么:使梯度仅延伸到右上角,而不是整个360。该角度为22.5度 - 这意味着我们目前的位置是两倍:AT -22.5 deg。
.triangle {
--angle: atan(calc(20/40));
background-image: conic-gradient(
from calc(-1*var(--angle)) /* the starting angle: -22.5deg */
at 50% 100%, /* gradient's center=point: bottom center */
red, yellow, green, blue calc(var(--angle)*2)); /* the gradient goes from red to blue over the span of 45 degrees only */
}
唯一的问题是我们没有告诉我们的梯度在45度后关闭。它仍然想做一个完整的圆圈,散布最后声明的颜色。这意味着,如果我们在45度标记之后不想要颜色,我们需要声明它:
.triangle {
--angle: atan(calc(20/40));
background-image: conic-gradient(
from calc(-1*var(--angle)) /* the starting angle: -22.5deg */
at 50% 100%, /* gradient's center=point: bottom center */
red, yellow, green, blue calc(var(--angle)*2),
transparent 0 ); /* the gradient goes from red to blue over the span of 45 degrees only */
}
透明度可以解决问题 - 45度后没有颜色:
,但真正的技巧是颜色规范之后的零。如果我们对语法非常严格,我们会这样这样做:
.triangle {
--angle: atan(calc(20/40));
background-image: conic-gradient(
from calc(-1*var(--angle))
at 50% 100%,
red, yellow, green, blue calc(var(--angle)*2),
transparent calc(var(--angle)*2) ); /* instead of transparent 0 */
使得透明度从同一点开始蓝色端(顺便说一句,我们必须这样做,因为如果我们不指定端点,CSS将假设透明度为100% - 您可能可以猜猜会发生什么)。但是它也可以是0-由于梯度仅顺时针方向进展,所以它不能回头,因此,如果透明度的开始是比蓝色的末端小的值透明度的开始。
这是使用圆锥级创建三角形的整个代码,并注释:
.triangle {
width: 40%; /* roughtly half of the viewport's width - using this insead of my usual em for simplicity */
aspect-ratio: 1;
margin: 5em;
--angle: atan(calc(20/40)); /* calculating the angle between either of the top corners and the center of the bottom by calculating the reverse tangent the base and the height */
background-image: conic-gradient(
from calc(-1*var(--angle)) /* the angle needs to be negative since the vertical line is the angle of 0, and here we're going counter-clockwise*/
at 50% 100%, /* center, bottom*/
red, yellow, green,blue calc(var(--angle)*2), /* the gradient ends with blue after making an arc of 45 (from 22.5 to get to the vertical line, and then, mirroring that, another 22.55 to reach the top right angle*/
transparent 0); /* the transparency needs to start right where the blue ends, so you could also serve it the same calc - but if you're lazy, you can give it any number that is smaller than the calculated value, because it can't go back to it, and will continue onwards from where it ended */
}
当然,您可以从这个基本的设置中脱颖而出 - 就像将三角形的尖端朝不同的方向:向上,向下,左或右:
以及更多的调整,更精确的校准。
但这是另一个故事。