Css只有条纹文字阴影效果

Ara*_*nel 5 css text effects cross-browser css3

是否有一种干净而优雅的方式来实现复古条纹文字阴影效果?

举个例子,我想用css重现这样的东西:

在此输入图像描述

我希望条纹与字体颜色不同.

Ana*_*Ana 6

近十年后看到这一点,让我们看看今天的情况如何!

\n

有两种选择:

\n
    \n
  1. 一个纯 CSS 的剪切到text. 和之前的回答有点不同。

    \n

    优点:

    \n
      \n
    • 超级容易理解
    • \n
    • 如果我们放弃透明度轮廓,就不会出现文本重复
    • \n
    • 可以设置阴影相对于font-size
    • \n
    \n

    缺点:

    \n
      \n
    • 无法准确再现效果
    • \n
    • 没有好的、简单的、标准的、跨浏览器的方法来获得真实且完全正确的透明度轮廓
    • \n
    \n
  2. \n
  3. filter使用 a来创建效果的SVG 。

    \n

    优点:

    \n
      \n
    • 准确再现效果
    • \n
    • 可以在文本周围切出真正的透明轮廓
    • \n
    • 大力支持
    • \n
    • 没有文本重复
    • \n
    \n

    缺点:

    \n
      \n
    • 无法将长阴影/3D 挤出偏移设置为相对于font-size
    • \n
    • 由于上述可扩展性限制和部分错误,事情并不那么简单(甚至不是完整!)解决方法的错误,事情并不那么简单
    • \n
    \n
  4. \n
\n

让我们来看看他们每个人吧!

\n

纯 CSS 通过剪切到text

\n

background我们首先在元素上设置条纹并将其剪切到text- 这就是条纹阴影。然后,我们设置text-shadow向左上方的偏移量 - 文本阴影绘制在实际文本下方(transparent在本例中是我们制作的),但在background(本例中的条纹)上方,因此该偏移量text-shadow位于条纹顶部并创建字母的主要部分。

\n

这里的技巧是,条纹“阴影”是实际文本,而“主要文本”实际上是用text-shadow.

\n

请注意,现在这background-clip: text是标准的,并且在所有浏览器中都可以在速记中不加前缀(至少在我测试过的所有现代桌面浏览器中,不确定移动设备,因为我没有智能手机)。

\n

\r\n
\r\n
@import url(\'https://fonts.googleapis.com/css2?family=Zilla+Slab:wght@700&display=swap\');\n\nhtml, body { display: grid }\n\nhtml { height: 100% }\n\nbody { background: #e2e7e3 }\n\n.striped {\n  --m: 2; /* font size multiplier */\n  --u: calc(var(--m)*1px*sqrt(2)); /* stripe unit */\n  --o: -.05em; /* shadow offset */\n  background: /* striped "shadow" is the actual text */\n    repeating-linear-gradient(45deg, \n        #000 0 var(--u), #0000 0 calc(2*var(--u))) text;\n  color: transparent; /* see through to stripes */\n  font: 700 \n    calc(var(--m)*clamp(1em, 30vmin, 10em))/ 1.125 \n    zilla slab, serif;\n  /* main text part is actually the shadow */\n  text-shadow: var(--o) var(--o) darkslategrey;\n  \n  /* layout & prettifying styles */\n  place-self: center;\n  letter-spacing: .1em;\n  text-align: center;\n  text-transform: uppercase;\n  text-wrap: balance\n}\n\n.allchar { --m: 1 }
Run Code Online (Sandbox Code Playgroud)\r\n
<div class=\'striped example\'>airbag</div>\n<div class=\'striped allchar\'>a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 & ! @ \xc2\xa3 $</div>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

(该背后的原因--u

\n

这很简单,而且看起来足够接近,即使它不一样- 在你的图像中,你实际上有一个散列的3D 挤压,而这种技术会产生一个散列的“阴影”

\n

我们的代码生成的 A 与原始图像中的 A 的比较

\n

视觉比较

\n

现在画出字母主要部分的轮廓。不幸的是,我能想到的在纯 CSS 中使其透明的唯一方法是复制文本(远非理想)并mask在 Firefox 和 Chrome 中以两种不同的方式使用,类似于我在几年前这篇关于表情符号玻璃形态的文章中有很多细节。

\n

在 Firefox 中,我们使用element()(这是标准的,但出于安全原因 Chrome 尚未实现这一点,并且 Firefox 仍然需要前缀-moz-)作为mask-image. 在 Chrome 中,我们使用非标准-webkit-mask-clip: text.

\n

您可以在这个 CodePen 演示中看到它是如何工作的- 它有很多特定于浏览器的代码(总共 30 个 CSS 声明,包含在特定于浏览器的代码中@supports),其中一些甚至不是标准的,所以我不能推荐使用它,我不会在这里转储到一个片段。

\n

即便如此,它仍然不是真正正确的透明轮廓 - 请参阅下面的箭头,我们只是在字母底部上方没有透明间隙。

\n

我们的代码生成的 A 与原始图像中的 A 的比较

\n

视觉比较

\n

SVGfilter解决方案

\n

我们首先向各个方向扩展文本,以获得被周围透明轮廓覆盖的区域。我们通过feMorphology使用dilate运算符来完成此操作。radius如果我们有一个很大的值,但我们只想要一个薄的透明轮廓,1-2 个像素就可以了,这会带来很多问题。

\n
<feMorphology operator=\'dilate\' radius=\'1.5\'/>\n
Run Code Online (Sandbox Code Playgroud)\n

接下来,我们在右下角创建 3D 挤压效果。这是通过使用n\xc3\x97n单位矩阵作为kernelMatrixfor feConvolveMatrix(其中也order 必须n),然后使用沿正方向(朝向右下角)的两个轴的n/2-size来完成的。feOffset

\n

我通常使用 Pug 来生成标记,这意味着我们也可以轻松地n\xc3\x97n根据n值生成单位矩阵:

\n
feConvolveMatrix(order=n \n                 kernelMatrix=(new Array(n*n)).fill(0) \n                                              .map((_, i) => 1*!(i%(n + 1))) \n                                              .join(\' \') \n                 divisor=\'1\')\nfeOffset(dx=.5*n dy=.5*n)\n
Run Code Online (Sandbox Code Playgroud)\n

如果您不想这样做,编译后的 HTML 看起来像这样n = 8

\n
<feConvolveMatrix order=\'8\' \n                  kernelMatrix=\'1 0 0 0 0 0 0 0 \n                                0 1 0 0 0 0 0 0 \n                                0 0 1 0 0 0 0 0 \n                                0 0 0 1 0 0 0 0 \n                                0 0 0 0 1 0 0 0 \n                                0 0 0 0 0 1 0 0 \n                                0 0 0 0 0 0 1 0 \n                                0 0 0 0 0 0 0 1\' \n                  divisor=\'1\'/>\n<feOffset dx=\'4\' dy=\'4\'/>\n
Run Code Online (Sandbox Code Playgroud)\n

是的,不漂亮......这就是我使用 Pug 的原因!

\n

接下来,我们从中减去第一步中的扩张文本,然后添加初始文本 (SourceGraphic在其之上添加初始文本 ( )。

\n

...我们有具有真正透明轮廓和 3D 挤压的文本!

\n

请注意,由于我们不将 SVG 用于除filter,因此我们可以将其从文档流中取出。

\n

\r\n
\r\n
<feMorphology operator=\'dilate\' radius=\'1.5\'/>\n
Run Code Online (Sandbox Code Playgroud)\r\n
feConvolveMatrix(order=n \n                 kernelMatrix=(new Array(n*n)).fill(0) \n                                              .map((_, i) => 1*!(i%(n + 1))) \n                                              .join(\' \') \n                 divisor=\'1\')\nfeOffset(dx=.5*n dy=.5*n)\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

等等,什么?条纹在哪里?好吧,我们只是还没有添加它们。

\n

在进一步进行之前,请注意如何将字母的放大版本剪掉(在将其原始版本添加回来之前)如何在字母周围留下真正透明的轮廓。

\n

屏幕截图突出显示字母周围有真正透明的轮廓

\n

现在让我们看看这些条纹!

\n

如果我们在轻松控制/调整其角度和厚度时想要任何控制或灵活性,我们需要从 CSS 添加这些条纹。

\n

我们需要这样做,因为正如您在上面看到的,无论字母大小如何,3D 挤出的大小始终相同。

\n

这是因为这里我们遇到了 SVG 不可缩放的部分。SVG 属性中的那些数字?膨胀半径、偏移......它们是固定px值!

\n

但是“SVG”中的“S”不应该代表“ S calable”吗?

\n

嗯,是的,SVG 过滤器确实有一个primitiveUnits可以设置的属性objectBoundingBox,在这种情况下,这些相同的值将变得相对于我们设置的框的尺寸filter。但这对于这个用例来说并没有任何好处。

\n

我们希望沿两个轴具有相同的偏移量,并且primitiveUnits设置为objectBoundingBox,除非包含文本的框的纵横比始终相同,否则我们无法实现此目的。我们的两个文本框都不是这种情况。

\n

屏幕截图突出显示文本框的边界

\n

此外,由于偏移量与盒子尺寸有关,这意味着盒子越大,偏移量就越大。但在我们的例子中,较大的框是带有更多但较小文本的框……我们希望为其设置更小的偏移量!

\n

在这一点上,当响应性如此重要时,SVG 就被抛在了后面——毕竟,CSSdrop-shadow()blur()过滤器允许我们使用相对于font-size它们所应用的元素的长度值,所以我认为 SVG 也应该如此。但现在还没有。

\n

目前,我们可以为不同的字体大小获得不同大小的挤压......是filter为每个字体大小创建一个单独的挤压。远非理想。

\n

那么让我们看看我们可以做些什么来至少控制 CSS 的条纹厚度!

\n

我们执行此操作的方法是将文本和 CSS 创建/可控条纹传递到 SVG,每个条纹都位于一个 RGB 通道中。是的,你听到的是对的!

\n

SVG 滤镜允许我们仅从 中提取一个通道SourceGraphic,因此我们将蓝色通道中的文本形状传递给 SVG B,将红色通道中的条纹传递给 SVG B

\n

最简单的方法是制作文本transparent,设置两个背景图层,一个完全blue剪切,text加上一个red覆盖整个框的条纹,然后使用lighten混合模式将它们混合。

\n

混合是一种逐像素进行的操作。我们分别对两个混合层的像素网格中的每个像素应用混合操作。

\n

显示混合的两个层的两个对应像素的插图,这会产生结果层的对应像素。

\n

混合如何进行

\n

lighten可分离的混合模式,这意味着它也单独应用于每个通道。

\n

因此,对于像素网格中行j和列i上的通用像素,我们采用顶层的通用通道和底层的C\xe2\x82\x81相应通道,两者之间的最大值(较亮)是应用时结果像素的结果通道混合模式。C\xe2\x82\x80lighten

\n

假设顶部是blue文本层,底部是red条纹层。

\n

首先取顶层。在文本之外,我们有透明度(rgba(0, 0, 0, 0))。文本本身是不透明的蓝色,所以rgb(0, 0, 255).

\n

然后取底层。在条纹之间,我们有black,所以就是rgb(0, 0, 0)。条纹本身就是red这样rgb(0, 0, 0)

\n

当文本 ( ) 外部的区域与条纹之间的( ) 区域rgba(0, 0, 0, 0)相交时,我们对于三个 RGB 通道有以下情况:blackrgb(0, 0, 0)

\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 0) = 0\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(0, 0) = 0\n
Run Code Online (Sandbox Code Playgroud)\n

所以结果是blackrgb(0, 0, 0)

\n

当文本 ( rgba(0, 0, 0, 0))之外的区域与red( rgb(0, 0, 255)) 条纹相交时,我们有:

\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 255) = 255\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(0, 0) = 0\n
Run Code Online (Sandbox Code Playgroud)\n

所以结果是redrgb(255, 0, 0)

\n

当蓝色文本本身 ( rgb(0, 0, 255))与条纹之间的black( ) 区域相交时,我们有:rgb(0, 0, 0)

\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 0) = 0\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(255, 0) = 255\n
Run Code Online (Sandbox Code Playgroud)\n

所以结果是bluergb(0, 0, 255)

\n

在蓝色文本本身 ( rgb(0, 0, 255)) 与red( rgb(0, 0, 255)) 条纹相交的地方,我们有:

\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 255) = 255\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(255, 0) = 255\n
Run Code Online (Sandbox Code Playgroud)\n

所以结果是rgb(255, 0, 255)或者magentafuchsia......甚至不用问,这些名字都是一团糟)

\n

\r\n
\r\n
<feConvolveMatrix order=\'8\' \n                  kernelMatrix=\'1 0 0 0 0 0 0 0 \n                                0 1 0 0 0 0 0 0 \n                                0 0 1 0 0 0 0 0 \n                                0 0 0 1 0 0 0 0 \n                                0 0 0 0 1 0 0 0 \n                                0 0 0 0 0 1 0 0 \n                                0 0 0 0 0 0 1 0 \n                                0 0 0 0 0 0 0 1\' \n                  divisor=\'1\'/>\n<feOffset dx=\'4\' dy=\'4\'/>\n
Run Code Online (Sandbox Code Playgroud)\r\n
@import url(\'https://fonts.googleapis.com/css2?family=Zilla+Slab:wght@700&display=swap\');\n\nhtml, body { display: grid }\n\nhtml { height: 100% }\n\nbody {\n  background: \n    url(\'https://images.unsplash.com/photo-1700607687506-5149877683e8?w=1400\') \n      50%/ cover fixed\n}\n\nsvg[height=\'0\'] { position: fixed } /* out of document flow */\n\n.text {\n  --m: 2; /* font size multiplier */\n  color: darkslategrey;\n  font: 700 \n    calc(var(--m)*clamp(1em, 28vmin, 10em))/ 1.125 \n    zilla slab, serif;\n  filter: url(#f);\n  \n  /* layout & prettifying styles */\n  place-self: center;\n  letter-spacing: .1em;\n  text-align: center;\n  text-transform: uppercase;\n  text-wrap: balance\n}\n\n.allchar { --m: 1 }
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

很酷,对吧?

\n

除了以下事实...如果您碰巧在 Firefox 或 Safari 中检查此内容,您可能会注意到它不起作用。

\n

结果应该是这样的:

\n

上述过程描述的混合结果

\n

但是,当涉及到多个图层时,只有一部分被剪切text(错误1481498background )时,Firefox 就会出现问题(错误 1481498),而当至少其中一个图层被剪切时,Safari 则无法混合图层text(错误267129)。

\n

所以...不幸的是,我们需要添加一个伪。

\n

我们保留文本blue,创建一个覆盖整个文本框的绝对定位伪,设置pointer-events: none为允许选择、右键单击以及文本上的所有好东西。然后将此伪内容与文本混合。

\n

\r\n
\r\n
<svg width=\'0\' height=\'0\'>\n  <filter id=\'f\'>\n    <feMorphology in=\'SourceAlpha\' operator=\'dilate\' radius=\'1.5\' result=\'dilate\'/>\n    <feConvolveMatrix order=\'8\' \n                      kernelMatrix=\'1 0 0 0 0 0 0 0 \n                                    0 1 0 0 0 0 0 0 \n                                    0 0 1 0 0 0 0 0 \n                                    0 0 0 1 0 0 0 0 \n                                    0 0 0 0 1 0 0 0 \n                                    0 0 0 0 0 1 0 0 \n                                    0 0 0 0 0 0 1 0 \n                                    0 0 0 0 0 0 0 1\' \n                      divisor=\'1\'/>\n    <feOffset dx=\'4\' dy=\'4\'/>\n    <feComposite in2=\'dilate\' operator=\'out\'/>\n    <feBlend in=\'SourceGraphic\'/>\n  </filter>\n</svg>\n\n<div class=\'text example\'>airbag</div>\n<div class=\'text allchar\'>a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 & ! @ \xc2\xa3 $</div>
Run Code Online (Sandbox Code Playgroud)\r\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 0) = 0\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(0, 0) = 0\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

这适用于跨浏览器!

\n

哦,我们应该在元素上设置一个完全black( rgb(0, 0, 0)) ,或者为了防止将伪元素与任何可以通过其父元素的透明看到的背景混合......如果我们有一个图像在:backgroundisolation: isolatebackgroundbackgroundbody

\n

与身体背景融为一体

\n

好吧...回到我们的 SVG filter

\n

我们给它提供这样的东西作为SourceGraphic输入。

\n

使用 提取蓝色通道中的文本feColorMatrix。对于 的默认类型matrix,该values属性采用空格分隔的 20 个值列表,用于计算生成的 RGBA 通道,每个值 5 个一组(前 5 个用于计算输出 R 通道,接下来 5 个用于计算输出 G 通道,依此类推) ...)。我们还想利用这个机会以我们希望的方式绘制文本,比如说以相同的方式darkslategrey(或rgb(47, 79, 79)rgb(18%, 31%, 31%)百分比 RGB 方式)。

\n

现在,对于 4 个通道中的每一个,其对应的矩阵中的 5 个值(称为v\xe2\x82\x80, v\xe2\x82\x81, v\xe2\x82\x82, v\xe2\x82\x83, v\xe2\x82\x84)与输入 RGBA 通道(更准确地说,使用百分比 RGBA 的十进制表示)和 相乘1

\n
R*v\xe2\x82\x80 + G*v\xe2\x82\x81 + B*v\xe2\x82\x82 + A*v\xe2\x82\x83 + 1*v\xe2\x82\x84\n
Run Code Online (Sandbox Code Playgroud)\n

现在,由于我们想随心所欲地绘制文本,因此对于前三批 5 个值,我们采用v\xe2\x82\x80v\xe2\x82\x81和为(意味着输入 RGBA 通道对输出 RGB 无关)和最终值v\xe2\x82\x82,,百分比 RGB 的相应十进制表示形式(即第一批以及第二批和第三批)。v\xe2\x82\x830v\xe2\x82\x84darkslategrey.18v\xe2\x82\x84.31

\n

至于 Alpha 通道,它位于1文本内(通道B1)和0其他位置,因此对于最后一批 5 个值,我们得到v\xe2\x82\x82(与通道相乘B)为1,其余为0

\n

这就是我们的feColorMatrix样子:

\n
<feColorMatrix values=\'0 0 0 0  .18 \n                       0 0 0 0  .31 \n                       0 0 0 0  .31 \n                       0 0 1 0 0\' result=\'basetext\'/>\n
Run Code Online (Sandbox Code Playgroud)\n

我们将其保存result为,\'basetext\'然后对其进行膨胀、拉伸、偏移、减去之前的膨胀并将其保存resultextruded.

\n

然后,我们red按照SourceGraphic提取文本的方式提取条纹blue(输出A通道仅位于1输入 的位置,因此对于最后一批 5 个值,我们取为,而所有其他值均为)并将其绘制为非常深灰色(比方说)。R1v\xe2\x82\x8010rgb(7%, 7%, 7%)

\n
<feColorMatrix values=\'0 0 0 0  .07 \n                       0 0 0 0  .07 \n                       0 0 0 0  .07 \n                       1 0 0 0 0\'/>\n
Run Code Online (Sandbox Code Playgroud)\n

然后,我们只将这些深灰色条纹保留挤压结果区域中。

\n

最后,我们将基本文本放在上面。就是这样!

\n

\r\n
\r\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 255) = 255\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(0, 0) = 0\n
Run Code Online (Sandbox Code Playgroud)\r\n
max(R\xe2\x82\x81, R\xe2\x82\x80) = max(0, 0) = 0\nmax(G\xe2\x82\x81, G\xe2\x82\x80) = max(0, 0) = 0\nmax(B\xe2\x82\x81, B\xe2\x82\x80) = max(255, 0) = 255\n
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

请注意,当我们进行此类feColorMatrix操作时,我们还需要设置color-interpolation-filterssRGB

\n