透明文本从背景中剪切出来

113

有没有办法用CSS实现像下面图片中一样的透明文字从背景上切出来的效果?
如果只用图片替换文本,将会损失宝贵的SEO。

Transparent text cut out of background

我最初想到的是阴影,但我无法想出任何办法...
该图片是网站背景,一个绝对定位的标签。

由于图像替换文本,您可能会失去所有宝贵的SEO。还有一些现有的图像替换技术,仍然非常友好。顺便说一句:实际上,字母背后的背景也需要保持透明,这就是问题所在... - feeela
如果可能的话,这将是一个很好的练习,可以使用CSS... - Jessica Lingmn
我不认为<h1><img alt="Some Text" /></h1><h1>Some Text</h1>更不利于SEO。传统上,图像的问题在于它们只是被随意地放置在页面上,没有支持的标记。 - cimmanon
@cimmanon 是的,你说得对。我可能可以使用图像而不失去SEO分数,但仍然无法选择文本,在页面上搜索会更加复杂,并且更新文本将需要更多的工作... :/ - Love Dager
2
只是让你知道,Google完全支持CSS图像替换技术:http://mezzoblue.com/archives/2008/05/05/image_replac/ - cimmanon
1
不确定是否相同:https://css-tricks.com/how-to-do-knockout-text/ - mbomb007
14个回答

54
尽管使用 CSS 也可以实现此功能,但更好的方法是使用 内联 SVGSVG 遮罩。这种方法比 CSS 有一些优点: 代码演示:SVG 文本遮罩

transparent text clipping background

body,html{height:100%;margin:0;padding:0;}
body{
  background:url('https://farm9.staticflickr.com/8760/17195790401_94fcf60556_c.jpg');
  background-size:cover;
  background-attachment:fixed;
}
svg{width:100%;}
<svg viewbox="0 0 100 60">
  <defs>
    <mask id="mask" x="0" y="0" width="100" height="50">
      <rect x="0" y="0" width="100" height="40" fill="#fff"/>
      <text text-anchor="middle" x="50" y="18" dy="1">SVG</text>
      <text text-anchor="middle" x="50" y="30" dy="1">Text mask</text>
    </mask>
  </defs>
  <rect x="5" y="5" width="90" height="30" mask="url(#mask)" fill-opacity="0.5"/>    
</svg>

如果您想使文本可选择和可搜索,您需要将其包含在<defs>标签之外。以下示例显示了一种方法,可以使用<use>标签保持透明文本:

body,html{height:100%;margin:0;padding:0;}
body{
  background:url('https://farm9.staticflickr.com/8760/17195790401_94fcf60556_c.jpg');
  background-size:cover;
  background-attachment:fixed;
}
svg{width:100%;}
<svg viewbox="0 0 100 60">
  <defs>
    <g id="text">
      <text text-anchor="middle" x="50" y="18" dy="1">SVG</text>
      <text text-anchor="middle" x="50" y="30" dy="1">Text mask</text>
    </g>
    <mask id="mask" x="0" y="0" width="100" height="50">
      <rect x="0" y="0" width="100" height="40" fill="#fff"/>
      <use xlink:href="#text" />
    </mask>
  </defs>
  <rect x="5" y="5" width="90" height="30" mask="url(#mask)" fill-opacity="0.5"/>
  <use xlink:href="#text" mask="url(#mask)" />
</svg>


2
这似乎是目前跨浏览器(IE11、FF、Chrome、Safari)唯一可行的解决方案,而且不需要像网络上提供的许多其他解决方案那样将图像作为背景。 - Timo Kähkönen
1
受到这个答案的启发,我制作了一个关键帧动画,在白色圆角矩形上旋转透明的SVG文字。背景有文本和图像。请前往查看:http://jsbin.com/nipuqu/3/ (世界锦标赛即将到来:)) - Timo Kähkönen
2
不错的解决方案,但由于“选择文本并在页面上搜索”似乎是OP的要求之一,您可能希望将<text>包含在<defs>部分之外。(顺便说一句,我不确定SE爬虫如何处理<defs>中的SVG内容)。这里有一种方法可以保持OP想要的功能,并利用可怕的FF漏洞:https://jsfiddle.net/tfneqxxb/ - Kaiido
如果文本是动态的,它不会自动调整宽度。 - Imran Bughio
1
@ImranBughio 不是这样的。SVG文本不像普通的HTML文本那样工作。您需要对其进行定位。有关更多信息,请参见此处 - web-tiki

54

使用 CSS3 是有可能实现的,但并非所有浏览器都支持。

使用 background-clip: text; 可以将背景应用于文本,但需要与页面背景进行对齐。

body {
    background: url(http://www.color-hex.com/palettes/26323.png) repeat;
    margin:10px;
}
h1 { 
    background-color:#fff;
    overflow:hidden;
    display:inline-block; 
    padding:10px; 
    font-weight:bold;
    font-family:arial;
    color:transparent;
    font-size:200px;
}
span { 
    background: url(http://www.color-hex.com/palettes/26323.png) -20px -20px repeat;
    -webkit-text-fill-color: transparent;
    -webkit-background-clip: text;
    display:block;
}
<h1><span>ABCDEFGHIKJ</span></h1>

http://jsfiddle.net/JGPuZ/1337/


自动对齐

使用一些 JavaScript,您可以自动对齐背景:

$(document).ready(function(){
  //Position of the header in the webpage
  var position = $("h1").position();
  var padding = 10; //Padding set to the header
  var left = position.left + padding;
  var top = position.top + padding;
  $("h1").find("span").css("background-position","-"+left+"px -"+top+"px"); 
});
body {
    background: url(http://www.color-hex.com/palettes/26323.png) repeat;
    margin:10px;
}
h1 { 
    background-color:#fff;
    overflow:hidden;
    display:inline-block; 
    padding:10px; 
    font-weight:bold;
    font-family:arial;
    color:transparent;
    font-size:200px;
}
span { 
    background: url(http://www.color-hex.com/palettes/26323.png) -20px -20px repeat;
    -webkit-text-fill-color: transparent;
    -webkit-background-clip: text;
    display:block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1><span>ABCDEFGHIKJ</span></h1>

http://jsfiddle.net/JGPuZ/1336/


你可能需要稍微修改一下代码,具体取决于情况,现在它可能会与其他元素产生冲突,而且这个代码是很快写出来的,如果你有任何问题,我会听取并解答。 - Gijs
好的,我对jQuery非常熟悉(好吧...差不多)。我可能会为它编写一个漂亮的小插件!另外谢谢你,已标记为正确! - Love Dager
3
虽然答案是一个非常好的解决方案,但请勿忽视该答案中提到的 background-clip:text 选项只适用于Webkit浏览器,并非标准选项。CSS3不允许在此属性中使用 text 值(请参见:http://stackoverflow.com/a/17224852)。 - tanius
确认在火狐浏览器中这个方法不起作用。 - larpo
1
这种技术允许您查看文本通常所在的元素背景。我正在寻找一种方法,通过文本通常所在的位置来查看文本下面的元素。 - Vince
@larpo 在我的 Firefox 中可以工作... 或许标准已经改变了? - wizzwizz4

45

使用纯CSS有一种简单的方法可以做到这一点:

background: black;
color: white;
mix-blend-mode: multiply;

对于黑色背景上的透明文本,或者

background: white;
color: black;
mix-blend-mode: screen;

为了在白色背景上显示透明文本,请在文本元素上应用这些样式,并将任何您想要的背景放置在其后面。

示例 CodePen 示例截图

阅读有关mix-blend-mode的内容并尝试使用它来使用不同的颜色。

注意事项:

  1. 要使此功能在Chrome中正常工作,还需要显式设置html元素的背景颜色。
  2. 除IE外,这在基本上所有现代浏览器上都有效(可查看支持情况)

5
对于白色背景和黑色颜色,请在mix-blend-mode上使用color-dodgelightenscreen - Imran Bughio
1
@ZachSaucier 谢谢,我已经更新了答案,提到现在需要在 html 元素上设置背景才能在 Chrome 中工作。 - Nic
我在html元素上没有设置背景,但在Chrome中它运行良好! - Marco Castanho
真他妈的,非常感谢你这个。在我看来,第一个回答使用文本元素的背景并对齐它是最糟糕的方法(例如,如果你正在使用动画渐变作为背景),使用SVG文本遮罩要好得多,但仍然有点麻烦。这只需两行CSS代码就解决了问题,如果你有黑色或白色背景的话! - Alessandro
真他妈的,非常感谢你这个。在我看来,第一个使用文本元素背景并对齐的答案绝对是最糟糕的方法(例如,如果你正在使用动画渐变作为背景),使用SVG文本遮罩要好得多,但仍然有些麻烦。这只需两行CSS代码就解决了问题,如果你的背景是黑色或白色的话! - undefined

3

只需放置该CSS即可。

    .banner-sale-1 .title-box .title-overlay {
      font-weight: 900;
      overflow: hidden;
      margin: 0;
      padding-right: 10%;
      padding-left: 10%;
      text-transform: uppercase;
      color: #080404;
      background-color: rgba(255, 255, 255, .85);

      /* that css is the main think (mix-blend-mode: lighten;)*/
      mix-blend-mode: lighten;

    }

3
我刚刚尝试了一个新方法来解决这个问题,虽然我不是百分之百确定它的原理(如果有人想要解释一下,请帮忙解释)。看起来这种方法很有效,并且不需要使用双背景或JavaScript。

Here's the code: JSFIDDLE

body {
  padding: 0;
  margin: 0;
}

div {
  background: url(http://www.color-hex.com/palettes/26323.png) repeat;
  width: 100vw;
  height: 100vh;
}

body::before {
  content: '$ALPHABET';
  left: 0;
  top: 0;
  position: absolute;
  color: #222;
  background-color: #fff;
  padding: 1rem;
  font-family: Arial;
  z-index: 1;
  mix-blend-mode: screen;
  font-weight: 800;
  font-size: 3rem;
  letter-spacing: 1rem;
}
<div></div>


请返回翻译后的文本:dup of https://dev59.com/gWYr5IYBdhLWcg3wIG1u#35107973 - depperm
返回的文本是:https://stackoverflow.com/a/35107973/3462319 的副本。 - undefined

3

在不久的将来,我们可以使用 element() 来实现这一点。

element() 函数允许作者使用文档中的元素作为图像。随着引用元素外观的更改,图像也会相应更改 ref

诀窍是创建一个带有文字的普通 div,然后结合使用 element()mask

下面是一个基本示例,目前只适用于最新版本的 Firefox。

#text {
  font-size:35px;
  font-weight:bold;
  color:#000;
  font-family:sans-serif;
  text-transform: uppercase;
  white-space:nowrap;
  /* we hide it */ 
  position:fixed;
  right:200vw;
  bottom:200vh
}


body {
  background:url(https://picsum.photos/id/1018/800/800) center/cover; 
}

.main {
  margin:50px;
  height:100px;
  background:red;
  -webkit-mask:
    -moz-element(#text) center/contain no-repeat, /* this behave like a background-image*/
    linear-gradient(#fff 0 0);
  mask-composite:exclude;
}
<div id="text">
You can put your text here
</div>

<div class="main">

</div>

它将会产生如下结果:

CSS透明文本覆盖背景图片

它是响应式的,因为我们依赖基本的背景属性,可以使用基本的CSS轻松更新文本。
我们可以考虑任何类型的内容,并创建图案:

#text {
  font-size:30px;
  font-weight:bold;
  color:#000;
  font-family:sans-serif;
  text-transform: uppercase;
  white-space:nowrap;
  padding:20px;
  /* we hide it */ 
  position:fixed;
  right:200vw;
  bottom:200vh
}
#text span {
  font-family:cursive;
  font-size:35px;
}


body {
  background:url(https://picsum.photos/id/1018/800/800) center/cover; 
}

.main {
  margin:50px;
  height:100px;
  background:red;
  -webkit-mask:
    -moz-element(#text) 0 0/20% auto, /* this behave like a background-image*/
    linear-gradient(#fff 0 0);
  mask-composite:exclude;
}
<div id="text">
Your <span>text</span> here 
</div>

<div class="main">

</div>

背景下的CSS文字遮罩

为什么不加入一些动画来创建无限滚动的文本呢:

#text {
  font-size:30px;
  font-weight:bold;
  color:#000;
  font-family:sans-serif;
  text-transform: uppercase;
  white-space:nowrap;
  padding:20px 5px;
  /* we hide it */ 
  position:fixed;
  right:200vw;
  bottom:200vh
}


body {
  background:url(https://picsum.photos/id/1018/800/800) center/cover; 
}

.main {
  margin:50px;
  height:100px;
  padding-right:calc(50% - 50px);
  background:red;
  -webkit-mask:
    -moz-element(#text) 0 50%/200% auto content-box, /* this behave like a background-image*/
    linear-gradient(#fff 0 0);
  mask-composite:exclude;
  animation:m 5s linear infinite;
}

@keyframes m{
  to {-webkit-mask-position:200% 50%}
}
<div id="text">
Srolling repeating text here 
</div>

<div class="main">

</div>

animated text using CSS mask


3

这是可能的,但目前只适用于基于Webkit的浏览器(Chrome,Safari,Rockmelt,任何基于Chromium项目的浏览器)。

诀窍是在白色元素中有一个与body相同背景的元素,然后在内部元素上使用-webkit- background-clip: text;,这基本上意味着“不要将背景延伸到文本之外”,并使用透明文本。

section
{
    background: url(http://norcaleasygreen.com/wp-content/uploads/2012/11/turf-grass1.jpg);
    width: 100%;
    height: 300px;
}

div
{
    background: rgba(255, 255, 255, 1);
    color: rgba(255, 255, 255, 0);

    width: 60%;
    heighT: 80%;
    margin: 0 auto;
    font-size: 60px;
    text-align: center;
}

p
{
    background: url(http://norcaleasygreen.com/wp-content/uploads/2012/11/turf-grass1.jpg);
    -webkit-background-clip: text;
}
​

http://jsfiddle.net/BWRsA/


是的,这还有另一个问题。它仍在开发中,我真诚地希望我们很快能看到类似的东西! :) - Kyle
此外,您始终可以在图像上覆盖透明文本,以便仍然可以选择它。但我确信这对于SEO来说是不好的做法...隐藏信息/关键字等。 - Kyle
我相信在Webkit浏览器中它看起来非常漂亮,但对于其他人来说,它只是一个半透明的白色框覆盖在一些草上。 - cimmanon
没有优雅的回退,这在生产中就是100%无用的。这不像box-shadowborder-radius,如果浏览器不支持这些属性,页面仍然可用,只是看起来不那么漂亮。但这个属性不是这种情况。 - cimmanon
1
啊,拜托各位……是的,我计划在生产中使用它。但当然不会没有备选方案!@cimmanon - Love Dager
显示剩余3条评论

2
您可以使用反色字体,并使用font-face="…" CSS规则应用它。您可能需要调整字母间距以避免字母之间出现小的白色间隙。
如果您不需要特定的字体,那就简单了。下载一个喜欢的字体,例如从这个反色字体收藏中。
如果您需要特定的字体(比如"Open Sans"),那就有点难了。您需要将现有的字体转换为反色版本。这可以手动使用Font Creator、FontForge等工具完成,但当然我们希望有一个自动化的解决方案。我还没有找到相关的指示,但有一些提示:

1

Demo Screenshot

我需要制作与原始帖子完全相同的文本,但我不能仅通过对齐背景来伪造它,因为该元素后面有一些动画效果。似乎还没有人提出这个建议,所以这就是我所做的:(尝试使其尽可能易于阅读。)

var el = document.body; //Parent Element. Text is centered inside.
var mainText = "THIS IS THE FIRST LINE"; //Header Text.
var subText = "THIS TEXT HAS A KNOCKOUT EFFECT"; //Knockout Text.
var fontF = "Roboto, Arial"; //Font to use.
var mSize = 42; //Text size.

//Centered text display:
var tBox = centeredDiv(el), txtMain = mkDiv(tBox, mainText), txtSub = mkDiv(tBox),
ts = tBox.style, stLen = textWidth(subText, fontF, mSize)+5; ts.color = "#fff";
ts.font = mSize+"pt "+fontF; ts.fontWeight = 100; txtSub.style.fontWeight = 400;

//Generate subtext SVG for knockout effect:
txtSub.innerHTML =
"<svg xmlns='http://www.w3.org/2000/svg' width='"+stLen+"px' height='"+(mSize+11)+"px' viewBox='0 0 "+stLen+" "+(mSize+11)+"'>"+
    "<rect x='0' y='0' width='100%' height='100%' fill='#fff' rx='4px' ry='4px' mask='url(#txtSubMask)'></rect>"+
    "<mask id='txtSubMask'>"+
        "<rect x='0' y='0' width='100%' height='100%' fill='#fff'></rect>"+
        "<text x='"+(stLen/2)+"' y='"+(mSize+6)+"' font='"+mSize+"pt "+fontF+"' text-anchor='middle' fill='#000'>"+subText+"</text>"+
    "</mask>"+
"</svg>";

//Relevant Helper Functions:
function centeredDiv(parent) {
    //Container:
    var d = document.createElement('div'), s = d.style;
    s.display = "table"; s.position = "relative"; s.zIndex = 999;
    s.top = s.left = 0; s.width = s.height = "100%";
    //Content Box:
    var k = document.createElement('div'), j = k.style;
    j.display = "table-cell"; j.verticalAlign = "middle";
    j.textAlign = "center"; d.appendChild(k);
    parent.appendChild(d); return k;
}
function mkDiv(parent, tCont) {
    var d = document.createElement('div');
    if(tCont) d.textContent = tCont;
    parent.appendChild(d); return d;
}
function textWidth(text, font, size) {
    var canvas = window.textWidthCanvas || (window.textWidthCanvas = document.createElement("canvas")),
    context = canvas.getContext("2d"); context.font = size+(typeof size=="string"?" ":"pt ")+font;
    return context.measureText(text).width;
}

只需将其放入window.onload中,将body的背景设置为您的图像,然后观看魔术发生!


1
您可以使用myadzel的Patternizer jQuery插件在各种浏览器中实现此效果。目前,仅使用CSS无法跨浏览器完成此操作。
要使用Patternizer,请将class="background-clip"添加到您希望将文本绘制为图像模式的HTML元素中,并在其他data-pattern="…"属性中指定图像。请参见演示的源代码。Patternizer将创建一个SVG图像,其中包含填充文本的模式,并将其作为底层呈现到透明渲染的HTML元素下面。
如果,如问题示例图像所示,文本填充模式应是超出“patternized”元素的背景图像的一部分,则我看到两个选项(未经测试,首选):
  • 使用遮罩代替SVG中的背景图像。web-tiki's answer所述,使用Patternizer仍会自动生成SVG并在顶部添加一个不可见的HTML元素,以允许文本选择和复制。
  • 或者使用模式图像的自动对齐。 可以使用类似于Gijs's answer中的JavaScript代码完成。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接