跳转到内容

文字适应纹理

刀刀

1/3/2025

0 字

0 分钟

目前有一个需求,需要文字适应背景图片的纹理,且能实现文字内容颜色自定义转换的功能。

咋一看该功能不好实现,一般情况下可以找设计师给 UI 图,但是无法适配纹理。因此还是需要回到前端自己实现,不过可以参考一下UI的实现思路——滤镜。

前端的滤镜第一时间先考虑到 filter ,但是找遍 filter 所有属性值都无法实现。因此继续往下思考,想到了 SVG 的滤镜。

SVG的滤镜效果更强大,可以说 CSS 的 filter 滤镜是 SVG 滤镜的子集,尝试一下。

SVG滤镜

首先写一个 svg 标签,标签内放置一个图片 image 和一个文本 text ,代码如下:

html
<svg viewBox="0 0 600 330">
  <Image
    href="./cat.png"
    x="0"
    y="0"
    width="100%"
    height="100%"
    preserveAspectRatio="none">
  </Image>
  <text
    x="50%"
    y="50%"
    font-size="10em"
    font-weight="bold"
    text-anchor="middle"
    alignment-baseline="middle"
    fill="#000">
    Logo
  </text>
</svg>

其中设置了图片标签的 x 轴和 y 轴位置、宽高缩放设置,设置了文本的 x 轴和 y 轴位置、字体大小和粗细、横轴纵轴对齐方式、字体颜色。

现在需要添加纹理,由于是让文字适配背景,因此要给文字添加滤镜。滤镜添加在 svg 内,通过 defsfilter 标签实现,为 filter 设置唯一值 id ,然后为 text 绑定。代码如下:

html
<svg viewBox="0 0 600 330">
  <defs> 
    <filter id="conform"></filter> 
  </defs> 
  <Image
    href="./cat.png"
    x="0"
    y="0"
    width="100%"
    height="100%"
    preserveAspectRatio="none">
  </Image>
  <text
    x="50%"
    y="50%"
    font-size="10em"
    font-weight="bold"
    text-anchor="middle"
    alignment-baseline="middle"
    fill="#000"
    filter="url(#conform)"> 
    Logo
  </text>
</svg>

现在刷新页面后文本内容不见了,这是必然的,因为 filter 内没设置任何东西,所以无效果,后续一步步来。

文字效果

首先,想要实现文字适配图片纹理,首先得要有一个图片滤镜,设置图片的路径以及其 x 轴和 y 轴起始坐标。最后通过 result 导出一个名称 ORIGIN_IMAGE 给后续的滤镜使用。

html
<feImage href="./cat.png"
  result="ORIGIN_IMAGE"
  x="0"
  y="0"
  width="100%"
  height="100%"
  preserveAspectRatio="none">
</feImage>

然后图片不需要太多颜色,设置为灰度图,通过 in 引入上一个滤镜的名称,再通过 result 导出名称 GRAY_IMAGE 给下一个滤镜使用。

html
<feColorMatrix in="ORIGIN_IMAGE"
  type="saturate"
  values="0"
  result="GRAY_IMAGE">
</feColorMatrix>

接着为文字设置置换滤镜,其本质就是改变文本里的每一个像素点。 in 引入原始图像 SourceGraphic (也就是 text 文本)和灰度图像 GRAY_IMAGE ,以灰度图作为参考,通过一个算法来实现偏移。该算法用大白话来说就是当参考点越黑,他就越往右下偏移;参考点越白他就越往左上偏移;不黑不白则不偏移。再通过 result 导出名称 TEXTURED_TEXT 给下一个滤镜使用。

html
<feDisplacementMap in="SourceGraphic"
  in2="GRAY_IMAGE"
  scale="15"
  xChannelSelector="R"
  yChannelSelector="R"
  result="TEXTURED_TEXT">
</feDisplacementMap>

紧接着要让文字和图片纹理贴合,需要 in 引入 TEXTURED_TEXT 把图像在写一遍,让图像和文本融合。再通过 result 导出名称 BG 给下一个滤镜使用。

html
<feImage href="./cat.png"
  in="TEXTURED_TEXT"
  x="0"
  y="0"
  width="100%"
  height="100%"
  preserveAspectRatio="none"
  result="BG">
</feImage>

再然后为滤镜添加不透明度,导出名称 OPACITY_TEXT

html
<feColorMatrix in="TEXTURED_TEXT"
  type="matrix"
  values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 9 0"
  result="OPACITY_TEXT">
</feColorMatrix>

最后得到不透明度的背景和融合成功的文本,把二者结合起来即可。

html
<feBlend in="BG"
  in2="OPACITY_TEXT"
  mode="multiply"
  result="BLENDED_TEXT">
</feBlend>

总体代码

点击查看代码
html
<template>
  <svg viewBox="0 0 600 330">
    <defs>
      <filter id="conform">
        <feImage href="./cat.png"
          result="ORIGIN_IMAGE"
          x="0"
          y="0"
          width="100%"
          height="100%"
          preserveAspectRatio="none">
        </feImage>
        <feColorMatrix in="ORIGIN_IMAGE"
          type="saturate"
          values="0"
          result="GRAY_IMAGE"></feColorMatrix>
        <feDisplacementMap in="SourceGraphic"
          in2="GRAY_IMAGE"
          scale="15"
          xChannelSelector="R"
          yChannelSelector="R"
          result="TEXTURED_TEXT"></feDisplacementMap>
        <feImage href="./cat.png"
          in="TEXTURED_TEXT"
          x="0"
          y="0"
          width="100%"
          height="100%"
          preserveAspectRatio="none"
          result="BG">
        </feImage>
        <feColorMatrix in="TEXTURED_TEXT"
          type="matrix"
          values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 9 0"
          result="OPACITY_TEXT"></feColorMatrix>
        <feBlend in="BG"
          in2="OPACITY_TEXT"
          mode="multiply"
          result="BLENDED_TEXT"></feBlend>
      </filter>
    </defs>
    <Image href="./cat.png"
      x="0"
      y="0"
      width="100%"
      height="100%"
      preserveAspectRatio="none">
    </Image>
    <text x="50%"
      y="50%"
      font-size="10em"
      font-weight="bold"
      text-anchor="middle"
      alignment-baseline="middle"
      fill="orange"
      filter="url(#conform)">
      Logo
    </text>
  </svg>
</template>

功能拓展

效果实现后,可以来点功能拓展,比如文本自定义、文本颜色自定义、背景图修改等。这些可以尝试一下。

总体效果