JasonSubmara

JasonSubmara 查看完整档案

杭州编辑蚌埠学院  |  光电信息科学与工程 编辑ISAS 前端G  |  Web前端开发工程师 编辑 jasonsubmara.github.io/marablog/ 编辑
编辑

微信公众号: ISAS 前端G

Github:https://github.com/JasonSubMara

个人动态

JasonSubmara 赞了文章 · 4月19日

CSS @property,让不可能变可能

本文主要讲讲 CSS 非常新的一个特性,CSS @property,它的出现,极大地增强的 CSS 的能力!

根据 MDN -- CSS Property,@property CSS at-rule 是 CSS Houdini API 的一部分, 它允许开发者显式地定义他们的 CSS 自定义属性,允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承。

CSS Houdini 又是什么呢,CSS Houdini 开放 CSS 的底层 API 给开发者,使得开发者可以通过这套接口自行扩展 CSS,并提供相应的工具允许开发者介入浏览器渲染引擎的样式和布局流程中,使开发人员可以编写浏览器可以解析的 CSS 代码,从而创建新的 CSS 功能。当然,它不是本文的重点,不过多描述。

CSS Property 如何使用呢?我们将通过一些简单的例子快速上手,并且着重介绍它在 CSS 动画中起到的关键性的作用,对 CSS 动画带来的巨大提升。

示例

正常而言,我们定义和使用一个 CSS 自定义属性的方法是这样的:

:root {
    --whiteColor: #fff;
}

p {
    color: (--whiteColor);
}

而有了 @property 规则之后,我们还可以像下述代码这样去定义个 CSS 自定义属性:

<style>
@property --property-name {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}

p {
    color: var(--property-name);
}
</style>

简单解读下:

  • @property --property-name 中的 --property-name 就是自定义属性的名称,定义后可在 CSS 中通过 var(--property-name) 进行引用
  • syntax:该自定义属性的语法规则,也可以理解为表示定义的自定义属性的类型
  • inherits:是否允许继承
  • initial-value:初始值

其中,@property 规则中的 syntax 和 inherits 描述符是必需的。

当然,在 JavaScript 内定义的写法也很简单,顺便一提:

<script>
CSS.registerProperty({
  name: "--property-name",
  syntax: "<color>",
  inherits: false,
  initialValue: "#c0ffee"
});
</script>

支持的 syntax 语法类型

syntax 支持的语法类型非常丰富,基本涵盖了所有你能想到的类型。

  • length
  • number
  • percentage
  • length-percentage
  • color
  • image
  • url
  • integer
  • angle
  • time
  • resolution
  • transform-list
  • transform-function
  • custom-ident (a custom identifier string)

syntax 中的 +#| 符号

定义的 CSS @property 变量的 syntax 语法接受一些特殊的类型定义。

  • syntax: '<color#>' :接受逗号分隔的颜色值列表
  • syntax: '<length+>' :接受以空格分隔的长度值列表
  • syntax: '<length | length+>':接受单个长度或者以空格分隔的长度值列表

OK,铺垫了这么多,那么为什么要使用这么麻烦的语法定义 CSS 自定义属性呢?CSS Houdini 定义的自定义变量的优势在哪里?下面我们一一娓娓道来。

使用 color syntax 语法类型作用于渐变

我们来看这样一个例子,我们有这样一个渐变的图案:

<div></div>
div {
    background: linear-gradient(45deg, #fff, #000);
}

我们改造下上述代码,改为使用 CSS 自定义属性:

:root {
    --colorA: #fff;
    --colorB: #000;
}
div {
    background: linear-gradient(45deg, var(--colorA), var(--colorB));
}

得到的还是同样的一个渐变图:

我们再加上一个过渡效果:

:root {
    --colorA: #fff;
    --colorB: #000;
}
div {
    background: linear-gradient(45deg, var(--colorA), var(--colorB));
    transition: 1s background;
    
    &:hover {
        --colorA: yellowgreen;
        --colorB: deeppink;
    }
}

看看鼠标 Hover 的时候,会发生什么:

虽然我们设定了 1s 的过渡动画 transition: 1s background,但是很可惜,CSS 是不支持背景渐变色的直接过渡变化的,我们得到的只是两帧之间的之间变化。

使用 CSS @property 进行改造

OK,接下来我们就是有本文的主角,使用 Houdini API 中的 CSS 自定义属性替换原本的 CSS 自定义属性。

简单进行改造一下,使用 color syntax 语法类型:

@property --houdini-colorA {
  syntax: '<color>';
  inherits: false;
  initial-value: #fff;
}
@property --houdini-colorB {
  syntax: '<color>';
  inherits: false;
  initial-value: #000;
}
.property {
    background: linear-gradient(45deg, var(--houdini-colorA), var(--houdini-colorB));
    transition: 1s --houdini-colorA, 1s --houdini-colorB;
    
    &:hover {
        --houdini-colorA: yellowgreen;
        --houdini-colorB: deeppink;
    }
}

我们使用了 @property 语法,定义了两个 CSS Houdini 自定义变量 --houdini-colorA--houdini-colorB,在 hover 变化的时候,改变这两个颜色。

需要关注的是,我们设定的过渡语句 transition: 1s --houdini-colorA, 1s --houdini-colorB,在这里,我们是针对 CSS Houdini 自定义变量设定过渡,而不是针对 background 设定过渡动画,再看看这次的效果:

Wow,成功了,渐变色的变化从两帧的逐帧动画变成了补间动画,实现了从一个渐变色过渡到另外一个渐变色的效果!而这,都得益于 CSS Houdini 自定义变量的强大能力!

CodePen Demo -- CSS Houdini 自定义变量实现渐变色过渡动画

使用 CSS @property 实现渐变背景色过渡动画

在上述的 DEMO 中,我们利用了 CSS Houdini 自定义变量,将原本定义在 background 的过渡效果嫁接到了 color 之上,而 CSS 是支持一个颜色变换到另外一个颜色的,这样,我们巧妙的实现了渐变背景色的过渡动画。

在之前我们有讨论过在 CSS 中有多少种方式可以实现渐变背景色过渡动画 -- 巧妙地制作背景色渐变动画!,到今天,我们又多了一种实现的方式!


@property --colorA {
  syntax: '<color>';
  inherits: false;
  initial-value: fuchsia;
}
@property --colorC {
  syntax: '<color>';
  inherits: false;
  initial-value: #f79188;
}
@property --colorF {
  syntax: '<color>';
  inherits: false;
  initial-value: red;
}
div {
    background: linear-gradient(45deg,
        var(--colorA),
        var(--colorC),
        var(--colorF));
    animation: change 10s infinite linear;
}

@keyframes change {
    20% {
        --colorA: red;
        --colorC: #a93ee0;
        --colorF: fuchsia;
    }
    40% {
        --colorA: #ff3c41;
        --colorC: #e228a0;
        --colorF: #2e4c96;
    }
    60% {
        --colorA: orange;
        --colorC: green;
        --colorF: teal;
    }
    80% {
        --colorA: #ae63e4;
        --colorC: #0ebeff;
        --colorF: #efc371;
    }
}

完整的代码可以戳这里:

CodePen Demo -- CSS Houdini 自定义变量实现渐变色过渡动画2

conic-gradient 配合 CSS @property 实现饼图动画

OK,上面我们演示了 syntaxcolor 语法类型的情况。在文章一开头,我们还列举了非常多的 syntax 类型。

下面我们尝试下其他的类型,使用 percentage 百分比类型或者 angle 角度类型,实现一个饼图的 hover 动画。

如果我们还是使用传统的写法,利用角向渐变实现不同角度的饼图:

<div></div>
.normal {
    width: 200px;
    height: 200px;
    border-radius: 50%;
    background: conic-gradient(yellowgreen, yellowgreen 25%, transparent 25%, transparent 100%); 
    transition: background 300ms;
    
    &:hover {
        background: conic-gradient(yellowgreen, yellowgreen 60%, transparent 60.1%, transparent 100%); 
    }
}

将会得到这样一种效果,由于 conic-gradient 也是不支持过渡动画的,得到的是一帧向另外一帧的直接变化:

好,使用 CSS Houdini 自定义变量改造一下:

@property --per {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 25%;
}

div {
    background: conic-gradient(yellowgreen, yellowgreen var(--per), transparent var(--per), transparent 100%); 
    transition: --per 300ms linear;
    
    &:hover {
        --per: 60%;
    }
}

看看改造后的效果:

CodePode Demo -- conic-gradient 配合 CSS @property 实现饼图动画

以往使用纯 CSS 非常复杂才能实现的效果,如果可以轻松的达成,不得不感慨 CSS @property 强大的能力!

syntax 的 | 符号

顺便演示一下定义 Houdini 自定义变量时 syntax 的一些稍微复杂点的用法。

conic-gradient 中,我们可以使用百分比也可以使用角度作为关键字,上述的 DEMO 也可以改造成这样:

@property --per {
  syntax: '<percentage> | <angle>';
  inherits: false;
  initial-value: 25%;
}
...

表示,我们的自定义属性即可以是一个百分比值,也可以是一个角度值。

除了 | 符号外,还有 +# 号分别表示接受以空格分隔、和以逗号分隔的属性,感兴趣的可以自行尝试。

使用 length 类型作用于一些长度变化

掌握了上述的技巧,我们就可以利用 Houdini 自定义变量的这个能力,去填补修复以前无法直接过渡动画的一些效果了。

过去,我们想实现这样一个文字下划线的 Hover 效果:

p {
    text-underline-offset: 1px;
    text-decoration-line: underline;
    text-decoration-color: #000;
    transition: all .3s;
    
    &:hover {
        text-decoration-color: orange;
        text-underline-offset: 10px;
        color: orange;
    }
}

因为 text-underline-offset 不支持过渡动画,得到的结果如下:

使用 Houdini 自定义变量改造,化腐朽为神奇:

@property --offset {
  syntax: '<length>';
  inherits: false;
  initial-value: 0;
}
div {
    text-underline-offset: var(--offset, 1px);
    text-decoration: underline;
    transition: --offset 400ms, text-decoration-color 400ms;
    
    &:hover {
        --offset: 10px;
        color: orange;
    text-decoration-color: orange;
    }
}

可以得到丝滑的过渡效果:

CodePen Demo - Underlines hover transition(Chrome solution with Houdini)

实战一下,使用 CSS @property 配合 background 实现屏保动画

嗯,因为 CSS @property 的存在,让以前需要非常多 CSS 代码的工作,一下子变得简单了起来。

我们尝试利用 CSS @property 配合 background,简单的实现一个屏保动画。

我们利用 background 可以简单的得到这样一个图形,代码如下:

html, body {
    width: 100%;
    height: 100%;
}
body {
    background-image:
        radial-gradient(
            circle at 86% 7%,
            rgba(40, 40, 40, 0.04) 0%,
            rgba(40, 40, 40, 0.04) 50%,
            rgba(200, 200, 200, 0.04) 50%,
            rgba(200, 200, 200, 0.04) 100%
        ),
        radial-gradient(
            circle at 15% 16%,
            rgba(99, 99, 99, 0.04) 0%,
            rgba(99, 99, 99, 0.04) 50%,
            rgba(45, 45, 45, 0.04) 50%,
            rgba(45, 45, 45, 0.04) 100%
        ),
        radial-gradient(
            circle at 75% 99%,
            rgba(243, 243, 243, 0.04) 0%,
            rgba(243, 243, 243, 0.04) 50%,
            rgba(37, 37, 37, 0.04) 50%,
            rgba(37, 37, 37, 0.04) 100%
        ),
        linear-gradient(rgb(34, 222, 237), rgb(135, 89, 215));
}

效果如下,还算可以的静态背景图:

image

在往常,我们想让它动起来,其实是需要费一定的功夫的,而现在,通过 CSS @property,对我们希望进行动画的一些元素细节进行改造,可以得到非常不错的动画效果:

body,
html {
    width: 100%;
    height: 100%;
}

@property --perA {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 75%;
}

@property --perB {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 99%;
}

@property --perC {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 15%;
}

@property --perD {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 16%;
}

@property --perE {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 86%;
}

@property --angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

body {
    background-image: 
        radial-gradient(
            circle at var(--perE) 7%,
            rgba(40, 40, 40, 0.04) 0%,
            rgba(40, 40, 40, 0.04) 50%,
            rgba(200, 200, 200, 0.04) 50%,
            rgba(200, 200, 200, 0.04) 100%
        ),
        radial-gradient(
            circle at var(--perC) var(--perD),
            rgba(99, 99, 99, 0.04) 0%,
            rgba(99, 99, 99, 0.04) 50%,
            rgba(45, 45, 45, 0.04) 50%,
            rgba(45, 45, 45, 0.04) 100%
        ),
        radial-gradient(
            circle at var(--perA) var(--perB),
            rgba(243, 243, 243, 0.04) 0%,
            rgba(243, 243, 243, 0.04) 50%,
            rgba(37, 37, 37, 0.04) 50%,
            rgba(37, 37, 37, 0.04) 100%
        ),
        linear-gradient(var(--angle), rgb(34, 222, 237), rgb(135, 89, 215));
    animation: move 30s infinite alternate linear;
}

@keyframes move {
    100% {
        --perA: 85%;
        --perB: 49%;
        --perC: 45%;
        --perD: 39%;
        --perE: 70%;
        --angle: 360deg;
    }
}

效果如下(因为 Gif 上传大小限制,加快了速率,截取了其中一部分,简单做个示意):

property1

整体的效果还是挺不错的,完整的 Demo 你可以戳这里:

CodePen Demo -- CSS @property PureCSS Wrapper

参考文献:

最后

好了,本文到此结束,介绍了 CSS Houdini API 中的 CSS @property 部分,并且利用它实现了一些以往无法简单实现的动画效果,希望对你有帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

查看原文

赞 28 收藏 16 评论 4

JasonSubmara 赞了文章 · 4月12日

【面试技巧】老生常谈之 n 种使用 CSS 实现三角形的技巧

在一些面经中,经常能看到有关 CSS 的题目都会有一道如何使用 CSS 绘制三角形,而经常的回答通常也只有使用 border 进行绘制一种方法。

而 CSS 发展到今天,其实有很多有意思的仅仅使用 CSS 就能绘制出来的三角形的方式,本文将具体罗列讲讲。

通过本文,你能了解到 6 种使用 CSS 绘制三角形的方式,并且它们都非常好掌握。当然本文仅是抛砖引玉,CSS 日新月异,可能还有一些有意思的方法本文遗漏了,欢迎大家在留言区补充~

使用 border 绘制三角形

使用 border 实现三角形应该是大部分人都掌握的,也是各种面经中经常出现的,利用了高宽为零的容器及透明的 border 实现。

简单的代码如下:

div {
  border-top: 50px solid yellowgreen;
  border-bottom: 50px solid deeppink;
  border-left: 50px solid bisque;
  border-right: 50px solid chocolate;
}

高宽为零的容器,设置不同颜色的 border:

image

这样,让任何三边的边框的颜色为 transparent,则非常容易得到各种角度的三角形:

image

CodePen Demo - 使用 border 实现三角形

使用 linear-gradient 绘制三角形

接着,我们使用线性渐变 linear-gradient 实现三角形。

它的原理也非常简单,我们实现一个 45° 的渐变:

div {
  width: 100px;
  height: 100px;
  background: linear-gradient(45deg, deeppink, yellowgreen);
}

image

让它的颜色从渐变色变为两种固定的颜色:

div {
  width: 100px;
  height: 100px;
  background: linear-gradient(45deg, deeppink, deeppink 50%, yellowgreen 50%, yellowgreen 100%);
}

image

再让其中一个颜色透明即可:

div {
  background: linear-gradient(45deg, deeppink, deeppink 50%, transparent 50%, transparent 100%);
}

image

通过旋转 rotate 或者 scale,我们也能得到各种角度,不同大小的三角形,完整的 Demo 可以戳这里:

CodePen Demo - 使用线性渐变实现三角形

使用 conic-gradient 绘制三角形

还是渐变,上述我们使用了线性渐变实现三角形,有意思的是,在渐变家族中,角向渐变 conic-gradient 也可以用于实现三角形。

方法在于,角向渐变的圆心点是可以设置的,类似于径向渐变的圆心点也可以被设置。

我们将角向渐变的圆心点设置于 50% 0,也就是 center top,容器最上方的中间,再进行角向渐变,渐变到一定的角度范围内,都是三角形图形。

假设我们有一个 200px x 100px 高宽的容器,设置其角向渐变圆心点为 50% 0

image

并且,设置它从 90° 开始画角向渐变图,示意图如下:

1

可以看到,在初始的时候,角向渐变图形没有到第二条边的之前,都是三角形,我们选取适合的角度,非常容易的可以得到一个三角形:

div {
    background: conic-gradient(from 90deg at 50% 0, deeppink 0, deeppink 45deg, transparent 45.1deg);
}

image

上述代码中的 deeppink 45deg, transparent 45.1deg 多出来的 0.1deg 是为了简单消除渐变产生的锯齿的影响,这样,我们通过 conic-gradient,也轻松的得到了一个三角形。

同理,再配合旋转 rotate 或者 scale,我们也能得到各种角度,不同大小的三角形,完整的 Demo 可以戳这里:

CodePen Demo - 使用角向渐变实现三角形

transform: rotate 配合 overflow: hidden 绘制三角形

这种方法还是比较常规的,使用 transform: rotate 配合 overflow: hidden。一看就懂,一学就会,简单的动画示意图如下:

设置图形的旋转中心在左下角 left bottom,进行旋转,配合 overflow: hidden

完整的代码:

.triangle {
    width: 141px;
    height: 100px;
    position: relative;
    overflow: hidden;
    
    &::before {
        content: "";
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background: deeppink;
        transform-origin: left bottom;
        transform: rotate(45deg);
    }
}

CodePen Demo - transform: rotate 配合 overflow: hidden 实现三角形

使用 clip-path 绘制三角形

clip-path 一个非常有意思的 CSS 属性。

clip-path CSS 属性可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的 URL 定义的路径或者外部 SVG 的路径。

也就是说,使用 clip-path 可以将一个容器裁剪成任何我们想要的样子。

通过 3 个坐标点,实现一个多边形,多余的空间则会被裁减掉,代码也非常简单:

div {
    background: deeppink;
    clip-path: polygon(0 0, 100% 0, 0 100%, 0 0);
}

image

CodePen Demo - 使用 clip-path 实现三角形

在这个网站中 -- CSS clip-path maker,你可以快捷地创建简单的 clip-path 图形,得到对应的 CSS 代码。

利用字符绘制三角形

OK,最后一种,有些独特,就是使用字符表示三角形。

下面列出一些三角形形状的字符的十进制 Unicode 表示码。

◄ : &#9668; 
► : &#9658; 
▼ : &#9660; 
▲ : &#9650;
⊿ : &#8895;
△ : &#9651;

譬如,我们使用 &#9660; 实现一个三角形 ▼,代码如下:

<div class="normal">&#9660; </div>
div {
    font-size: 100px;
    color: deeppink;
}

效果还是不错的:

image

然而,需要注意的是,使用字符表示三角形与当前设定的字体是强相关的,不同的字体绘制出的同一个字符是不一样的,我在 Google Font 上随机选取了几个不同的字体,分别表示同一个字符,得到的效果如下:

image

可以看到,不同字体的形状、大小及基线都是不一样的,所以如果你想使用字符三角形,确保用户的浏览器安装了你指定的字体,否则,不要使用这种方式。

完整的对比 Demo,你可以戳这里:

CodePen Demo - 使用字符实现三角形

最后

好了,本文到此结束,关于使用 CSS 绘制三角的 6 种不同方式,希望对你有帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

查看原文

赞 5 收藏 3 评论 1

JasonSubmara 回答了问题 · 4月6日

解决echarts如何改变axisPointer为shadow的宽度

可参考官方文档修改柱形图相关的一些属性值进行尝试:barWidth

PS: 另外,不难看出,设计师做的图表极大可能是使用AntV/ChartCube来制作的,项目允许的情况下可以考虑使用AntV/G2\G2plot

关注 4 回答 2

JasonSubmara 关注了用户 · 4月2日

政采云前端团队 @zhengcaiyunqianduantuandui

Z 是政采云拼音首字母,oo 是无穷的符号,结合 Zoo 有生物圈的含义。寄望我们的前端 ZooTeam 团队,不论是人才梯队,还是技术体系,都能各面兼备,成长为一个生态,卓越且持续卓越。

政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推动并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。

关注 2636

JasonSubmara 回答了问题 · 3月30日

vue+js+css怎么实现以下的样式,点击A或者B,出现在右侧的方框中

之前碰巧做过这种结构的东西,用的flex布局,上下左右四个方向均包含,部分内容不方便截图,仅对于实现的思路说明下,希望对你有帮助

image.png

对于你需要的左右结构的二叉树,那么每一级为一层node-wrap,最外层的node-box,使用display:flex; align-items:center;,每一层node-wrap使用flex-direction:column,针对横向的线条,对于每个node-item使用定位,利用伪类显示,而属相的层级的线条,使用node-wrap的伪类,长度需要动态计算 = calc(100% - 上一层第一个高度半值 - 最后一个高度半值)

image.png

部分关键代码贴一部分:

// Right Nodes Box Stylus
.right-nodes-box {
  position: relative;
  display: flex;
  flex-direction: column;
  &::before {
    content: "";
    position: absolute;
    left: 0;
    height: calc(100% - #{$node-height} - 80px - 2px);
    width: 2px;
    background: $border-color;
    bottom: ($node-height + 2px + 80px) / 2;
  }
  .node-wrap {
    padding: 40px 0 40px 70px;
    flex-direction: row;
    &::before {
      content: "";
      position: absolute;
      left: 0;
      bottom: calc(50% - 2px);
      width: 70px;
      height: 2px;
      background: #00aaff;
    }
    &:not(:first-child):not(:last-child) {
      .nodes-child-box {
        position: relative;
        bottom: 0;
      }
    }
    &::after {
      content: '';
      position: absolute;
      left: 40px;
      bottom: 50%;
      margin-bottom: -8px;
      transform: translateX(-50%);
      width: 0;
      border-color:  transparent transparent transparent #00aaff;
      border-style: solid;
      border-width: 8px 6px 8px;
    }
  }
  &.onlyone {
    .node-wrap {
      padding-left: 0;
      &::before, &::after {
        content: none;
      }
    }
  }
}
.node-wrap .right-nodes-box {
  position: absolute;
  left: $node-width + 100px + 42px;
  z-index: -1;
    display: flex;
  margin-left: 2px;
  top: 50%;
  transform: translateY(-50%);
}

主要是看你怎么想你的实现思路,针对实现思路再去考虑相应的代码如何去写~

希望对你有帮助~

关注 3 回答 3

JasonSubmara 赞了文章 · 3月24日

有意思!强大的 SVG 滤镜

想写一篇关于 SVG 滤镜的文章已久,SVG 滤镜的存在,让本来就非常强大的 CSS 如虎添翼。让仅仅使用 CSS/HTML/SVG 创作的效果更上一层楼。题图为袁川老师使用 SVG 滤镜实现的云彩效果 -- CodePen Demo -- Cloud (SVG filter + CSS)

什么是 SVG 滤镜

SVG 滤镜与 CSS 滤镜类似,是 SVG 中用于创建复杂效果的一种机制。很多人看到 SVG 滤镜复杂的语法容易心生退意。本文力图使用最简洁明了的方式让大家尽量弄懂 SVG 滤镜的使用方式。

本文默认读者已经掌握了一定 SVG 的基本概念和用法。

SVG 滤镜的种类

SVG 滤镜包括了:

feBlend
feColorMatrix
feComponentTransfer
feComposite
feConvolveMatrix
feDiffuseLighting
feDisplacementMap
feFlood
feGaussianBlur
feImage
feMerge
feMorphology
feOffset
feSpecularLighting
feTile
feTurbulence
feDistantLight
fePointLight
feSpotLight

看着内容很多,有点类似于 CSS 滤镜中的不同功能:blur()contrast()drop-shadow()

SVG 滤镜的语法

我们需要使用 <defs><filter> 标签来定义一个 SVG 滤镜。

通常所有的 SVG 滤镜元素都需要定义在 <defs> 标记内。

现在,基本上现代浏览器,即使不使用 <defs> 包裹 <filter>,也能够定义一个 SVG 滤镜。

这个 <defs> 标记是 definitions 这个单词的缩写,可以包含很多种其它标签,包括各种滤镜。

其次,使用 <filter> 标记用来定义 SVG 滤镜。 <filter> 标签需要一个 id 属性,它是这个滤镜的标志。SVG 图形使用这个 id 来引用滤镜。

看一个简单的 DEMO:

<div class="cssFilter"></div>
<div class="svgFilter"></div>

<svg>
    <defs>
        <filter id="blur">
            <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
        </filter>
    </defs>
</svg>
div {
    width: 100px;
    height: 100px;
    background: #000;
}
.cssblur {
    filter: blur(5px);
}
.svgFilter{
    filter: url(#blur);
}

这里,我们在 defsfilter 标签内,运用了 SVG 的 feGaussianBlur 滤镜,也就是模糊滤镜, 该滤镜有两个属性 instdDeviation。其中 in="SourceGraphic" 属性指明了模糊效果要应用于整个图片,stdDeviation 属性定义了模糊的程度。最后,在 CSS 中,使用了 filter: url(#blur) 去调用 HTML 中定义的 id 为 blur 的滤镜。

为了方便理解,也使用 CSS 滤镜 filter: blur(5px) 实现了一个类似的滤镜,方便比较,结果图如下:

image

CodePen Demo - SVG 滤镜

嘿,可以看到,使用 SVG 的模糊滤镜,实现了一个和 CSS 模糊滤镜一样的效果。

CSS filter 的 url 模式

上文的例子中使用了 filter: url(#blur) 这种模式引入了一个 SVG 滤镜效果,url 是 CSS 滤镜属性的关键字之一,url 模式是 CSS 滤镜提供的能力之一,允许我们引入特定的 SVG 过滤器,这极大的增强 CSS 中滤镜的能力。

相当于所有通过 SVG 实现的滤镜效果,都可以快速的通过 CSS 滤镜 URL 模式一键引入。

多个滤镜搭配工作

和 CSS 滤镜一样,SVG 滤镜也是支持多个滤镜搭配混合使用的。

所以我们经常能看到一个 <filter> 标签内有大量的代码。很容易就懵了~

再来看个简单的例子:

<div></div>

<svg>
    <defs>
        <!-- Filter declaration -->
        <filter id="MyFilter">

            <!-- offsetBlur -->
            <feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
            <feOffset in="blur" dx="10" dy="10" result="offsetBlur" />

            <!-- merge SourceGraphic + offsetBlur -->
            <feMerge>
                <feMergeNode in="offsetBlur" />
                <feMergeNode in="SourceGraphic" />
            </feMerge>
        </filter>
    </defs>
</svg>
div {
    width: 200px;
    height: 200px;
    background: url(xxx);
    filter: url(#MyFilter);
}

我们先来看看整个滤镜的最终结果,结果长这样:

image

CSS 可能一行代码就能实现的事情,SVG 居然用了这么多代码。(当然,这里 CSS 也不好实现,不是简单容器的阴影,而是 PNG 图片图形的轮廓阴影)

分解步骤

首先看这一段:

<!-- offsetBlur -->
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
<feOffset in="blur" dx="10" dy="10" result="offsetBlur" />

首先 <feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" /> 这一段,我们上面也讲到了,会生成一个模糊效果,这里多了一个新的属性 result='blur',这个就是 SVG 的一个特性,不同滤镜作用的效果可以通过 result 产出一个中间结果(也称为 primitives 图元),其他滤镜可以使用 in 属性导入不同滤镜产出的 result,继续操作。

紧接着,<feOffset> 滤镜还是很好理解的,使用 in 拿到了上一步的结果 result = 'blur',然后做了一个简单的位移。

这里就有一个非常重要的知识点:在不同滤镜中利用 resultin 属性,可以实现在前一个基本变换操作上建立另一个操作,比如我们的例子中就是添加模糊后又添加位移效果。

结合两个滤镜,产生的图形效果,其实是这样的:

image

实际效果中还出现了原图,所以这里我们还使用了 <feMerge> 标签,合并了多个效果。也就是上述这段代码:

<!-- merge SourceGraphic + offsetBlur -->
<feMerge>
    <feMergeNode in="offsetBlur" />
    <feMergeNode in="SourceGraphic" />
</feMerge>

feMerge 滤镜允许同时应用滤镜效果而不是按顺序应用滤镜效果。利用 result 存储别的滤镜的输出可以实现这一点,然后在一个 <feMergeNode> 子元素中访问它。

  • <feMergeNode in="offsetBlur" /> 表示了上述两个滤镜的最终输出结果 offsetBlur ,也就是阴影的部分
  • <feMergeNode in="SourceGraphic" /> 中的 in="SourceGraphic" 关键词表示图形元素自身将作为 <filter> 原语的原始输入

整体再遵循后输入的层级越高的原则,最终得到上述结果。示意流程图如下:

image

至此,基本就掌握了 SVG 滤镜的工作原理,及多个滤镜如何搭配使用。接下来,只需要搞懂不同的滤镜能产生什么样的效果,有什么不同的属性,就能大致对 SVG 滤镜有个基本的掌握!

关于 SVG 滤镜还需要知道的

上面大致过了一下 SVG 滤镜的使用流程,过程中提到了一些属性,可能也漏掉了一些属性的讲解,本章节将补充说明一下。

滤镜标签通用属性

有一些属性是每一个滤镜标签都有,都可以进行设置的。

属性作用
x, y提供左上角的坐标来定义在哪里渲染滤镜效果。 (默认值:0)
width, height绘制滤镜容器框的高宽(默认都为 100%)
result用于定义一个滤镜效果的输出名字,以便将其用作另一个滤镜效果的输入(in)
in指定滤镜效果的输入源,可以是某个滤镜导出的 result,也可以是下面 6 个值

in 属性的 6 个取值

SVG filter 中的 in 属性,指定滤镜效果的输入源,可以是某个滤镜导出的 result,也可以是下面 6 个值:

in 取值作用
SourceGraphic该关键词表示图形元素自身将作为 <filter> 原语的原始输入
SourceAlpha该关键词表示图形元素自身将作为 <filter> 原语的原始输入。SourceAlphaSourceGraphic 具有相同的规则除了 SourceAlpha 只使用元素的非透明部分
BackgroundImage与 SourceGraphic 类似,但可在背景上使用。 需要显式设置
BackgroundAlpha与 SourceAlpha 类似,但可在背景上使用。 需要显式设置
FillPaint将其放置在无限平面上一样使用填充油漆
StrokePaint将其放在无限平面上一样使用描边绘画
后 4 个基本用不上~

更多 SVG 滤镜介绍讲解

上面已经提到了几个滤镜,我们简单回顾下:

  • <feGaussianBlur > - 模糊滤镜
  • <feOffset > - 位移滤镜
  • <feMerge> - 多滤镜叠加滤镜

接下来再介绍一些比较常见,有意思的 SVG 滤镜。

feBlend 滤镜

<feBlend> 为混合模式滤镜,与 CSS 中的混合模式相类似。

在 CSS 中,我们有混合模式 mix-blend-modebackground-blend-mode 。我有过非常多篇关于 CSS 混合模式相关的一些应用。如果你还不太了解 CSS 中的混合模式,可以先看看这几篇文章:

SVG 中的混合模式种类比 CSS 中的要少一些,只有 5 个,其作用与 CSS 混合模式完全一致:

  • normal — 正常
  • multiply — 正片叠底
  • screen — 滤色
  • darken — 变暗
  • lighten— 变亮

简单一个 Demo,我们有两张图,利用不同的混合模式,可以得到不一样的混合结果 :

<div></div>

<svg>
    <defs>
        <filter id="lighten" x="0" y="0" width="200" height="250">
            <feImage width="200" height="250" xlink:href="image1.jpg" result="img1" />
            <feImage width="200" height="250" xlink:href="image2.jpg" result="img2" />
            <feBlend mode="lighten" in="img1" in2="img2"/>
        </filter>
    </defs>
</svg>
.container {
    width: 200px;
    height: 250px;
    filter: url(#lighten);
}

这里还用到了一个 <feImage> 滤镜,它的作用是提供像素数据作为输出,如果外部来源是一个 SVG 图像,这个图像将被栅格化。

image

上述运用了 feBlend 滤镜中的 mode="lighten" 后的结果,两个图像叠加作用了 lighten 混合模式:

image

看看全部 5 中混合模式的效果:

image

CodePen Demo -- SVG Filter feBlend Demo

feColorMatrix

<feColorMatrix> 滤镜也是 SVG 滤镜中非常有意思的一个滤镜,顾名思义,它的名字中包含了矩阵这个单词,表示该滤镜基于转换矩阵对颜色进行变换。每一像素的颜色值(一个表示为[红,绿,蓝,透明度] 的矢量) 都经过矩阵乘法 (matrix multiplated) 计算出的新颜色。

这个滤镜稍微有点复杂,我们一步一步来看。

<feColorMatrix> 滤镜有 2 个私有属性 typevalues,type 它支持 4 种不同的类型:saturate | hueRotate | luminanceToAlpha | matrix,其中部分与 CSS Filter 中的一些滤镜效果类似。

type 类型作用values 的取值范围
saturate转换图像饱和度0.0 - 1.0
hueRotate转换图像色相0.0 - 360
luminanceToAlpha阿尔法通道亮度(不知道如何翻译 :sad)只有一个效果,无需改变 values 的值
matrix使用矩阵函数进行色彩变换需要应用一个 4 x 5 的矩阵

在这里,我做了一个简单的关于 <feColorMatrix> 前 3 个属性 saturate | hueRotate | luminanceToAlpha 的效果示意 DEMO -- CodePen - feColorMatrix Demo,可以感受下它们的具体的效果:

1gif

saturate、hueRotate 滤镜和 CSS 中的 filter 中的 saturate、hue-rotate 的作用是一模一样的。

feColorMatrix 中的 type=matrix

feColorMatrix 中的 type=matrix 理解起来要稍微更复杂点,它的 values 需要传入一个 4x5 的矩阵。

像是这样:

<filter id="colorMatrix">
  <feColorMatrix type="matrix" values="1 0 0 0 0, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 1 0"/>
</filter>

要理解如何运用这些填写矩阵,就不得不直面另外一个问题 -- 图像的表示。

数字图像的本质是一个多维矩阵。在图像显示时,我们把图像的 R 分量放进红色通道里,B 分量放进蓝色通道里,G 分量放进绿色通道里。经过一系列处理,显示在屏幕上的就是我们所看到的彩色图像了。

而 feColorMatrix 中的 matrix 矩阵,就是用来表示不同通道的值每一个分量的值,最终通过计算得到我们熟知的 rgba() 值。

计算逻辑为:

/* R G B A 1 */ 
1 0 0 0 0 // R = 1*R + 0*G + 0*B + 0*A + 0 
0 1 0 0 0 // G = 0*R + 1*G + 0*B + 0*A + 0 
0 0 1 0 0 // B = 0*R + 0*G + 1*B + 0*A + 0 
0 0 0 1 0 // A = 0*R + 0*G + 0*B + 1*A + 0

中文的文章,对 feColorMatrix 的 matrix 讲解最好的应该就是大漠老师的这篇 -- 详解feColorMatrix,对具体的表示法感兴趣的可以看看。

仅仅是使用的话,这里还有一个可视化的 DEMO -- CodePen - feColorMatrix Demo,帮助大家理解记忆:

2


到目前为止,大部分 SVG 滤镜的展示讲解都是 CSS 现有能力能够实现的,那 SVG 滤镜的独特与魅力到底在哪呢?有什么是 CSS 能力无法做到的么?下面来看看另外几个有意思的 SVG 滤镜。

feSpecularLighting/feDiffuseLighting 光照滤镜

feSpecularLighting 与 feDiffuseLighting 都意为光照滤镜,使用它们可以照亮一个源图形,不同的是,feSpecularLighting 为镜面照明,而 feDiffuseLighting 为散射光照明。

  • feDiffuseLighting:来自外部光源,适合模拟太阳光或者灯光照明
  • feSpecularLighting:指定从反射面反射的二次光

简单看其中一个 Demo,代码看着有点多,但是一步一步也很好理解:

<div></div>
<div class="svg-filter"></div>
<svg>
    <defs>
        <filter id="filter">
            <!--Lighting effect-->
            <feSpecularLighting in="SourceGraphic" specularExponent="20" specularConstant="0.75" result="spec">
              <fePointLight x="0" y="0" z="200" />
            </feSpecularLighting>
            <!--Composition of inputs-->
            <feComposite in="SourceGraphic" in2="spec" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
        </filter>
    </defs>
</svg>
div {
    background: url(avator.png);
}
.svg-filter {
    filter: url(#filter);
}

左边是原图,右边是应用了光照滤镜之后的效果。

image

CodePen - feSpotLight SVG Light Source

feMorphology 滤镜

feMorphology 为形态滤镜,它的输入源通常是图形的 alpha 通道,用来它的两个操作可以使源图形腐蚀(变薄)或扩张(加粗)。

使用属性 operator 确定是要腐蚀效果还是扩张效果。使用属性 radius 表示效果的程度,可以理解为笔触的大小。

  • operator:erode 腐蚀模式,dilate 为扩张模式,默认为 erode
  • radius:笔触的大小,接受一个数字,表示该模式下的效果程度,默认为 0

我们将这个滤镜简单的应用到文字上看看效果:

<div class="g-text">
    <p>Normal Text</p>
    <p class="dilate">Normal Text</p>
    <p class="erode">Normal Text</p>
</div>

<svg width="0" height="0">
    <filter id="dilate">
        <feMorphology in="SourceAlpha" result="DILATED" operator="dilate" radius="3"></feMorphology>
    </filter>
    <filter id="erode">
        <feMorphology in="SourceAlpha" result="ERODE" operator="erode" radius="1"></feMorphology>
    </filter>
</svg>
p {
    font-size: 64px;
}
.dilate {
    filter: url(#dilate);
}
.erode {
    filter: url(#erode);
}

效果如下:最左边的是正常文字,中间的是扩张的模式,右边的是腐蚀模式,看看效果,非常好理解:

image

当然,我们还可以将其运用在图片之上,这时,并非是简单的让图像的笔触变粗或者变细,

  • 对于 erode 模式,会将图片的每一个像素向更暗更透明的方向变化,
  • dilate 模式,则是将每个向像素周围附近更亮更不透明的方向变化

简单看个示例动画 DEMO,我们有两张图,分别作用 operator="erode"operator="dilate",并且动态的去改变它们的 radius,其中一个的代码示意如下:

<svg width="450" height="300" viewBox="0 0 450 300">
    <filter id="morphology">
        <feMorphology operator="erode" radius="0">
            <animate attributeName="radius" from="0" to="5" dur="5s" repeatCount="indefinite" />
        </feMorphology>
    </filter>

    <image xlink:href="image.jpg" width="90%" height="90%" x="10" y="10" filter="url(#morphology)"></image>
</svg>

3

上图左边是扩张模式,右边是腐蚀模式:

CodePen Demo -- SVG feMorphology Animation

feTurbulence 滤镜

turbulence 意为湍流,不稳定气流,而 SVG <feTurbulence> 滤镜能够实现半透明的烟熏或波状图像。 通常用于实现一些特殊的纹理。滤镜利用 Perlin 噪声函数创建了一个图像。噪声在模拟云雾效果时非常有用,能产生非常复杂的质感,利用它可以实现了人造纹理比如说云纹、大理石纹的合成。

有了 feTurbulence,我们可以自使用 SVG 创建纹理图形作为置换图,而不需要借助外部图形的纹理效果,即可创建复杂的图形效果。

这个滤镜,我个人认为是 SVG 滤镜中最有意思的一个,因为它允许我们自己去创造出一些纹理,并且叠加在其他效果之上,生成出非常有意思的动效。

feTurbulence 有三个属性是我们特别需要注意的:typebaseFrequencynumOctaves

  • type:实现的滤镜的类型,可选fractalNoise 分形噪声,或者是 turbulence 湍流噪声。

    • fractalNoise:分形噪声更加的平滑,它产生的噪声质感更接近云雾
    • turbulence:湍流噪声
  • baseFrequency: 表示噪声函数的基本频率的参数,频率越小,产生的图形越大,频率越大,产生的噪声越复杂其图形也越小越精细,通常的取值范围在 0.02 ~ 0.2
  • numOctaves:表示噪声函数的精细度,数值越高,产生的噪声更详细。 默认值为1

这里有一个非常好的网站,用于示意 feTurbulence 所产生的两种噪声的效果:http://apike.ca/ - feTurbulence

两种噪声的代码基本一致,只是 type 类型不同:

<filter id="fractal" >
  <feTurbulence id="fe-turb-fractal" type="fractalNoise" baseFrequency="0.00025" numOctaves="1"/>
</filter>
<filter id="turbu">
  <feTurbulence id="fe-turb-turbulence" type="fractalNoise" baseFrequency="0.00025" numOctaves="1"/>
</filter>

我们通过改变 baseFrequencynumOctaves 参数看看实际产生的两种噪声的效果:

同时,baseFrequency 允许我们传入两个值,我们可以只改变某一方向上的频率,具体的你可以戳这个 Demo 看看:CodePen -- feTurbulence baseFrequency & numOctaves

单单一个 <feTurbulence> 滤镜其实是比较难搞懂这滤镜想干什么的,需要将这个滤镜作为纹理或者输入,和其他滤镜一起搭配使用,实现一些效果,下面我们来看看:

使用 feTurbulence 滤镜实现文字流动的效果

首先,尝试将 feTurbulence 所产生的纹理和文字相结合。

简单的代码如下:

<div>Coco</div>
<div class="turbulence">Coco</div>

<svg>
    <filter id="fractal" filterUnits="objectBoundingBox" x="0%" y="0%" width="100%" height="100%">
        <feTurbulence id="turbulence" type="fractalNoise" baseFrequency="0.03" numOctaves="1" />
        <feDisplacementMap in="SourceGraphic" scale="50"></feDisplacementMap>
    </filter>
</svg>
.turbulence {
    filter: url(#fractal);
}

左边是正常的效果,后边是应用了 <feTurbulence> 的效果,你可以试着点进 Demo,更改 baseFrequencynumOctaves 参数的大小,可以看到不同的效果:

image

CodePen Demo -- feTurbulence text demo

feDisplacementMap 映射置换滤镜

上面的 Demo 还用到了 feDisplacementMap 滤镜,也需要简单的讲解下。

feDisplacementMap 为映射置换滤镜,想要用好这个滤镜不太容易,需要掌握非常多的关于 PhotoShop 纹理创建或者是图形色彩相关的知识。该滤镜用来自图像中从 in2 的输入值到空间的像素值置换图像从 in 输入值到空间的像素值。

说人话就是 feDisplacementMap 实际上是用于改变元素和图形的像素位置的。该滤镜通过遍历原图形的所有像素点,使用 feDisplacementMap 重新映射到一个新的位置,形成一个新的图形。

在上述的 feTurbulence 滤镜与文字的结合使用中,我们通过 feTurbulence 噪声得到了噪声图形,然后通过 feDisplacementMap 滤镜根据 feTurbulence 所产生的噪声图形进行形变,扭曲,液化,得到最终的效果。

MDN 上有这个滤镜转化的一个公式(感兴趣的可以研究下,我啃不动了):

P'(x,y) ← P( x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5))

使用 feTurbulence 滤镜实现褶皱纸张的纹理

好,我们继续 feTurbulence ,使用这个滤镜,我们可以生成各种不同的纹理,我们可以尝试使用 feTurbulence 滤镜搭配光照滤镜实现褶皱的纸张纹理效果,代码也非常少:

<div></div>
<svg>
    <filter id='roughpaper'>
        <feTurbulence type="fractalNoise" baseFrequency='0.04' result='noise' numOctaves="5" />

        <feDiffuseLighting in='noise' lighting-color='#fff' surfaceScale='2'>
            <feDistantLight azimuth='45' elevation='60' />
        </feDiffuseLighting>
    </filter>
</svg>
div {
    width: 650px;
    height: 500px;
    filter: url(#roughpaper);
}

效果如下:

image

CodePen Demo -- Rough Paper Texture with SVG Filters

你可以在 Sara Soueidan 的一次关于 SVG Filter 的分享上,找到制作它的教程:Youtube -- SVG Filters Crash Course

使用 feTurbulence 滤镜实现按钮hover效果

使用 feTurbulence 滤镜搭配 feDisplacementMap 滤镜,还可以制作一些非常有意思的按钮效果。

尝试实现一些故障风格的按钮,其中一个按钮的代码如下:

<div class="fe1">Button</div>
<div class="fe2">Button</div>

<svg>
    <defs>
        <filter id="fe1">
            <feTurbulence id="animation" type="fractalNoise" baseFrequency="0.00001 9.9999999" numOctaves="1" result="warp">
                <animate attributeName="baseFrequency" from="0.00001 9.9999" to="0.00001 0.001" dur="2s" repeatCount="indefinite"/>
            </feTurbulence>
            <feOffset dx="-90" dy="-90" result="warpOffset"></feOffset>
            <feDisplacementMap xChannelSelector="R" yChannelSelector="G" scale="30" in="SourceGraphic" in2="warpOffset"></feDisplacementMap>
        </filter>
    </defs>
</svg>
.fe1 {
    width: 200px;
    height: 64px;
    outline: 200px solid transparent;
}

.fe1:hover {
    filter: url(#fe1);
}

通过 hover 按钮的时候,给按钮添加滤镜效果,并且滤镜本身带有一个无限循环的动画:

完整的代码你可以戳这里:CodePen Demo - SVG Filter Button Effects

使用 feTurbulence 滤镜实现云彩效果

最后,我们回到题图上的云彩效果,使用 feTurbulence 滤镜,我们可以非常逼真的使用 SVG 模拟出真实的云彩效果。

首先,通过随机生成的多重 box-shadow,实现这一一个图形:

<div></div>
div {
    width: 1px;
    height: 1px;
    box-shadow: rgb(240 255 243) 80vw 11vh 34vmin 16vmin, rgb(17 203 215) 33vw 71vh 23vmin 1vmin, rgb(250 70 89) 4vw 85vh 21vmin 9vmin, rgb(198 241 231) 8vw 4vh 22vmin 12vmin, rgb(198 241 231) 89vw 11vh 31vmin 19vmin, rgb(240 255 243) 5vw 22vh 38vmin 19vmin, rgb(250 70 89) 97vw 35vh 33vmin 16vmin, rgb(250 70 89) 51vw 8vh 35vmin 14vmin, rgb(17 203 215) 75vw 57vh 40vmin 4vmin, rgb(250 70 89) 28vw 18vh 31vmin 11vmin, rgb(250 70 89) 8vw 89vh 31vmin 2vmin, rgb(17 203 215) 13vw 8vh 26vmin 19vmin, rgb(240 255 243) 98vw 12vh 35vmin 5vmin, rgb(17 203 215) 35vw 29vh 27vmin 18vmin, rgb(17 203 215) 67vw 58vh 22vmin 15vmin, rgb(198 241 231) 67vw 24vh 25vmin 7vmin, rgb(17 203 215) 76vw 52vh 22vmin 7vmin, rgb(250 70 89) 46vw 86vh 26vmin 20vmin, rgb(240 255 243) 50vw 20vh 25vmin 1vmin, rgb(250 70 89) 74vw 14vh 25vmin 16vmin, rgb(240 255 243) 31vw 100vh 29vmin 20vmin
}

这个工作,你可以交给 SASS、LESS 或者 JavaScript 这些能够有循环函数能力的语言去生成,它的效果大概是这样:

image

紧接着,通过 feTurbulence 产生分形噪声图形,使用 feDisplacementMap 进行映射置换,最后给图形叠加上这个滤镜效果。

<svg width="0">
  <filter id="filter">
    <feTurbulence type="fractalNoise" baseFrequency=".01" numOctaves="10" />
    <feDisplacementMap in="SourceGraphic" scale="240" />
  </filter>
</svg>
div {
    filter: url(#filter);
}

即可得到这样的云彩效果:

image

完整的代码,你可以戳这里到袁川老师的 CodePen 观看:Cloud (SVG filter + CSS)

总结一下

关于 SVG 滤镜入门的第一篇总算差不多了,本文简单的介绍了一下 SVG 滤镜的使用方式以及一些常见的 SVG 滤镜并给出了最简单的一些使用效果,希望大家看完能对 SVG 滤镜有一个简单的认识。

本文罗列的滤镜效果更多的是单个效果或者很少几个组合在一起的效果,实际的使用或者应用到应用场景下其实会是更多滤镜的的组合产生出的一个效果。

后面的文章将会更加细致的去探讨分析多个 SVG 滤镜组合效果,探讨更复杂的排列组合。

文章的题目叫SVG 滤镜从入门到放弃因为 SVG 滤镜学起来确实太繁琐太累了,它不像 CSS 滤镜或者混合模式那么容易上手那么简单。当然也由于 SVG 滤镜的功能非常强大,定制化能力强以及它已经存在了非常之久有关。SVG 滤镜的兼容性也很好,它们其实是早于 CSS3 一些特殊效果之前就已经存在的。

CSS 其实一直在向 SVG 的一些特殊能力靠拢,用更简单的语法让人更易上手,不过 SVG 滤镜还是有其独特的魅力所在。后续将会有更多关于 SVG 滤镜的文章。也希望读到这里的同学不要放弃!

参考资料

最后

好了,本文到此结束,希望对你有帮助 :)

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

查看原文

赞 14 收藏 10 评论 0

JasonSubmara 赞了文章 · 3月12日

小技巧!CSS 整块文本溢出省略特性探究

今天的文章很有意思,讲一讲整块文本溢出省略打点的一些有意思的细节。

文本超长打点

我们都知道,到今天(2021/03/06),CSS 提供了两种方式便于我们进行文本超长的打点省略。

对于单行文本,使用单行省略:

{
    width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

image

而对于多行文本的超长省略,使用 -webkit-line-clamp 相关属性,兼容性也已经非常好了:

{
    width: 200px;
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
}

image

CodePen Demo -- inline-block 实现整块的溢出打点

问题一:超长文本整块省略

基于上述的超长打点省略方案之下,会有一些变化的需求。譬如,我们有如下结构:

<section>
    <a href="/" class="avatar"></a>
    <div class="info">
        <p class="person-card__name">Sb Coco</p>
        <p class="person-card__desc">
            <span>FE</span>
            <span>UI</span>
            <span>UX Designer</span>
            <span>前端工程师</span>
        </p>
    </div>
</section>

image

对于上述超出的情况,我们希望对于超出文本长度的整一块 -- 前端工程师,整体被省略。

如果我们直接使用上述的方案,使用如下的 CSS,结果会是这样,并非我们期待的整块省略:

.person-card__desc {
    width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

image

display: inline 改为 display: inline-block 实现整块省略

这里,如果我们需要实现一整块的省略,只需要将包裹整块标签元素的 spandisplayinline 改为 inline-block 即可。

.person-card__desc span {
    display: inline-block;
}

image

这样,就可以实现,基于整块的内容的溢出省略了。完整的 Demo,你可以戳这里:

CodePen Demo - 整块超长溢出打点省略

问题二:iOS 不支持整块超长溢出打点省略

然而,上述方案并非完美的。经过实测,上述方案在 iOSSafari 下,没能生效,表现为这样:

image

查看规范 - CSS Basic User Interface Module Level 3 - text-overflow,究其原因,在于 text-overflow 只能对内联元素进行打点省略。(Chrome 对此可能做了一些优化,所以上述非 iOS 和 Safari 的场景是正常的)

所以猜测是因为经过了 display: inline-block 的转化后,已经不再是严格意义上的内联元素了。

解决方案,使用多行省略替代单行省略

当然,这里经过试验后,发现还是有解的,我们在开头还提到了一种多行省略的方案,我们将多行省略的代码替换单行省略,只是行数 -webkit-line-clamp: 2 改成一行即可 -webkit-line-clamp: 1

.person-card__desc {
    width: 200px;
    white-space: normal;
    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
}
.person-card__desc span {
    display: inline-block;
}

这样,在 iOS/Safari 下也能完美实现整块的超长打点省略:

image

CodePen Demo -- iOS 下的整块超长溢出打点省略方案

值得注意的是,在使用 -webkit-line-clamp 的方案的时候,一定要配合 white-space: normal 允许换行,而不是不换行。这一点,非常重要。

这样,我们就实现了全兼容的整块的超长打点省略了。

当然, -webkit-line-clamp 本身也是存在一定的兼容性问题的,实际使用的时候还需要具体去取舍。

最后

好了,本文到此结束,一个简单的 CSS 小技巧,希望对你有帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

gzh_sssmall.png

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

查看原文

赞 19 收藏 14 评论 0

JasonSubmara 赞了文章 · 3月8日

奇思妙想 CSS 文字动画

之前有些过两篇关于字体的文章,是关于如何定义字体的:

本文将会和这篇 -- CSS 奇思妙想边框动画类似,讲一些文字效果,利用不同的属性搭配,实现各式各样的文字动效。

Google Font

在写各种 DEMO 的时候,有的时候一些特殊的字体能更好的体现动画的效果。这里讲一个快速引入不同格式字体的小技巧。

就是 Google Font 这个网站,上面有非常多的不同的开源字体:

image

当我们相中了一个我们喜欢的字体,它也提供了非常快速的便捷的引入方式。选中对应的字体,选择 +Select this style,便可以通过 link@import 两种方式引入:

image

使用 link 标签引入:

<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@200&display=swap" rel="stylesheet">

OR,在 CSS 代码中,使用 @import 引入:

<style>
@import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@200&display=swap');
</style>

上述两种方式内部其实都是使用的 @font-face 进行了字体的定义。

我们可以通过 @font-face 快速声明指定一个自定义字体。类似这样:

@font-face {
  font-family: "Open Sans";
  src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"),
       url("/fonts/OpenSans-Regular-webfont.woff") format("woff");
}

这样,利用 Google Font,我们就可以便捷的享受各种字体了。

我在我的个人项目或者一些 DEMO 中,需要一些艺术字体或者特殊字体展示不一样的效果时,经常会使用这种方式。而至于在业务中,是否需要引入一些特殊字体,就见仁见智了。


接下来,就会分门别类的看看,字体在 CSS 中,和不同是属性相结合,能够鼓捣出什么样的效果。

文字与阴影

通过将字体与字体阴影 text-shadow,相结合,阴影的不同运用方式,产生不一样的效果。

我们通过一系列 Demo 来看看。

长阴影文字效果

通过多层次,颜色逐渐变化(透明)的阴影变化,可以生成长阴影:

div {
  text-shadow: 0px 0px #992400, 1px 1px rgba(152, 36, 1, 0.98), 2px 2px rgba(151, 37, 2, 0.96), 3px 3px rgba(151, 37, 2, 0.94), 4px 4px rgba(150, 37, 3, 0.92), 5px 5px rgba(149, 38, 4, 0.9), 6px 6px rgba(148, 38, 5, 0.88), 7px 7px rgba(148, 39, 5, 0.86), 8px 8px rgba(147, 39, 6, 0.84), 9px 9px rgba(146, 39, 7, 0.82), 10px 10px rgba(145, 40, 8, 0.8), 11px 11px rgba(145, 40, 8, 0.78), 12px 12px rgba(144, 41, 9, 0.76), 13px 13px rgba(143, 41, 10, 0.74), 14px 14px rgba(142, 41, 11, 0.72), 15px 15px rgba(142, 42, 11, 0.7), 16px 16px rgba(141, 42, 12, 0.68), 17px 17px rgba(140, 43, 13, 0.66), 18px 18px rgba(139, 43, 14, 0.64), 19px 19px rgba(138, 43, 15, 0.62), 20px 20px rgba(138, 44, 15, 0.6), 21px 21px rgba(137, 44, 16, 0.58), 22px 22px rgba(136, 45, 17, 0.56), 23px 23px rgba(135, 45, 18, 0.54), 24px 24px rgba(135, 45, 18, 0.52), 25px 25px rgba(134, 46, 19, 0.5), 26px 26px rgba(133, 46, 20, 0.48), 27px 27px rgba(132, 47, 21, 0.46), 28px 28px rgba(132, 47, 21, 0.44), 29px 29px rgba(131, 48, 22, 0.42), 30px 30px rgba(130, 48, 23, 0.4), 31px 31px rgba(129, 48, 24, 0.38), 32px 32px rgba(129, 49, 24, 0.36), 33px 33px rgba(128, 49, 25, 0.34), 34px 34px rgba(127, 50, 26, 0.32), 35px 35px rgba(126, 50, 27, 0.3), 36px 36px rgba(125, 50, 28, 0.28), 37px 37px rgba(125, 51, 28, 0.26), 38px 38px rgba(124, 51, 29, 0.24), 39px 39px rgba(123, 52, 30, 0.22), 40px 40px rgba(122, 52, 31, 0.2), 41px 41px rgba(122, 52, 31, 0.18), 42px 42px rgba(121, 53, 32, 0.16), 43px 43px rgba(120, 53, 33, 0.14), 44px 44px rgba(119, 54, 34, 0.12), 45px 45px rgba(119, 54, 34, 0.1), 46px 46px rgba(118, 54, 35, 0.08), 47px 47px rgba(117, 55, 36, 0.06), 48px 48px rgba(116, 55, 37, 0.04), 49px 49px rgba(116, 56, 37, 0.02), 50px 50px rgba(115, 56, 38, 0);
}

image

当然,多重阴影以及每重的颜色我们很难一个一个手动去写,在写长阴影的时候通常需要借助 SASSLESS 去帮助节省时间:

@function makelongrightshadow($color) {
    $val: 0px 0px $color;

    @for $i from 1 through 50 {
        $color: fade-out(desaturate($color, 1%), .02);
        $val: #{$val}, #{$i}px #{$i}px #{$color};
    }

    @return $val;
}
div {
    text-shadow: makeLongShadow(hsl(14, 100%, 30%));
}

立体阴影文字效果

如果多层阴影,但是颜色变化没那么强烈,能够塑造一种立体的效果。

div {
  text-shadow: 0 -1px 0 #ffffff, 0 1px 0 #2e2e2e, 0 2px 0 #2c2c2c, 0 3px 0 #2a2a2a, 0 4px 0 #282828, 0 5px 0 #262626, 0 6px 0 #242424, 0 7px 0 #222222, 0 8px 0 #202020, 0 9px 0 #1e1e1e, 0 10px 0 #1c1c1c, 0 11px 0 #1a1a1a, 0 12px 0 #181818, 0 13px 0 #161616, 0 14px 0 #141414, 0 15px 0 #121212);
}

image

内嵌阴影文字效果

合理的阴影颜色和背景底色搭配,搭配,可以实现类似内嵌效果的阴影。

div {
  color: #202020;
  background-color: #2d2d2d;
  letter-spacing: .1em;
  text-shadow: -1px -1px 1px #111111, 2px 2px 1px #363636;
}

image

CodePen Demo -- 5 text shadow effects in css3

氖光效果(Neon)

氖光效果,英文名叫 Neon,是我在 Codepen 上看到的最多的效果之一。它的原理非常简单,却可以产生非常酷炫的效果。

我们只需要设置 3~n 层阴影效果,每一层的模糊半径(文字阴影的第三个参数)间隔较大,并且每一层的阴影颜色相同即可。

p {
    color: #fff;
    text-shadow: 
        0 0 10px #0ebeff,
        0 0 20px #0ebeff,
        0 0 50px #0ebeff,
        0 0 100px #0ebeff,
        0 0 200px #0ebeff
}

当然,通常运用 Neon 效果时,背景底色都是偏黑色。

image

合理运用 Neon 效果,就可以制作非常多有意思的动效。譬如作用于鼠标 hover 上去的效果:

p {
    transition: .2s;
 
    &:hover {
        text-shadow: 0 0 10px #0ebeff, 0 0 20px #0ebeff, 0 0 50px #0ebeff, 0 0 100px #0ebeff, 0 0 200px #0ebeff;
    }
}

CodePen Demo -- Neon Demo

CodePen 上有一个 2K+ 赞的 DEMO,实现了非常赞的 Neon 效果,可以戳进去看看 CodePen -- Neon Glow

也可以选取适当合适的字体,配合动画效果,实现一种渐进的亮灯效果:

<p>
  <span id="n">n</span>
  <span id="e">e</span>
  <span id="o">o</span>
  <span id="n2">n</span>
</p>
p:hover span {
  animation: flicker 1s linear forwards;
}
p:hover #e {
  animation-delay: .2s;
}
p:hover #o {
  animation-delay: .5s;
}
p:hover #n2 {
  animation-delay: .6s;
}

@keyframes flicker {
  0% {
    color: #333;
  }
  5%, 15%, 25%, 30%, 100% {
    color: #fff;
    text-shadow: 
      0px 0px 5px var(--color),
      0px 0px 10px var(--color),
      0px 0px 20px var(--color),
      0px 0px 50px var(--color);
      
  }
  10%, 20% {
    color: #333;
    text-shadow: none;
  }
}

截 GIF 图的帧率不太够,看着效果不太好,可以点进下面的 DEMO 感受下:

CodePen Demo -- Neon Demo

文字与背景

CSS 中的背景 background,也提供了一些属性用于增强文字的效果。

background-clip 与文字

背景中有个属性为 background-clip, 其作用就是设置元素的背景(背景图片或颜色)的填充规则

box-sizing 的取值非常类似,通常而言,它有 3 个取值,border-boxpadding-boxcontent-box,后面规范新增了一个 background-clip。时至今日,部分浏览器仍需要添加前缀 webkit 进行使用 -webkit-background-clip

使用了这个属性的意思是,以区块内的文字作为裁剪区域向外裁剪,文字的背景即为区块的背景,文字之外的区域都将被裁剪掉。

看个最简单的 Demo ,没有使用 background-clip:text :

<div>Clip</div>

<style>
div {
  font-size: 180px;
  font-weight: bold;
  color: deeppink;
  background: url($img) no-repeat center center;
  background-size: cover;
}
</style>

效果如下:

image

CodePen Demo

使用 background-clip:text

我们稍微改造下上面的代码,添加 -webkit-background-clip:text

div {
  font-size: 180px;
  font-weight: bold;
  color: deeppink;
  background: url($img) no-repeat center center;
  background-size: cover;
  background-clip: text;
}

效果如下:

image

CodePen Demo

看到这里,可能有人就纳闷了,这不就是文字设置 color 属性嘛。

别急,由于文字设置了颜色,挡住了 div 块的背景,如果将文字设置为透明呢?文字是可以设置为透明的 color: transparent

div {
  color: transparent;
  background-clip: text;
}

效果如下:

image

CodePen Demo

通过将文字设置为透明,原本 div 的背景就显现出来了,而文字以外的区域全部被裁剪了,这就是 background-clip:text 的作用。

利用 background-clip 图文搭配

这样,我们可以选取合适的图片合适的字体,实现任意风格的文字效果。

image

CodePen Demo -- background-clip: text & Image text

又或者,利用这个效果实现一张创意海报:

利用 background-clip 实现渐变文字

再者,利用这个属性,也可以轻松的实现渐变色的文字:

{
    background: linear-gradient(45deg, #009688, yellowgreen, pink, #03a9f4, #9c27b0, #8bc34a);
    background-clip: text;
}

image

配合 background-position 或者 filter: hue-rotate(),让渐变动起来:

{
    background: linear-gradient(45deg, #009688, yellowgreen, pink, #03a9f4, #9c27b0, #8bc34a);
    background-clip: text;
    animation: huerotate 5s infinite;
}

@keyframes huerotate {
    100% {
        filter: hue-rotate(360deg);
    }
}

CodePen Demo -- background-clip: text 文字渐变色

利用 background-clip 给文字增加高光动画

利用 background-clip, 我们还可以轻松的给文字增加高光动画。

譬如这样:

其本质也是利用了 background-clip,伪代码如下:

<p data-text="Lorem ipsum dolor"> Lorem ipsum dolor </p>
p {
    position: relative;
    color: transparent;
    background-color: #E8A95B;
    background-clip: text;
}
p::after {
    content: attr(data-text);
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-image: linear-gradient(120deg, transparent 0%, transparent 6rem, white 11rem, transparent 11.15rem, transparent 15rem, rgba(255, 255, 255, 0.3) 20rem, transparent 25rem, transparent 27rem, rgba(255, 255, 255, 0.6) 32rem, white 33rem, rgba(255, 255, 255, 0.3) 33.15rem, transparent 38rem, transparent 40rem, rgba(255, 255, 255, 0.3) 45rem, transparent 50rem, transparent 100%);
    background-clip: text;
    background-size: 150% 100%;
    background-repeat: no-repeat;
    animation: shine 5s infinite linear;
}
@keyframes shine {
    0% {
        background-position: 50% 0;
    }
    100% {
        background-position: -190% 0;
    }
}

去掉伪元素的 background-clip: text,就能看懂原理:

11

CodePen Demo -- shine Text && background-clip

mask 与文字

还有一个与背景相关的属性 -- mask

之前在多篇文章都提到了 mask,比较详细的一篇是 -- 奇妙的 CSS MASK,本文不对 mask 的基本概念做过多讲解,向下阅读时,如果对一些 mask 的用法感到疑惑,可以再去看看。

只需要记住核心的,使用 mask 最重要结论就是:添加了 mask 属性的元素,其内容会与 mask 表示的渐变的 transparent 的重叠部分,并且重叠部分将会变得透明。

利用 mask,我们可以实现各种文字的出场特效:

<div>
    <p>Hello MASK</p>
</div>

核心的 CSS 代码:

div {
    mask: radial-gradient(circle at 50% 0%, #000, transparent 30%);
    animation: scale 6s infinite;
}
@keyframes scale {
    0% {
        mask-size: 100% 100%;
    }
    60%,
    100% {
        mask-size: 150% 800%;
    }
}

改变不同的方向,又或者是这样:

CodePen Demo -- MASK Text Effect

文字与混合模式(mix-blend-mode)及滤镜(filter)

接下来,就到了非常有意思的混合模式及滤镜了。这两个属性给 CSS 世界增添了非常多的趣味性,活灵活用,会感叹 CSS 居然如此的强大美妙。

之前有多非常多篇关于混合模式滤镜的文章,一些基础的用法就不再赘述。

给文字添加边框,生成镂空文字

在 CSS 中,我们可以利用 -webkit-text-stroke,给文字快速的添加边框,利用这个,可以快速生成镂空型的文字:

p {
    -webkit-text-stroke: 3px #373750;
}

image

当然,我们看到,用到的属性 -webkit-text-stroke 带了 webkit 前缀,存在一定的兼容性问题。

所以,在更早的时候,我们还会使用 text-shadow,生成镂空文字。

p {
    text-shadow: 0 0 5px #fff;
}

image

可以看到,因为使用的是阴影,所以有很明显的虚化的感觉,存在一定的瑕疵。

还有一种非常绕的方法,利用混合模式加上滤镜,也能生成镂空文字。

p {
    position: relative;
    color: #fff;

    &::after {
        content: 'Magic Text';
        position: absolute;
        left: 0;
        top: 0;
        color: #fff;
        mix-blend-mode: difference;
        filter: blur(1px);
    }
}

image

这里利用 filter: blur(1px) 生成了一个比原字体稍微大一点点的字体覆盖在原字体之上,再利用 mix-blend-mode: difference 消除掉了同色的部分,只留下了利用模糊滤镜多出来的那一部分。

mix-blend-mode: difference: 差值模式(Difference),作用是查看每个通道中的颜色信息,比较底色和绘图色,用较亮的像素点的像素值减去较暗的像素点的像素值。与白色混合将使底色反相;与黑色混合则不产生变化。

示意动图如下:

CodePen Demo -- Hollow TEXT EFFECT

利用混合模式,生成渐变色镂空文字

好,回到上面的 -webkit-text-stroke,拿到了镂空文字后,我们还可以利用混合模式 mix-blend-mode: multiply 生成渐变色的文字。

mix-blend-mode: multiply: 正片叠底(multiply),将两个颜色的像素值相乘,然后除以255得到的结果就是最终色的像素值。通常执行正片叠底模式后的颜色比原来两种颜色都深。任何颜色和黑色正片叠底得到的仍然是黑色,任何颜色和白色执行正片叠底则保持原来的颜色不变,而与其他颜色执行此模式会产生暗室中以此种颜色照明的效果。
p {
    position: relative;
    -webkit-text-stroke: 3px #9a9acc;

    &::before{
        content: ' ';
        width: 100%;
        height: 100%;
        position: absolute;
        left: 0;
        top: 0;
        background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
        mix-blend-mode: multiply;
    }
}

image

在这里,mix-blend-mode: multiply 发挥的作用和 mask 非常的类似,我们其实是生成了一幅渐变图案,但是只有在文字轮廓内,渐变颜色才会显现。

当然,上述效果和整体的黑色底色也是有关系的。

示意图如下:

CodePen Demo -- Hollow TEXT EFFECT

利用混合模式,生成光影效果文字

OK,在上述的基础上,我们可以继续叠加混合模式,这次我们利用剩余的一个 ::after 伪类,再添加一个 mix-blend-mode: color-dodge 混合模式,给文字加上最后的点缀,实现美妙的光影效果。

mix-blend-mode: color-dodge: 颜色减淡模式(Color Dodge),查看每个通道的颜色信息,通过降低“对比度”使底色的颜色变亮来反映绘图色,和黑色混合没变化。。

核心的伪代码:

p {
    position: relative;
    -webkit-text-stroke: 3px #7272a5;

    &::before {
        content: ' ';
        background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
        mix-blend-mode: multiply;
    }

    &::after {
        content: "";
        position: absolute;
        background: radial-gradient(circle, #fff, #000 50%);
        background-size: 25% 25%;
        mix-blend-mode: color-dodge;
        animation: mix 8s linear infinite;
    }
}

@keyframes mix {
    to {
        transform: translate(50%, 50%);
    }
}

看看效果:

这里就要感叹 mix-blend-mode: color-dodge 的神奇了,去掉这个混合模式,其实是这样的:

9

CodePen -- Hollow TEXT EFFECT

好,就上面这个效果,还可以继续吗?答案是可以的。限于篇幅,本文不再继续在这个效果上深入,感兴趣的可以拿着上面的 DEMO 自己再捣鼓捣鼓。

利用混合模式实现文字与底色反色的效果

这里还是利用 mix-blend-mode: difference 差值模式,实现一种文字与底色反色的 Title 效果。

mix-blend-mode: difference: 差值模式(Difference),作用是查看每个通道中的颜色信息,比较底色和绘图色,用较亮的像素点的像素值减去较暗的像素点的像素值。与白色混合将使底色反相;与黑色混合则不产生变化。

代码非常的简单,我们实现一个黑白相间的背景,文本的颜色为白色,配合上差值模式,即可实现黑底上的文字为白色,白底上的文字为黑色的效果。

p  {
    background: repeating-radial-gradient(circle at 200% 200%, #000 0, #000 150px, #fff 150px, #fff 300px);

    &::before {
        content: "LOREM IPSUM";
        color: #fff;
        mix-blend-mode: difference;
    }
}

可以用于一些标题效果:

image

CodePen Demo -- Radial-gradient + Mix-blend-mode

利用混合模式实现动态类抖音风格 Glitch 效果

OK,接下来,我们再尝试下其他混合模式的搭配。在 CSS 故障艺术 一文中,提到了一种故障艺术。

什么是故障艺术?我们熟知的抖音的 LOGO 正是故障艺术其中一种表现形式。它有一种魔幻的感觉,看起来具有闪烁、震动的效果,很吸引人眼球。

关键点

  • 利用 mix-blend-mode: lighten 混合模式实现两段文字结构重叠部分为白色
  • 利用元素位移完成错位移动动画,形成视觉上的冲击效果

看看效果:

textglitch

本文篇幅有点长,代码就不上了,完整 DEMO 在这里:

类抖音 LOGO 文字故障效果

当然,我们也不是一定要使用混合模式去使得融合部分为白色,可以仅仅是使用这个配色效果,基于上面效果的另外一个版本,没有使用混合模式。

关键点

  • 利用了伪元素生成了文字的两个副本
  • 视觉效果由位移、遮罩、混合模式完成
  • 配色借鉴了抖音 LOGO 的风格

textglitch2

完整 DEMO 在这里:

CSS文字故障效果

仅仅使用配色没有使用混合模式的好处在于,对于每一个文字的副本,有了更大的移动距离和可以处理的空间。

Glitch Art 风格的 404 效果

稍微替换一下文本文案为 404,再添加上一些滤镜效果(hue-rotate()blur())嘿嘿,找到了一个可能实际可用的场景:

效果一:

404

效果二:

404

两个 404 效果的 Demo 如下:

小技巧,在使用混合模式的时,有的时候,效果不希望和背景混合在一起,可以使用 isolation: isolate 进行隔离。

使用滤镜生成文字融合效果

你所不知道的 CSS 滤镜技巧与细节 一文中,介绍了利用滤镜实现的一种融合效果。

利用了模糊滤镜叠加对比度滤镜产生的融合效果

单独将两个滤镜拿出来,它们的作用分别是:

  1. filter: blur(): 给图像设置高斯模糊效果。
  2. filter: contrast(): 调整图像的对比度。

但是,当他们“合体”的时候,产生了奇妙的融合现象。简单的例子:

filtermix

CodePen Demo -- filter mix between blur and contrast

利用这个特性,可以实现一些文字融合的效果:

利用这个方法,我们还可以设计一些文字融合的效果:

wordanimation

bluranimation

具体实现你可以看这里:

CodePen Demo -- word animation | word filter

文字与 SVG

最后,我们再来看看文字与 SVG。

在 SVG 与 CSS 的搭配中,有一类非常适合拿来做动画的属性,也就是 stroke-* 相关的几个属性,利用它们,我们只需要掌握简单的 SVG 语法,就可以快速制作相关的线条动画。

我们利用 SVG 中几个和边框、线条相关的属性,来实现文字的线条动画,下面罗列一下,其实大部分和 CSS 对比一下非常好理解,只是换了个名字:

  • stroke-width:类比 css 中的 border-width,给 svg 图形设定边框宽度;
  • stroke:类比 css 中的 border-color,给 svg 图形设定边框颜色;
  • stroke-linejoin | stroke-linecap:设定线段连接处的样式;
  • stroke-dasharray:值是一组数组,没数量上限,每个数字交替表示划线与间隔的宽度;
  • stroke-dashoffset:则是虚线的偏移量
具体的更深入的介绍,可以看看这篇:【Web动画】SVG 线条动画入门

线条文字动画

接下来,我们利用 stroke-* 相关属性,实现一个简单的线条文字动画。

<svg viewBox="0 0 400 200">
    <text x="0" y="70%"> Lorem </text>
</svg>    
svg text {
    animation: stroke 5s infinite alternate;
    letter-spacing: 10px;
    font-size: 150px;
}
@keyframes stroke {
    0% {
        fill: rgba(72, 138, 20, 0);
        stroke: rgba(54, 95, 160, 1);
        stroke-dashoffset: 25%;
        stroke-dasharray: 0 50%;
        stroke-width: 1;
    }
    70% {
        fill: rgba(72, 138, 20, 0);
        stroke: rgba(54, 95, 160, 1);
        stroke-width: 3;
    }
    90%,
    100% {
        fill: rgba(72, 138, 204, 1);
        stroke: rgba(54, 95, 160, 0);
        stroke-dashoffset: -25%;
        stroke-dasharray: 50% 0;
        stroke-width: 0;
    }
}

动画的核心就是,利用动态变化文字的 stroke-dasharraystroke-dashoffset 形成视觉上的线条变换,动画的最后再给文字上色。看看效果:

CodePen Demo -- SVG Text Line Effect

最后

本文介绍了一些我认为比较有意思的文字动画小技巧,当然 CSS 中还有非常多有意思的文字效果,限于篇幅,不一一展开。

本文到此结束,希望对你有帮助 :),想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

123.png

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

查看原文

赞 37 收藏 30 评论 2

JasonSubmara 回答了问题 · 3月8日

一台电脑如何同时装vue2.x和vue3.x?

项目的Vue版本实际上跟你自己电脑本身全局安装的Vue版本并无关系,如果一定要同时安装,可以考虑使用 nvm 或者 nvm-windows 来安装不同的node环境 ,在相对较低的版本里面全局安装Vue2.x,相对较高的版本里面全局安装Vue3.x
具体安装 nvm 教程可以参考这个 https://segmentfault.com/a/11...

关注 3 回答 2

JasonSubmara 回答了问题 · 3月3日

解决请问这种图表怎么画啊,我找了半天没找到例子

image.png

AntV官方案例中有这样子的一个,右侧的图不确定是否可修改,你可以看下官方的文档,有绘制连线区域的代码和右侧放大内容的渲染代码

import { Chart } from '@antv/g2';

const otherRatio = 6.67 / 100; // Other 的占比
const otherOffsetAngle = otherRatio * Math.PI; // other 占的角度的一半
const data = [
  { type: '微博', value: 93.33 },
  { type: '其他', value: 6.67 },
];
const other = [
  { type: '论坛', value: 1.77 },
  { type: '网站', value: 1.44 },
  { type: '微信', value: 1.12 },
  { type: '客户端', value: 1.05 },
  { type: '新闻', value: 0.81 },
  { type: '视频', value: 0.39 },
  { type: '博客', value: 0.37 },
  { type: '报刊', value: 0.17 },
];
const chart = new Chart({
  container: 'container',
  autoFit: true,
  height: 500,
});
chart.legend(false);
chart.tooltip({
  showMarkers: false,
});

const view1 = chart.createView({
  region: {
    start: {
      x: 0,
      y: 0,
    },
    end: {
      x: 0.5,
      y: 1,
    },
  },
});
view1.coordinate('theta', {
  radius: 0.7,
  startAngle: 0 + otherOffsetAngle,
  endAngle: Math.PI * 2 + otherOffsetAngle,
});
view1.data(data);
view1.interaction('element-highlight');
view1
  .interval()
  .adjust('stack')
  .position('value')
  .color('type', ['#38c060', '#2593fc'])
  .label('value', function () {
    return {
      offset: -10,
      content: (obj) => {
        return obj.type + '\n' + obj.value + '%';
      },
    };
  });

const view2 = chart.createView({
  region: {
    start: {
      x: 0.5,
      y: 0.1,
    },
    end: {
      x: 1,
      y: 0.9,
    },
  },
});
view2.axis(false);
view2.data(other);
view2.interaction('element-highlight');
view2
  .interval()
  .adjust('stack')
  .position('value')
  .color('type', ['#063d8a', '#0b53b0', '#1770d6', '#2593fc', '#47abfc', '#6dc1fc', '#94d6fd', '#bbe7fe'])
  .label('value', {
    position: 'right',
    offsetX: 5,
    offsetY: 10,
    content: (obj) => {
      return obj.type + ' ' + obj.value + '%';
    },
  });
chart.render();
drawLinkArea();
chart.on('afterpaint', function () {
  drawLinkArea();
});

/* ---------绘制连接区间-----------*/
function drawLinkArea() {
  const canvas = chart.getCanvas();
  const container = chart.backgroundGroup;
  const view1_coord = view1.getCoordinate();
  const center = view1_coord.getCenter();
  const radius = view1_coord.getRadius();
  const interval_geom = view2.geometries[0];
  const interval_container = interval_geom.container;
  const interval_bbox = interval_container.getBBox();
  const view2_coord = view2.getCoordinate();
  // area points
  const pie_start1 = {
    x: center.x + Math.cos(Math.PI * 2 - otherOffsetAngle) * radius,
    y: center.y + Math.sin(Math.PI * 2 - otherOffsetAngle) * radius,
  };
  const pie_start2 = {
    x: center.x + Math.cos(otherOffsetAngle) * radius,
    y: center.y + Math.sin(otherOffsetAngle) * radius,
  };
  const interval_end1 = {
    x: interval_bbox.minX,
    y: view2_coord.end.y,
  };
  const interval_end2 = {
    x: interval_bbox.minX,
    y: view2_coord.start.y,
  };
  const path = [
    ['M', pie_start1.x, pie_start1.y],
    ['L', pie_start2.x, pie_start2.y],
    ['L', interval_end2.x, interval_end2.y],
    ['L', interval_end1.x, interval_end1.y],
    ['Z'],
  ];
  container.addShape('path', {
    attrs: {
      path,
      fill: '#e9f4fe',
    },
  });
  canvas.draw();
}

关注 3 回答 1

认证与成就

  • 获得 14 次点赞
  • 获得 5 枚徽章 获得 0 枚金徽章, 获得 1 枚银徽章, 获得 4 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • linelip

    Multiline text overflow ellipsis

  • normalreset.css

    A modern alternative to CSS resets Based on Nicolas Gallagher and Jonathan Neal's normalize.css and Eric's reset.css

注册于 2017-12-26
个人主页被 1.1k 人浏览