在一些 web 活动页中经常能看到特殊处理的标题文字,比如这样的
image-20210918103841918
暂时忽略掉特殊字体,通过设计稿的图层样式可以发现,共有 3 个文字特效,分别是渐变、描边、投影
image-20210918105003942
作为一个有追求的前端,当然不会直接用图片啦~ 这里分别用 CSS 和 SVG 两种方式来实现,一起看看吧
温馨提示:文章细节较多,不感兴趣的也可以直接跳到底部查看在线 demo
一、CSS 文字渐变
首先来看 CSS 中的实现。
CSS 中并没有直接的属性来设置文字渐变,通常文字只能是纯色。不过可以通过背景裁剪 background-clip让背景色在文本区域显示出来,看着就像是文字有了渐变
<pclass=”text”>为你定制发现精彩</p>.text{background-image:linear-gradient(#FFCF02,#FF7352);background-clip:text;-webkit-background-clip:text;}
但是这样没什么效果,文字仍然是默认颜色
image-20210918113056809
原因其实很简单,由于是裁剪的背景,最后展示的其实是背景颜色,有颜色的文字覆盖在背景之上,所以这里需要将文字颜色设置为透明就行了,用 color 和 -webkit-text-fill-color都可以实现。
.text{background-image:linear-gradient(#FFCF02,#FF7352);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;/*需要文字透明*/}
这样就可以看到文字渐变效果了
image-20210918113558250二、SVG 文字渐变
再来看看 SVG 中的文字渐变。
SVG 中天然支持文字渐变,完全可以把文字当成普通的矢量路径,结构如下
<svg><text>为你定制发现精彩</text></svg>
直接通过 fill填充就行了,不过需要注意的是这里填充稍微麻烦一点,渐变不能像 CSS 那样,必须使用专门的渐变标签 <linearGradient>,有兴趣的可以查看 linearGradient – SVG | MDN (mozilla.org)[1],需定义在 <defs></defs>中
<svg><defs><linearGradientid=”gradient”><stopoffset=”0%”stop-color=”#FFCF02″/><stopoffset=”100%”stop-color=”#FF7352″/></linearGradient></defs><textclass=”text”>为你定制发现精彩</text></svg>
<linearGradient>中的 <stop> 标签用来定义渐变的颜色坡度,offset 和 stop-color分别定义渐变的节点和颜色,然后通过 fill属性填充渐变(指明 id )
.text{fill:url(#gradient);}
效果如下(并不是图片加载有问题)
image-20210918150224988
这样下来有两个问题
文本水平方向和垂直方向都不居中渐变方向是水平向右的
首先看第一个问题。SVG 中对文字的自适应处理还是非常弱的,比如 CSS 中常见的自动换行 SVG 中只能手动在指定位置换行。这里居中需要用到两个属性 text-anchor和dominant-baseline,分别为文本锚点对齐和文本基线对齐,简单来说就是水平和垂直方向的对齐方式
.text{text-anchor:middle;dominant-baseline:middle;fill:url(#gradient);}
同时 <text>还需要设置 x、y位置,这里的百分比可以和 CSS 中的背景位置百分比做类比
<textclass=”text”x=”50%”y=”50%”>为你定制发现精彩</text>
这样就居中显示了
image-20210918151535204
关于渐变方向的问题,SVG 中是用x1、y1、x2、y2两组坐标来确定的。给定一个矩形,左上角是 [0,0],右下角是 [1, 1],这样任意角度都可以表示出来了
image-20210918151901299
比如现在需要垂直向下方向的,那么可以在<linearGradient>设置 x1=”0″ y1=”0″ x2=”0″ y2=”1″,如下
<svg><defs><linearGradientid=”gradient”x1=”0″y1=”0″x2=”0″y2=”1″><stopoffset=”0%”stop-color=”#FFCF02″/><stopoffset=”100%”stop-color=”#FF7352″/></linearGradient></defs><textclass=”text”>为你定制发现精彩</text></svg>
效果如下
image-20210918152238267三、CSS 文字描边
CSS 中有个专门用于文字描边的属性 -webkit-text-stroke,可以控制描边的宽度和颜色,比如
.text{-webkit-text-stroke:2px#333;}
效果如下
image-20210918154946939
确实有描边了,但是文字好像瘦了一圈,如果觉得不太明显,可以再设置大一点
image-20210918155647207
从这里可以看出,-webkit-text-stroke其实是 居中描边,并且是覆盖在文本上的,也无法更改描边方式。而事实上,很多设计工具都是可以选择描边方式的,比如 figma
image-20210918160301899
那么,如何实现外描边效果呢?
也是可以的!用两层文本,一层文本描边,一层文本渐变就可以了,为了节省标签,可以用伪元素来生成
<pclass=”text”data-title=”为你定制发现精彩”>为你定制发现精彩</p>
.text::before{content:attr(data-title);position:absolute;background-image:linear-gradient(#FFCF02,#FF7352);background-clip:text;-webkit-background-clip:text;-webkit-text-fill-color:transparent;-webkit-text-stroke:0;}.text{-webkit-text-stroke:6px#333;}
叠加示意如下
Kapture 2021-09-19 at 12.32.04
改变不同的描边也不会出现文字“变瘦”的情况
image-20210919123457209四、SVG 文字描边
SVG 也可以实现描边效果,和 CSS 比较类似,应该说 CSS 是借鉴 SVG 的,通过 stroke 和 stroke-width来控制描边颜色和大小,比如
.text{/*其他*/stroke-width:4px;stroke:#333;}
可以得到这样的效果
image-20210919124453144
和 CSS 表现一样,都是居中描边,也无法改变。
不一样的是,SVG 控制更为灵活,默认是先填充、然后再描边,所以看着是描边在填充之上,但是,我们可以改变这种规则,设置先描边,再填充,那么填充的颜色就会覆盖在描边之上了。SVG 中改变这种规则的可以通过 paint-order[2] 来设置,关于这个属性,有兴趣的可以访问张鑫旭老师的这篇文章:CSS paint-order祝大家元旦快乐[3]
.text{/*其他*/stroke-width:4px;stroke:#333;paint-order:stroke;/*先描边*/}
这样就实现了外描边效果,是不是比 CSS 方便许多?
image-20210919125130810
除此之外,SVG 还可以设置描边路径的转角处的形状,比如 figma 中关于转角的设置如下
image-20210919125653465
SVG 中与之相对应的属性叫做 stroke-linejoin[4],这里是圆角,可以做如下设置
.text{/*其他*/stroke-width:4px;stroke:#333;paint-order:stroke;/*先描边*/stroke-linejoin:round;/*路径转角为圆角*/}
各种属性效果如下
image-20210919130558691五、CSS 文字投影
继续添加效果。CSS 可以通过 text-shadow来添加文本投影
.text{-webkit-text-stroke:6px#333;text-shadow:04px0#333;}
结果变成了这样
image-20210919131243404
原因其实还和文本渐变有关,渐变其实是背景色,文字是透明的,所以给文字添加阴影,结果阴影就覆盖在了背景之上。除了使用text-shadow,还可以通过 drop-shadow滤镜实现
.text{-webkit-text-stroke:6px#333;filter:drop-shadow(04px0#333);}
这样就完美实现了
image-20210919131815511六、SVG 文字投影
SVG 就比较灵活了,比如上面使用的 drop-shadow滤镜,其实就是借鉴了 SVG 中的 [5]滤镜,所以 SVG 也可以这样实现
<svg><defs><linearGradientid=”gradient”x1=”0″y1=”0″x2=”0″y2=”1″><stopoffset=”0%”stop-color=”#FFCF02″/><stopoffset=”100%”stop-color=”#FF7352″/></linearGradient><filterid=”shadow”><feDropShadowdx=”0″dy=”4″stdDeviation=”0″flood-color=”#333″/></filter></defs><textx=”50%”y=”50%”class=”text”>为你定制发现精彩</text></svg>
这里dx、dy、stdDeviation、flood-color和 drop-shadow(dx,dy,stdDeviation,flood-color)中的参数是一一对应的,就不多说明了,然后在文字中应用滤镜
.text{/*其他*/filter:url(#shadow);}
这样也能实现文字投影
image-20210919132653973
其实 SVG 中大可不必这么麻烦,刚才上面 text-shadow之所以不能使用,就是因为 CSS 实现的文字渐变是背景,是假的文字渐变,但是 SVG 中是真真正正的渐变填充,所以没错,这里可以直接用 CSS 中的 text-shadow 来实现,SVG 和 CSS 现在很多属性和样式都互通了,如下
.text{/*其他*/fill:url(#gradient);text-shadow:04px0#333;}
实现更加简洁
image-20210919132959524七、特殊字体处理
通常活动标题会采用一些特殊的字体,英文字体还好,整个引入都可以,但是中文就不行了,大多数中文字体都非常大,可能达到几十MB或者几百MB。其实我们只需要用到出现的字体,如果把出现的文字这一部分的特殊字体单独提取出来,那么整个字体文件将大大减小,这个过程就叫做字体子集化。
那么该如何处理呢?
这里推荐一个工具 Fontmin – 字体子集化方案[6],关于字体子集化的原理,可以参考这篇文章:性能优化魔法师:中文字体实践篇 – 掘金[7]
image-20210922192856814
下载客户端后,导入字体文件.ttf,然后输入需要用到的文字,如下
image-20210922193402350
image-20210922193601713
其中第一个以-embed为后缀的 CSS,里面是转换 base64 后的文件,可以直接引入
这样几乎实现了和设计稿完全一致的效果
image-20210922194407525
CSS 实现可以访问 text-css (codepen.io)[8]
SVG 实现可以访问 text-svg (codepen.io)[9]
八、总结和说明
以上介绍了 CSS 和 SVG 两种不同的方式来实现文本的特殊效果,从效果来看,显然 SVG 要更胜一筹,比如描边更加平滑、也无需多层嵌套,但 CSS 也有优势,比如渐变色和投影更加简单,总结一下
CSS 文字渐变本质是背景裁剪,需要将文字颜色设为透明SVG 文字天然支持渐变填充,需要借助 linearGradient 标签SVG 文本居中稍微麻烦点,需要借助 text-anchor 和 dominant-baselineCSS 和 SVG 描边都是居中描边,并且无法改变CSS 外描边可以通过多层结构叠加实现SVG 可以通过 paint-order 让填充绘制在描边之上CSS 文本阴影在文字透明时会穿透过来,可以用 drop-shadow 模拟投影SVG 中的 feDropShadow 和 CSS 中的 drop-shadow 类似SVG 可以直接用 CSS 中的 text-shadow 实现文字投影字体子集化 fontmin
参考资料[1]
linearGradient – SVG | MDN (mozilla.org):https://developer.mozilla.org/zh-CN/docs/Web/SVG/Element/linearGradient
[2]
paint-order:https://developer.mozilla.org/zh-CN/docs/Web/CSS/paint-order
[3]
CSS paint-order祝大家元旦快乐:https://www.zhangxinxu.com/wordpress/2020/01/css-paint-order/
[4]
stroke-linejoin:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/stroke-linejoin
[5]
:https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDropShadow
[6]
Fontmin – 字体子集化方案:http://ecomfe.github.io/fontmin/
[7]
性能优化魔法师:中文字体实践篇 – 掘金:https://juejin.cn/post/6844904073905307656
[8]
text-css (codepen.io):https://codepen.io/xboxyan/pen/PojRjPg
[9]
text-svg (codepen.io):https://codepen.io/xboxyan/pen/ZEyxyQL