最近,在寻找关于编写什么代码的一些想法时,因为我的艺术感为零,所以我唯一能做的就是找到其他人想出的漂亮东西,并用干净紧凑的代码重新制作它们……我遇到了这些糖果鬼按钮!
它们似乎是我可以快速编码的一个很酷的小东西的完美选择。不到十五分钟后,这是我的 Chromium 结果:
纯 CSS 糖果幽灵按钮。
我认为这项技术值得分享,因此在本文中,我们将介绍我最初是如何做到的以及我们还有哪些其他选择。
起点
一个按钮是用…创建的,你准备好了吗?一个button元素!这个button元素有一个data-ico属性,我们可以在其中放置一个表情符号。它还具有--slist在style属性中设置的停止列表自定义属性 。
<button data-ico="" style="--slist: #ffda5f, #f9376b">boo!</button>
写完这篇文章后,我了解到 Safari 有很多剪切到 的问题text,即它对button元素不起作用,或者对具有display: flex(也许grid也是?)的元素不起作用,更不用说元素子元素的文本了。遗憾的是,这意味着这里介绍的所有技术在 Safari 中都失败了。唯一的解决方法是将button此处的所有样式应用到span嵌套在 中的元素上button,覆盖其父元素的border-box. 而且,如果这可以帮助其他像我一样使用 Linux 而无法物理访问 Apple 设备的人(除非你把 iPhone 5 算在四楼的人——你不想为这样的东西烦恼的人超过反正一个月两次——最近买的),我也学会了使用在未来顿悟。感谢布赖恩的建议!
对于 CSS 部分,我们将图标添加到::after伪元素中,并在 上使用grid布局button以使文本和图标对齐。在 上button,我们还设置了 a border、 a padding、 a border-radius,使用停止列表--slist, 为对角线渐变并美化了font。
button {
display: grid;
grid-auto-flow: column;
grid-gap: .5em;
border: solid .25em transparent;
padding: 1em 1.5em;
border-radius: 9em;
background:
linear-gradient(to right bottom, var(--slist))
border-box;
font: 700 1.5em/ 1.25 ubuntu, sans-serif;
text-transform: uppercase;
&::after { content: attr(data-ico) }
}
关于上面的代码,有一点需要澄清。在突出显示的行上,我们将 thebackground-origin和 the都设置background-clip为border-box。background-origin两者都将0 0点放在background-position它设置的框的左上角,并为我们提供了与其尺寸background-size相对的框。
也就是说,如果background-origin设置为padding-box,则0 0for的点background-position位于 的左上角padding-box。如果background-origin设置为border-box,则 的0 0点background-position位于 的左上角border-box。如果background-origin被设置为padding-box,一个background-size的50% 25%装置50%的的padding-box宽度和25%所述的padding-box高度。如果background-origin被设置为border-box,同样background-size的50% 25%手段50%的的border-box宽度和25%所述的border-box高度。
为默认值background-origin是padding-box,这意味着默认大小100% 100%渐变将覆盖padding-box再重演下面的border(在这里我们看不到它,如果border是完全不透明)。然而,在我们的例子中,border是完全的transparent,我们希望我们的梯度延伸到整个border-box. 这意味着我们需要将background-origin值更改为border-box。
应用基本样式后的结果(现场演示)。
简单但可悲的是非标准 Chromium 解决方案
这涉及使用mask三层并将它们合成。如果您需要复习mask合成,可以查看此速成课程。
请注意,在 CSSmask图层的情况下,只有 alpha 通道很重要,因为遮罩元素的每个像素都获得相应mask像素的 alpha ,而 RGB 通道不会以任何方式影响结果,因此它们可能是任何有效的价值。下面,你可以看到的效果purple,以transparent渐变叠加与使用完全相同的梯度作为的效果mask。
渐变叠加与相同的渐变蒙版(现场演示)。
我们将从底部的两层开始。第一个是完全不透明的层,完全覆盖整个border-box,这意味着它的 alpha1绝对无处不在。另一个也是完全不透明的,但限制(通过使用mask-clip)到padding-box,这意味着,虽然该层在整个 中的 alpha 为 1 padding-box,但它完全transparent在该border区域中,在0那里有一个 alpha 。
如果你很难想象这一点,一个很好的技巧是将元素的布局框视为嵌套矩形,如下图所示。
布局框(现场演示)。
在我们的例子中,底层1在整个橙色框( )上是完全不透明的(alpha 值为border-box)。我们放置在第一层之上的第二层是完全不透明的(alpha 值为1),整个红色框(the padding-box)和完全transparent(alpha 为0)在padding限制和border限制之间的区域中。
关于这些框的限制,一件非常酷的事情是圆角由 决定border-radius(并且,在 的情况下padding-box,也由 决定border-width)。下面的交互式演示对此进行了说明,我们可以在其中看到 的圆角是如何border-box由border-radius值给出的,而 的圆角padding-box计算为border-radius减去 the border-width(在0差值为负值的情况下限制为)。
现在让我们回到我们的mask图层,一个在整个 中完全不透明border-box,而它上面的一个在整个padding-box区域完全不透明并且在该border区域(在padding限制和border限制之间)完全透明。这两个层使用exclude操作(xor在非标准 WebKit 版本中调用)合成。
两个基础层(现场演示)。
在两层的 alpha 为0或的情况下,此操作的名称非常具有启发性1,就像在我们的例子中一样——第一层1的 alpha 无处不在,而第二层的 alpha(我们放在第一)的顶部是1内部的padding-box并0在border所述区域之间padding限制和border限制。
在这种情况下,应用布尔逻辑的规则非常直观——对两个相同的值0进行异或运算得到,而对两个不同的值进行异或运算得到1。
在整个 中padding-box,第一层和第二层的 alpha 均为1,因此使用此操作将它们合成为我们0在该区域中生成的层的 alpha为 。但是,在该border区域(在padding限制之外,但在border限制内)中,第一层的 alpha 为1,而第二层的 alpha 为0,因此我们1在该区域中得到的结果层的 alpha为 。
下面的交互式演示对此进行了说明,您可以在其中切换查看mask以 3D 方式分离的两个图层,以及使用此操作查看堆叠和合成的图层。
把东西变成代码,我们有:
button {
/* same base styles */
--full: linear-gradient(red 0 0);
-webkit-mask: var(--full) padding-box, var(--full);
-webkit-mask-composite: xor;
mask: var(--full) padding-box exclude, var(--full);
}
在我们进一步讨论之前,让我们讨论一些关于上述 CSS 的微调细节。
首先,由于完全不透明的层可以是任何东西(alpha 通道是固定的,总是1和 RGB 通道不重要),我通常制作它们red——只有三个字符!同样,使用圆锥渐变而不是线性渐变也可以为我们节省一个字符,但我很少这样做,因为我们仍然有支持 masking但不支持圆锥渐变的移动浏览器。使用线性可确保我们获得全面支持。好吧,除了 IE 和 Chromium Edge 之前的版本,但话又说回来,无论如何,没有多少很酷的闪亮的东西在这些东西中起作用。
其次,我们对两个层都使用渐变。我们没有background-color为底部使用一个普通的,因为我们不能background-clip为它background-color本身设置一个单独的。如果我们要将background-image图层裁剪到padding-box,那么这个background-clip值也将应用于background-color下面——它也会被裁剪到padding-box,我们无法让它覆盖整个border-box。
第三,我们没有mask-clip为底层明确设置一个值,因为这个属性的默认值正是我们在这种情况下想要的值,border-box。
第四,我们可以mask-composite在mask速记中包含标准(Firefox 支持),但不能包含非标准(WebKit 浏览器支持)。
最后,我们始终将标准版本设置为最后,以便它覆盖可能也受支持的任何非标准版本。
到目前为止,我们的代码的结果(此时仍然是跨浏览器的)如下所示。我们还在background-image根上添加了一个,这样很明显我们在整个padding-box区域都有真正的透明度。
屏蔽整个padding-box(现场演示)后的结果。
这不是我们想要的。虽然我们有一个很好的渐变border(顺便说一下,这是我获得渐变的首选方法,border因为我们在整个过程中都有真正的透明度padding-box,而不仅仅是封面),但我们现在缺少文本。
因此,下一步是使用mask前一层之上的另一层重新添加文本,这一次仅限于text(同时也使实际文本完全,transparent以便我们可以background通过它看到渐变)和 XOR 这第三mask层与前两个异或的结果(结果可以在上面的屏幕截图中看到)。
下面的交互式演示允许查看mask在 3D 中分离以及堆叠和合成的三个图层。
请注意,textfor的值mask-clip是非标准的,因此,遗憾的是,这仅适用于 Chrome。在 Firefox 中,我们只是不再对按钮进行任何屏蔽,并且制作了 text transparent,我们甚至不会优雅地降级。
button {
/* same base styles */
-webkit-text-fill-color: transparent;
--full: linear-gradient(red 0 0);
-webkit-mask: var(--full) text, var(--full) padding-box, var(--full);
-webkit-mask-composite: xor;
/* sadly, still same result as before :( */
mask: var(--full) padding-box exclude, var(--full);
}
如果我们不想以button这种方式使我们无法使用,我们应该将应用mask和 制作文本的代码transparent放在一个@supports块中。
button {
/* same base styles */
@supports (-webkit-mask-clip: text) {
-webkit-text-fill-color: transparent;
--full: linear-gradient(red 0 0);
-webkit-mask: var(--full) text, var(--full) padding-box, var(--full);
-webkit-mask-composite: xor;
}
}
使用仅屏蔽方法的最终结果(现场演示)。
我真的很喜欢这种方法,它是我们目前拥有的最简单的方法,我真的希望它text是一个标准值,mask-clip并且所有浏览器都正确支持它。
然而,我们还有其他一些实现糖果幽灵按钮效果的方法,尽管它们比我们刚刚讨论的非标准 Chromium-only 更复杂或更有限,但它们也得到了更好的支持。让我们来看看这些。
额外的伪元素解决方案
这涉及设置与以前相同的初始样式,但是,我们不使用 ,而是将mask剪切background到该text区域。
button {
/* same base styles */
background:
linear-gradient(to right bottom, var(--slist))
border-box;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent
}
就像以前一样,我们还需要制作实际的 text transparent,这样我们就可以看到它background后面的柔和渐变,现在被剪裁到了它的形状。
淘汰赛按钮文本(现场演示)。
好的,我们有渐变文本,但现在我们缺少渐变border。因此,我们将使用绝对定位的::before伪元素添加它,该伪元素覆盖 的整个border-box区域button并继承border,border-radius并background从其父元素(除了background-clip,它被重置为border-box)。
$b: .25em;
button {
/* same as before */
position: relative;
border: solid $b transparent;
&::before {
position: absolute;
z-index: -1;
inset: -$b;
border: inherit;
border-radius: inherit;
background: inherit;
background-clip: border-box;
content: '';
}
}
inset: -$b是以下的简写:
top: -$b;
right: -$b;
bottom: -$b;
left: -$b
请注意,我们在此处使用带减号的border-width值 ( $b)。该0值将使margin-box假的(等同于border-box在这种情况下,因为我们没有margin对::before)仅覆盖padding-box其button母公司,我们希望它覆盖整个border-box。此外,正方向是向内,但我们需要向外走 aborder-width才能从padding极限到border极限,因此是负号 – 我们向负方向前进。
我们还在z-index这个绝对定位的元素上设置了一个否定,因为我们不希望它位于button文本的顶部并阻止我们选择它。在这一点上,文本选择是我们区分文本和 的唯一方法background,但我们很快就会解决这个问题!
添加渐变伪后的结果(现场演示)。
请注意,由于伪元素内容不可选择,因此选择仅包括按钮的实际文本内容,而不包括::after伪元素中的表情符号。
下一步是添加一个两层,它们之间mask有一个exclude合成操作,以便只留下border这个伪元素的区域可见。
button {
/* same as before */
&::before {
/* same as before */
--full: linear-gradient(red 0 0);
-webkit-mask: var(--full) padding-box, var(--full);
-webkit-mask-composite: xor;
mask: var(--full) padding-box exclude, var(--full);
}
}
这几乎是我们button在前一种方法的一个中间阶段中为实际所做的。
使用额外的伪方法(现场演示)的最终结果。
我发现在大多数情况下,这是最好的方法,当我们想要跨浏览器的东西并且不包括 IE 或 Chromium Edge 之前,它们都不支持屏蔽。
该border-image解决方案
在这一点上,你们中的一些人可能会在屏幕上尖叫,::before当我们可以使用渐变border-image来创建这种幽灵按钮时,没有必要使用伪元素——这是一种已经使用了四分之三以上的策略十年!
但是,border-image用于药丸形状的按钮有一个很大的问题:此属性与 不兼容border-radius,如下面的交互式演示中所示。一旦我们border-image在一个元素上设置了 a border-radius,我们就会失去 的角舍入border,即使通过,有趣的是,background仍然会尊重这个舍入。
尽管如此,在不需要圆角或所需圆角最多为border-width.
在没有圆角的情况下,除了删除现在毫无意义的border-radius,我们不需要太多改变初始样式:
button {
/* same base styles */
--img: linear-gradient(to right bottom, var(--slist));
border: solid .25em;
border-image: var(--img) 1;
background: var(--img) border-box;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
结果可以在下面看到,跨浏览器(即使在 Chromium Edge 之前也应该支持)。
使用border-image方法(现场演示)的无圆角结果。
所需圆角小于 的技巧border-width取决于border-radius工作方式。当我们设置这个属性时,我们设置的半径代表border-box. 如果该差为正,则padding-box(即 的内部圆角border)的角的圆角为border-radius负数,border-width否则为0(无圆角)。这意味着border如果border-radius小于或等于 ,我们没有内部舍入border-width。
在这种情况下,我们可以将该inset()函数用作clip-path值,因为它还提供了圆角剪切矩形角的可能性。如果您需要复习此功能的基础知识,可以查看下图:
如何inset()函数工作。
inset()剪掉由元素边缘的距离定义的裁剪矩形之外的所有内容border-box,指定方式与我们指定的相同margin,border或者padding(使用一个、两个、三个或四个值)以及此矩形的圆角,指定相同我们会指定方式border-radius(任何有效的border-radius价值也是有效的在这里)。
在我们的例子中,到 边缘的距离border-box是全部0(我们不想从 的任何边缘切掉任何东西button),但是我们有一个舍入,最多必须与 一样大,border-width这样就没有任何内部border舍入都是有道理的。
$b: .25em;
button {
/* same as before */
border: solid $b transparent;
clip-path: inset(0 round $b)
}
请注意,clip-path也将删除我们可能在button元素上添加的任何外部阴影,无论它们是通过box-shadow还是filter: drop-shadow().
使用border-image方法(现场演示)的小角圆角结果。
虽然这种技术不能达到药丸形状的外观,但它确实具有当今强大的支持优势,并且在某些情况下可能是我们所需要的。
到目前为止讨论的三个解决方案可以在下面的演示中看到编译,该演示还附带一个 YouTube 链接,如果您更喜欢通过观看在视频上构建的东西而不是阅读它们来学习,您可以看到我从头开始编写每个解决方案.
所有这些方法都padding-box在文本外部创造了真正的透明度,因此它们适用于background我们在button. 但是,我们还有一些其他方法可能值得一提,尽管它们在该部门受到限制。
封面解决方案
就像border-image方法一样,这是一种非常有限的策略。除非我们background在button.
它涉及使用不同background-clip值对背景进行分层,就像渐变边框的覆盖技术一样。唯一的区别是,在这里我们在模拟元素background后面的图层之上再添加一个渐变图层,button并将此顶层剪辑为text。
$c: #393939;
html { background: $c; }
button {
/* same as before */
--grad: linear-gradient(to right bottom, var(--slist));
border: solid .25em transparent;
border-radius: 9em;
background: var(--grad) border-box,
linear-gradient($c 0 0) /* emulate bg behind button */,
var(--grad) border-box;
-webkit-background-clip: text, padding-box, border-box;
-webkit-text-fill-color: transparent;
}
遗憾的是,这种方法在 Firefox 中由于一个旧错误而失败——只是没有应用任何内容background-clip,同时使文本transparent产生一个没有可见文本的药丸状按钮。
全background-clip覆盖解决方案(现场演示)。
我们仍然可以通过border在::before伪和background-clip: text实际上使用渐变的覆盖方法使其跨浏览器button,这基本上只是我们讨论的第二个解决方案的更有限版本 – 我们仍然需要使用伪,但是,因为我们使用盖子,而不是mask,它只有background在button.
$b: .25em;
$c: #393939;
html { background: $c; }
button {
/* same base styles */
--grad: linear-gradient(to right bottom, var(--slist));
border: solid $b transparent;
background: var(--grad) border-box;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
&::before {
position: absolute;
z-index: -1;
inset: -$b;
border: inherit;
border-radius: inherit;
background: linear-gradient($c 0 0) padding-box,
var(--grad) border-box;
content: '';
}
}
从好的方面来说,这个更受限制的版本也应该适用于 Chromium Edge 之前的版本。
background按钮后面的实体的伪封面解决方案(现场演示)。
下面,还有固定background版本。
$f: url(balls.jpg) 50%/ cover fixed;
html { background: $f; }
button {
/* same as before */
&::before {
/* same as before */
background: $f padding-box,
var(--grad) border-box
}
}
固定background按钮后面的伪封面解决方案(现场演示)。
总的来说,我不认为这是最好的策略,除非我们都符合background限制并且我们需要在不支持遮罩但支持将 剪切background到 的浏览器中重现效果text,例如前 Chromium Edge。
混合溶液
这种方法是另一种有限的一个,因为它不会工作,除非每个那就是每一个可见的像素梯度,其通道具有要么全部被大或比所有比的对应的像素较小的值background下面的button。然而,这并不是最糟糕的限制,因为它可能会导致我们的页面具有更好的对比度。
在这里,我们首先制作我们想要渐变的部分(即文本、图标和border)white或black,这取决于我们分别拥有带有浅渐变的深色主题还是带有深色渐变的浅色主题。的其余部分button(文本和图标周围的区域,但在 内部border)与之前选择的相反color(white如果我们将color值设置为black和black否则)。
在我们的例子中,我们有一个漂亮的渐变光button在一个黑暗的background,所以我们先从white文本,图标和边界,并black为background。我们的两个梯度停止点的十六进制通道值是ff( R), da( G), 5f( B) 和f9( R), 37( G), 6b( B),所以我们background对通道值至多与min(ff, f9) = f9红色一样大的任何像素都是安全的,min(da, 37) = 37因为绿色和min(5f, 6b) = 5f蓝色。
这意味着background-color在我们的后面有一个button小于或等于f9,37和 的通道值,或者单独5f作为一个实体background,或者在background-image我们使用multiply混合模式混合的层下面(这总是产生至少与两层中较深的一层)。我们将它设置background在一个伪元素上,因为与实际混合body或html在 Chrome 中不起作用。
$b: .25em;
body::before {
position: fixed;
inset: 0;
background: url(fog.jpg) 50%/ cover #f9375f;
background-blend-mode: multiply;
content: '';
}
button {
/* same base styles */
position: relative; /* so it shows on top of body::before */
border: solid $b;
background: #000;
color: #fff;
&::after {
filter: brightness(0) invert(1);
content: attr(data-ico);
}
}
需要注意的是使图标完全white指使其先black用brightness(0),然后反转这个black用invert(1)。
在black和white按钮(现场演示)。
然后我们添加一个渐变::before伪元素,就像我们为第一个跨浏览器方法所做的那样。
button {
/* same styles as before */
position: relative;
&::before {
position: absolute;
z-index: 2;
inset: -$b;
border-radius: inherit;
background: linear-gradient(to right bottom, var(--slist);
pointer-events: none;
content: '';
}
}
唯一的区别是,这里不是给它一个负数z-index,而是给它一个正数z-index。这样它不仅在实际的button,而且在::after伪上,我们设置pointer-events为none以允许鼠标与button下面的实际内容进行交互。
在black和white按钮顶部添加渐变伪后的结果(现场演示)。
现在下一步是保留black我们的部分button,但用渐变替换white部分(即文本、图标和border)。我们可以使用darken混合模式来做到这一点,其中两层是带有::after图标的黑白按钮和其上的渐变伪。
对于每个 RGB 通道,此混合模式采用两个图层的值,并使用较暗(较小)的一个作为结果。由于一切都比 暗white,因此生成的图层使用该区域中的渐变像素值。由于black是暗于一切,所得层是black无处不在button的black。
button {
/* same styles as before */
&::before {
/* same styles as before */
mix-blend-mode: darken;
}
}
“几乎在那里”的结果(现场演示)。
好的,但如果background后面button是纯black. 否则,在 abackground的每个像素都比我们的 上的渐变的相应像素更暗的情况下button,我们可以应用第二个混合模式,这次是lighten在实际上button(以前,我们darken在::before伪上)。
对于每个 RGB 通道,此混合模式采用两个图层的值,并使用较亮(较大)的图层作为结果。因为任何事情比更轻black,所得层使用background后面button到处button是black。并且由于要求是 的每个渐变像素button都比其background后面的相应像素更亮,因此生成的图层使用该区域的渐变像素值。
button {
/* same styles as before */
mix-blend-mode: lighten;
}
深色背景上的浅色幽灵按钮(现场演示)。
对于buttonlight 上的暗渐变background,我们需要切换混合模式。也就是说,使用lighten的::before假,并darken在button自身。首先,我们需要确保background后面button足够轻。
假设我们的梯度介于#602749和之间#b14623。我们梯度站的通道值是60( R), 27( G), 49( B) 和b1( R), 46( G), 23( R),所以background后面的button通道值需要至少max(60, b1) = b1为红色、max(27, 46) = 46绿色和max(49, 23) = 49蓝色。
这意味着background-color在我们button的通道值上有一个大于或等于b1, 46and 49,无论是作为一个实体background,还是在一个background-image层下面,使用screen混合模式(它总是产生至少与两层)。
我们还需要制作button边框、文本和图标black,同时将其设置background为white:
$b: .25em;
section {
background: url(fog.jpg) 50%/ cover #b14649;
background-blend-mode: screen;
}
button {
/* same as before */
border: solid $b;
background: #fff;
color: #000;
mix-blend-mode: darken;
&::before {
/* same as before */
mix-blend-mode: lighten
}
&::after {
filter: brightness(0);
content: attr(data-ico);
}
}
::after伪元素中的图标是black通过filter: brightness(0)在其上设置来制作的。
浅色背景上的黑色幽灵按钮(现场演示)。
我们还可以选择将所有button图层混合为其 的一部分background,无论是浅色还是深色主题,但是,如前所述,Firefox 只会忽略任何background-clip声明,其中text是值列表的一部分,而不是单个值。
嗯,就是这样!我希望你有(或有)一个可怕的万圣节。我发现的所有错误绝对让我感到可怕……或重新发现,以及同时它们尚未修复的现实。
文章版权声明
1 原创文章作者:0008,如若转载,请注明出处: https://www.52hwl.com/35690.html
2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈
3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)
4 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

微信扫一扫
支付宝扫一扫