如何在某些区域制作单个 运行-of-text 并在其他区域制作实体填充?

How can I make a single run-of-text outlined in some areas and a solid-fill in other areas?

我最近遇到了这个 dribbble/landing page concept with hollow/filled text。

首先,我不完全确定这个概念是否可以在 CSS 中重新创建。 Google 确实让我找到了 CSS 文本掩码,但我找不到任何 post 可以真正重现这种效果。

我如何重建 hollow/filled 文本,具体取决于文本背后的背景是否有图像?

  • 可以在pure-HTML+CSS中完成(没有任何SVG)。

    • 它也可以在纯 SVG 中完成。
      • SVG-only 选项会更简单,因为 SVG 具有更强大的混合和遮罩功能,HTML 元素(CSS 框)(CSS 框)目前还不能使用这些功能.
  • 我决定为 HTML+CSS 做这件事,因为我觉得这是一个挑战。

    • 这个 HTML 版本是动画的,文本是完全可选的。
    • 这个HTML+CSS版本可以进一步简化by using mask-image: element(#target) which意味着我们不需要mask-only文本,不幸的是 Chrome 似乎还不支持 element()(但是 Firefox 支持)。
  • 一个小 warttext-stroke 轮廓不完全 line-up 纯白色文本(至少在 Chrome 在 Windows 10 上,我的计算机版本 Helvetica 为 96dpi,但在同一浏览器和计算机中为 192dpi(2x,又名 Retina))看起来很完美。

  • 以下是它在我的机器上以 96dpi 分辨率查看动画中不同点的方式:

  • 下面的示例实现适用于以下浏览器(在撰写本文时):

    • Chrome 98
    • 边 98
    • 火狐 97
    • macOS Safari 技术预览(第 137 版)
      • 有点 在 macOS Safari 15.2 中工作,但由于某些原因你必须先点击它。
      • 它在 iOS Safari 15.1 中不起作用(只有轮廓文本可见,实心文本根本不呈现)。
      • 因为它 确实 在 Safari 预览浏览器中工作,所以它应该在 iOS Safari 和 macOS Safari 的下一次更新中工作,但是。

 body {
background-color: #dbdac2;
--solid-white: linear-gradient(white,white);
 }
#container,
#container > #div1 {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;

    background-size:
        153px 302px,
        148px 302px,
        154px 302px;

    background-position:
        131px 94px,
        309px 28px,
        480px 94px;

    background-repeat:
        no-repeat,
        no-repeat,
        no-repeat;

    animation: moveImages 2s infinite;
    animation-direction: alternate;
}

#container {
    border: 1px solid white;
    position: relative;
    width: 711px;
    height: 440px;

    /* These are the 3 photo images, rendered as separate background-image layers: */
    background-image:
        url( "https://i.stack.imgur.com/hmwyh.png" ),
        url( "https://i.stack.imgur.com/JeHEg.png" ),
        url( "https://i.stack.imgur.com/pVgz6.png" );
    }
    #container p {
        margin: 0;
        position: static;
        padding-top: 192px;
        padding-left: 62px;
        overflow: hidden;

        font-family: Helvetica;
        font-size: 99px;
        letter-spacing: -2px;
        font-weight: 600;
    }
        #container > #pStroke {
            text-stroke: 1px white;
            -webkit-text-stroke: 1px white;
            color: transparent;
        }

        #container > #div1 {
            /* #div1's background-image layers must match #container's: */
            background-image:
                var(--solid-white),
                var(--solid-white),
                var(--solid-white);

            -webkit-background-clip: text;
            background-clip: text;
            color: transparent;
        }

@keyframes moveImages {
    /* The `@keyframes from {}` rule is optional, btw. */
    to {
        background-size:
            53px 302px,
            58px 302px,
            154px 302px;

        background-position:
            431px 94px,
            209px 28px,
            280px 194px;
    }
}
<div id="container">

  <div id="div1">
    <p id="pWhite">Fashion Give<br />Impression.</p>
  </div>
  
  <p id="pStroke">Fashion Give<br />Impression.</p>

</div>

解释:

  • div#container 元素有一个 background-image 属性 设置为 3 个不同的来源图像(使用模因图像代替您发布的示例中的时尚照片) .

    • 这些图像可以通过 background-sizebackground-position 属性独立定位和缩放。
  • 另一个元素(#div1)通过absolute定位覆盖div#container并且具有相同的background-sizebackground-position属性,但是使用单个纯白色背景图像(来自 linear-gradient(white,white))而不是照片,然后这 3 个白色背景图像层被 #div1 的内部 <p> 文本使用 background-clip: text;.

    • 这个例子必须使用background-image: linear-gradient(或background-image: url("1x1px-white.png");)而不是background-color: white;,因为它需要在3个不同的层中重复,而 background-color: white; 不能用于定义背景内的矩形区域,也不能有 多个 background-color 层(即使 semi-transparent)。
    • 该示例还必须使用DOM文本来遮盖白色矩形——而不是使用白色矩形来遮盖DOM文本——因为CSS-in-HTML 不支持任何类型的 reversed 文本掩码,否则这会容易得多。不过,我相信这在 SVG 中很容易实现。
    • #div1<p> 元素用于通过仅使用其内部 padding 而不是 position: absolute; 来正确定位文本,因为定位的文本不能与 background-clip: text;,不幸的是。
  • 带有文本副本的另一个 <p> 元素用于描边文本(text-stroke: 1px white;)

  • 虽然文本内容在 HTML 源中被复制,但 3 个图像(及其各自的白色蒙版)的更繁琐的大小和位置信息不需要被复制到CSS,还好;感谢 CSS 的选择器如何工作(因为 #container#div1background-sizebackground-position 属性由单个 CSS 规则设置).

可能的替代方法:

  • 不是为纯白色部分使用重复的相同大小的 background-image 层,而是单个(但非常复杂)clip-path 跟踪 3 个框(就像一条线画得像 etch-a-sketch) 可以用在 #pWhite 上,但这不可能是可动画的。

  • 我认为最好的方法是这样的:

    • 为 3 张照片使用 3 个单独的 <img /> 元素(而不是 background-image)并在新的 <div id="images">.
    • 中使用 absolute 定位
    • 然后纯白色文本 <p>(在 div#images 的兄弟元素中)将 absolute-ly 定位在 div#images 之上 z-index并用 mask-image: element(#images);
    • 掩盖
    • 但是Chrome似乎不​​支持在HTML+CSS中使用element(),据我所知只有Firefox支持。
    • 描边文本将作为重复的 <p> 元素保留在 div#images 后面。
    • 这种方法的另一个优点是 <img/> 元素s 可以使用 transform: translate 定位,而不必通过 background-positionposition: absolute 来定位,这会产生更好的性能和帧率。
  • 不过,我想不出任何不需要复制文本内容的方法 - 至少直到 CSS 的 content: 属性允许元素从其他元素复制文本。