clip-path:circle() 半径似乎没有正确计算

2022-01-14 00:00:00 geometry css clip-path

每当我尝试使用 CSS circle() 函数进行一些剪辑时,我一直困扰着我:

.red {宽度:200px;高度:300px;背景:红色;边框:2px纯黑色;剪辑路径:圆(69%);/* 几乎不切掉 .red div 的角 */}/* 整个圆圈将包围整个框,大约为 71% 或 (sqrt(2)/2 * 100%)根据 Mozilla 文档,而不是像人们期望的那样 100% */

<div class='red'></div>

似乎从来没有像我期望的那样计算半径.查看 Mozilla MDN 参考 (



这对我来说似乎不正确.我想他们会计算包围元素矩形(div、img 等)的圆周半径,如下所示:





但情况似乎并非如此.任何人都可以对此有所了解.这是某种错误还是我只是不理解这里的东西?

解决方案

它的定义是这样的,他们从来没有打算计算你显示的半径.

R 是您正在计算的c"(绿线),r 是使用的参考(紫色线).您可以很容易地看到 r = R/sqrt(2)R = sqrt(w² + h²).将两者结合起来会得到:

r = sqrt(w² + h²)/sqrt(2)

您在 MDN 页面中看到的公式是什么.

在正方形内使用该值的 50% 将为我们提供逻辑圆:

 r/2 = sqrt(w² + h²)/(2*sqrt(2)) = sqrt(2*w²)/(2*sqrt(2)) = w/2(或h/2)

为了覆盖整个正方形,我们需要一个等于 R/2 的值,即 r/sqrt(2) = r/1.41 并且因为 r100% 你将拥有你发现的 71%

.red {宽度:200px;高度:200px;背景:红色;剪辑路径:圆(计算(100%/1.44));/* 比 1.4 稍微好一点才能看得更清楚*/边框:2px 实心;框尺寸:边框框;}

<div class='red'></div>

同样的逻辑适用于宽度和高度不同但参考保持不变的非正方形形状:

r = sqrt(w² + h²)/sqrt(2)


从上面我们可以得出结论,71% 是一个神奇的值,无论形状如何,它都会产生相同的输出,因为它依赖于包围矩形元素的圆周半径,而 50%(或任何其他值)会给出不同的结果:

.red {宽度:200px;高度:200px;背景:红色;盒子阴影:0 0 0 100px 黄色;显示:内联块;剪辑路径:圆(71%);边距:70px;}

<div class='red'></div><div class='red' style="width:300px;"></div><div class='red' style="width:100px;"></div><div class='red' style="width:50px;"></div>

使用 50%

.red {宽度:200px;高度:200px;背景:红色;剪辑路径:圆(50%);边框:2px 实心;框尺寸:边框框;}

<div class='red'></div><div class='red' style="width:300px;"></div><div class='red' style="width:100px;"></div><div class='red' style="width:50px;"></div>

我们也可能认为任何大于 71% 的值都是无用的,因为我们总是会给出一个比我们的元素更大的圆.这是真的,但我们不应该忘记我们也有这个职位.

使用100%200%甚至300%的输出示例!

.red {宽度:200px;高度:200px;背景:红色;边框:2px 实心;框尺寸:边框框;}

<div class='red' style="clip-path: circle(100% at 0 50%)"></div><div class='red' style="clip-path: circle(200% at -100% 50%)"></div><div class='red' style="clip-path: circle(300% at -200% 50%)"></div>


我会考虑一个不同的属性来更好地消除混淆,即 radial-gradient.

.box {宽度:200px;高度:200px;边框:1px 实心;背景:径向渐变(圆形 50%,红色,蓝色);}

<div class="box"></div>

下面的代码是用来定义一个半径等于50%的圆,但它是无效的,因为我们不知道参考:

<块引用>

注意:此处不允许使用百分比;它们只能用于指定椭圆渐变的大小,而不是圆形渐变.之所以存在此限制,是因为对于百分比应与哪个维度相关,有多种合理的答案.此模块的未来级别可能会提供使用百分比调整圆圈大小的功能,可能会更明确地控制使用哪个维度.参考

我们正在处理矩形形状,因此我们可以使用您正在计算的高度、宽度、半径等.有很多选项,所以他们只是决定使其无效,但对于 clip-path 他们做出了决定,并为 percetange 的使用定义了一个参考.

顺便说一句,考虑到 closest-side/farthest-side 等值,您可以更好地控制您的圈子.

下面将始终为我们提供与最近边接触的圆圈(与 contain 的方式相同)

.red {宽度:200px;高度:200px;背景:红色;剪辑路径:圆圈(最近的一侧);边框:2px 实心;框尺寸:边框框;}

<div class='red'></div><div class='red' style="width:300px;"></div><div class='red' style="width:100px;"></div><div class='red' style="width:50px;"></div>

下面将始终为我们提供最远边的圆圈(与 cover 具有背景的方式相同)

.red {宽度:200px;高度:200px;背景:红色;剪辑路径:圆(最远侧);边框:2px 实心;框尺寸:边框框;}

<div class='red'></div><div class='red' style="width:300px;"></div><div class='red' style="width:100px;"></div><div class='red' style="width:50px;"></div>


结合位置,它们可以给出一些有趣的结果:

.red {宽度:200px;高度:200px;背景:红色;边框:2px 实心;框尺寸:边框框;过渡:1s全部;}

<div class='red' style="clip-path:circle(farthest-side at left); "></div><div class='red' style="clip-path:circle(closest-side at 10% 10%); "></div><div class='red' style="clip-path:circle(farthest-side at left top);"></div><div class='red' style="clip-path:circle(closest-side at 40% 50%); "></div>

It's been bugging me for a while whenever I try to use the CSS circle() function to do some clipping as in:

.red {
  	width: 200px;
        height: 300px;
        background: red;
        border: 2px solid black;
        clip-path: circle(69%);  /*barely cuts off the corners of the .red div */
}

/*  the full circle will enclose the entire box at around 71% or (sqrt(2)/2 * 100%)
 per Mozilla documentation and not at 100% as one might expect */

<div class='red'></div>

the radius never seem to be calculated as I would expect it to. Upon looking into the Mozilla MDN reference (https://developer.mozilla.org/en-US/docs/Web/CSS/basic-shape) it appears they calculate it as follows:





which to me just doesn't seem correct. I would imagine they would calculate the radius of the circumference that encloses the elements rectangle (div, img, etc) as follows:





but that just doesn't seem to be the case. Can anyone shed some light on this. Is this a bug of sorts or am I just not understanding something here?

解决方案

It's defined to be like that, they never meant to calculate the radius you are showing. It's also in the specification.

To better understand let's consider a square. You can have a perfect circle if you consider 50% as value

.red {
  width: 200px;
  height: 200px;
  background: red;
  clip-path: circle(50%);
  border:2px solid;
  box-sizing:border-box;
}

<div class='red'></div>

The idea behind is to consider the following figure:

R is the 'c' you are calculating (the Green lines) and r is the reference used (the puple line). You can easily see that r = R/sqrt(2) and R = sqrt(w² + h²). Combining both will give us:

r = sqrt(w² + h²)/sqrt(2)

Which is the formula you see in the MDN page.

Using 50% of this value inside a square will give us the logical circle:

 r/2 = sqrt(w² + h²)/(2*sqrt(2)) = sqrt(2*w²)/(2*sqrt(2)) = w/2 (or h/2)

To cover the whole square we need a value equal to R/2 which is r/sqrt(2) = r/1.41 and since r is 100% you will have the 71% you discovered

.red {
  width: 200px;
  height: 200px;
  background: red;
  clip-path: circle(calc(100% / 1.44)); /* a little bogger  than 1.4 to better see*/
  border:2px solid;
  box-sizing:border-box;
}

<div class='red'></div>

The same logic apply to a non-square shape where the width and height are different but the reference remain the same:

r = sqrt(w² + h²)/sqrt(2)


From the above we can conclude that the 71% is a magic value that will produce the same output whataver the shape since it rely on the radius of the circumference that encloses the elements rectangle wheraes 50% (or any other value) will give different results:

.red {
  width: 200px;
  height: 200px;
  background: red;
  box-shadow:0 0 0 100px yellow;
  display:inline-block;
  clip-path: circle(71%); 
  margin: 70px;
}

<div class='red'></div>
<div class='red' style="width:300px;"></div>
<div class='red' style="width:100px;"></div>
<div class='red' style="width:50px;"></div>

Using 50%

.red {
  width: 200px;
  height: 200px;
  background: red;
  clip-path: circle(50%); 
  border:2px solid;
  box-sizing:border-box;
}

<div class='red'></div>
<div class='red' style="width:300px;"></div>
<div class='red' style="width:100px;"></div>
<div class='red' style="width:50px;"></div>

We may also think that any value bigger than 71% is useless since it we will always give a circle bigger than our element. This is true but we should not forget that we have also the position.

Example of output using 100%,200% and even 300%!

.red {
  width: 200px;
  height: 200px;
  background: red;
  border:2px solid;
  box-sizing:border-box;
}

<div class='red' style="clip-path: circle(100% at  0    50%)"></div>

<div class='red' style="clip-path: circle(200% at -100% 50%)"></div>

<div class='red' style="clip-path: circle(300% at -200% 50%)"></div>


I will consider a different property to better clear the confusion which is radial-gradient.

.box {
  width:200px;
  height:200px;
  border:1px solid;
  background:radial-gradient(circle 50%, red ,blue);
}

<div class="box">

</div>

The below code is meant to define a circle with a radius equal to 50% but it's invalid because we don't know the reference:

Note: Percentages are not allowed here; they can only be used to specify the size of an elliptical gradient, not a circular one. This restriction exists because there is are multiple reasonable answers as to which dimension the percentage should be relative to. A future level of this module may provide the ability to size circles with percentages, perhaps with more explicit controls over which dimension is used.ref

We are dealing with rectangular shapes so we can use the height, the width, the radius you are calculating, etc, etc. A lot of options so they simply decided to make it invalid but for the clip-path they took a decision and defined a reference for the use of percetange.

By the way, you can have better control over your circle considering values like closest-side/farthest-side.

The below will always give us circle touching the closest sides (the same way as contain with background)

.red {
  width: 200px;
  height: 200px;
  background: red;
  clip-path:circle(closest-side); 
  border:2px solid;
  box-sizing:border-box;
}

<div class='red'></div>
<div class='red' style="width:300px;"></div>
<div class='red' style="width:100px;"></div>
<div class='red' style="width:50px;"></div>

The below will always give us circles touching the farthest sides (the same way as cover with background)

.red {
  width: 200px;
  height: 200px;
  background: red;
  clip-path:circle(farthest-side); 
  border:2px solid;
  box-sizing:border-box;
}

<div class='red'></div>
<div class='red' style="width:300px;"></div>
<div class='red' style="width:100px;"></div>
<div class='red' style="width:50px;"></div>


Combined with position they can give some intresting results:

.red {
  width: 200px;
  height: 200px;
  background: red;
  border:2px solid;
  box-sizing:border-box;
  transition:1s all;
}

<div class='red' style="clip-path:circle(farthest-side at left); "></div>
<div class='red' style="clip-path:circle(closest-side at 10% 10%); "></div>
<div class='red' style="clip-path:circle(farthest-side at top left); "></div>
<div class='red' style="clip-path:circle(closest-side at 40% 50%); "></div>

相关文章