Skip to content

cover

SVG入门到入土

相信绝大多数前端同学都不愿意自己手写SVG代码吧,图片上的事情都是直接交给UI省事的多,但是我学习了SVG的基础用法之后,发现SVG还是可以给平时开发带来很大便利的,至少可以简单的修改SVG属性以及使用SVG做出简单的效果。此篇文章是我学习SVG的全过程,我将查阅到的资料综合起来并加入一些我的个人见解和实现的例子,记录下来,方便日后查阅,得益于VitePress提供markdown拓展能力,可以在markdown中直接编写Vue代码,文章中所有例子我都没有使用效果截图,而实直接展示代码效果。文中部分内容参考自他人的文章,我已注明出处。我也希望此篇文章能帮助到其他前端小伙伴入门SVG,一起享受SVG带来的便利。

什么是SVG

SVG 是一种 XML 语言,可以用来绘制 矢量图形 。什么是矢量图呢?常见的图片被是由像素构成的,放大图形之后会失真,边缘出现锯齿状,这类图称之为位图,而矢量图形是通过 XML 语言描述的,放大后不会失真,举一个通俗点的例子,矢量图通过代码定义了一个圆,不管图形放多大,渲染器都会把它画成一个平滑的圆,位图放大之后像素也被放大了,所以边缘会出现由一个个放大的像素构成的锯齿。

我分别用canvas和SVG绘制出两个半径10px的圆形并放大5倍做对比:

点击查看源码
vue
<script setup>
import {onMounted, ref} from "vue";

const canvasRef = ref();
onMounted(() => {
  const canvas = canvasRef.value;
  canvas.width = 100;
  canvas.height = 100;
  const ctx = canvas.getContext('2d');
  ctx.beginPath();
  ctx.arc(50, 50, 10, 0, 2 * Math.PI);
  ctx.stroke();
})
</script>

<template>
  <div class="wrapper">
    <el-card class="card">
      <canvas id="canvas" ref="canvasRef"></canvas><!--//-->
    </el-card>
    <el-card class="card">
      <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><!--//-->
        <circle cx="50" cy="50" r="10" stroke="black" stroke-width="1" fill="none"/>
      </svg>
    </el-card>
  </div>
</template>

<style scoped lang="scss">
.wrapper{
  display: flex;
  gap: 20px;
}
.card{
  width: fit-content;
}
#canvas{
  width: 100px;
  height: 100px;
  transform: scale(5)/* [!code focus]*/
}
svg{
  transform: scale(5)/* [!code focus]*/
}
</style>

canvas绘制出来的图形是位图,放大后可以明显的锯齿状,而SVG绘制的圆形依然圆润。

使用方法

常见svg使用方法有3种:

  • .svg 结尾的文件可以视作一种图片格式,所以可以用 img 标签的 src 属性直接引入 例如:
html
<img src="./test.svg" alt="svg">
  • 作为一种图片格式,自然也可以通过cssbackground属性引入:
css
.svg-bg {
  background: url('./test.svg') no-repeat;
}
  • 直接内嵌到 HTML 中,这种方式是最推荐的,如果使用了Vue,也可以将svg写成单独的组件,方便引用:
html
<div class="svg-wrapper">
  <svg width="100" height="100">
    <circle cx="50" cy="50" r="10" stroke="black" stroke-width="1" fill="none"/>
  </svg>
</div>
vue
<!-- @/components/icons/IconTest.vue -->

<template>
  <svg width="100" height="100">
    <circle cx="50" cy="50" r="10" stroke="black" stroke-width="1" fill="none"/>
  </svg>
</template>

<!-- @/views/test.vue -->

...
<span><IconTest />组件引入SVG</span>
...

svg标签

SVG代码都放在 <svg> 标签之中,widhtheight 属性指定了svg元素占据宽高,可以直接写数字代表的是相应的像素值,也可以使用和css相同的属性值,例如:widht="100px",widht="100%" ,如果不指定这两个属性,SVG的默认大小是300×150(像素)。

如果只展示SVG部分图像,可以使用 viewBox 指定可视区域,下图为一个 widht:"100" height:"100" 的svg图形:

当我们将其设置为 width="100" height="100" viewBox="0 0 50 50",图形则变成了:

viewBox 的4个数字分别表示左上角的横坐标和纵坐标、视口的宽度和高度,上面图形种,SVG 图像是100像素宽 x 100像素高,viewBox 属性指定视口从(0, 0)这个点开始,长50像素,宽50像素的一个视口区域。所以,实际看到的是左上角的四分之一圆。

基本形状

这里介绍的SVG中的几个基本的形状,可以通过这些形状的命名便可知道其用途。

矩形

使用 <rect> 标签代表矩形,矩形最少需要设置4个属性来确定矩形起始位置和宽高,也可以设置圆角属性,作用和css中的 border-radius 一样。

html
<svg width="200" height="100">
  <rect x="20" y="20" width="60" height="60"/>
  <rect x="120" y="20" rx="10" ry="10" width="60" height="60"/>
</svg>

属性:

  • x :左上角x轴坐标
  • y :左上角y轴坐标
  • width :矩形的宽度
  • height :矩形的高度
  • rx :圆角在x轴方位的半径
  • ry :圆角在y轴方位的半径

圆形

使用 <circle> 标签代表圆形,需要设置圆的半径和中心坐标。

html
<svg width="100" height="100">
  <circle cx="50" cy="50" r="50"/>
</svg>

属性:

  • r :圆的半径
  • c1 :圆心在x轴的坐标
  • c2 :圆心在y轴的坐标

椭圆

使用 <ellipse> 定义椭圆,不同于数学上定义椭圆需要两个确定两个焦点,SVG只需要确定一个中心点,另外,还需要确定一个x轴半径(长轴半径)和y轴半径(短轴半径)。

html
<svg width="100" height="100">
  <ellipse cx="50" cy="50" rx="50" ry="30"/>
</svg>

属性:

  • cx :圆心在x轴的坐标
  • cy :圆心在y轴的坐标
  • rx :x轴的半径
  • ry :y轴的半径

直线

使用 <line> 定义直线,直线就和数学上的线段一样了,需要确定起点和终点坐标。

html
<svg width="100" height="100">
  <line x1="10" x2="50" y1="110" y2="150" stroke="black"/>
</svg>

属性:

  • x1 :起始点x坐标
  • y1 :起始点y坐标
  • x2 :结束点x坐标
  • y2 :结束点y坐标

折线

使用 <polyline> 绘制折线,折线就不止要定义两个点了,要定义一个点的集合。

html
<svg width="100" height="100">
  <polyline
      points="10 10, 50 50, 10 90"/>
</svg>

属性:

  • points:点集数列。每个点必须包含 2 个数字,一个是 x 坐标,一个是 y 坐标,每个数字用空白、逗号、终止命令符或者换行符分隔开,点集(0,0), (1,1)(2,2) 可以写成这样:0,0 1,1 2,2,也可以写成这样 0 0, 1 1, 2 2,也可以只要空格 0 0 1 1 2 2

多边形

多边形使用 <polygon> 标签,和折线一样,多边形需要一个点集,顶点的点集。

html
<svg width="100" height="100">
  <polygon points="10 10, 50 50, 10 90"/>
</svg>

属性:

  • points :点集数列。多边形首尾会自动闭合,所以点集第一个和最后一个点不需要设置成同一个点。

路径

使用 path 绘制路径, path 是SVG中最常用的也是最强大的图形,你可以用它创建线条,曲线,弧形等,上面介绍的图形都可以用 path 绘制出来。所有描述轮廓的数据都放在 d 属性里,ddata 的简写。

d 属性包含了众多命令,以字母开头,后面跟的数字是参数,比如字母 M 10 10M 表示 Move to的意思,两个数字表示移动至 (10,10) 点。每一个命令有两种表示方式,一种是大写字母,表示采用绝对定位,另一种是用小写字母,表示采用相对定位(例如:从上一个点开始,向上移动 10px,向左移动 10px)。

直线命令

path 有5种画直线的命令:

M

起始点坐标,Move to 的意思。每个路径都必须以 M 开始。M 传入 xy 坐标,用逗号或者空格隔开。下面是 M 的一个例子,但是单独一个 M 指令不会绘制任何图形,但是我将移动到的点标注出来了。


L

Line to的意思。L 需要两个参数,分别是一个点的 x 轴和 y 轴坐标,L 命令将会在当前位置和新位置(L 前面画笔所在的点)之间画一条线段。


H

Horizontal line to的意思,由于是水平移动那目标位置,所以只需要一个参数,即目标点x轴。


V

Vertical line to的意思,也是只需要一个参数,目标点y轴。


Z

关闭当前路径,closepath 的意思。它会绘制一条直线回到当前子路径的起点,不用区分大小写。

代码如下:

html
<svg width="100" height="100">
  <path d="M0 0,L50 30 H90 V80Z " stroke="red" fill="none"></path>
</svg>

曲线命令

参考自MDN

SVG 中绘制平滑曲线的命令有3个,其中一个可以绘制圆弧,另外两个用来绘制贝塞尔曲线。

A

A 为绘制圆弧的命令。 弧形可以视为圆形或椭圆形的一部分,假设,已知椭圆形的长轴半径和短轴半径,并且已知两个点(在椭圆上),根据半径和两点,可以画出两个椭圆,在每个椭圆上根据两点都可以画出两种弧形。所以,仅仅根据半径和两点,可以画出四种弧形。为了保证创建的弧形唯一,A 命令需要用到比较多的参数:

html
A(rx, ry, xr, laf, sf, x, y)
  • rx :椭圆X轴半径
  • ry :椭圆Y轴半径
  • xr :椭圆旋转角度
  • laf :是否选择弧长较长的那一段。0: 短边(小于180度); 1: 长边(大于等于180度)
  • sf :是否顺时针绘制。0: 逆时针; 1: 顺时针
  • x :终点X轴坐标
  • y :终点Y轴坐标

上面的公式中并没有起点,起点其实是由 M 或者上一次绘制的终点决定的,我们通过两个点和椭圆的半径,可以绘制出两个椭圆,椭圆可以被切割出4个圆弧:

html
  <svg width="400" height="200">
    <line x1="2" y1="1" x2="400" y2="200" stroke="#999"></line>
    <!--红--><!---->
    <path d="M160 80 A100 50 0 1 1 240 120" stroke="red" fill="none"></path>
    <!--蓝-->
    <path d="M160 80 A100 50 0 0 0 240 120" stroke="blue" fill="none"></path>
    <!--绿-->
    <path d="M160 80 A100 50 0 1 0 240 120" stroke="green" fill="none"></path>
    <!--黄-->
    <path d="M160 80 A100 50 0 0 1 240 120" stroke="#efef00" fill="none"></path>
    <circle cx="160" cy="80" r="3" fill="black"/>
    <circle cx="240" cy="120" r="3" fill="black"/>
  </svg>

上面列子是固定旋转角度 xr 属性的,如果在加上旋转角度,则可以绘制出固定两点与椭圆半径的无数个弧线:

滑动改变 xr值:

我们不妨大胆点,再将其他元素值动态化:

起始点坐标: x : y :
终点坐标: x : y :
椭圆半径: x : y :

C

C 指令可以用于绘制三次贝塞尔曲线,需要定义两个控制点和一个终点的坐标参数:

html
C x1 y1, x2 y2, x y
html
<svg width="100" height="100">
  <path d="M0 60 C40 20, 60 20, 100 60" fill="none" stroke="red"></path>
</svg>

这里的最后一个坐标 (x,y) 表示的是曲线的终点,另外两个坐标是控制点,(x1,y1) 是起点的控制点,(x2,y2) 是终点的控制点。控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程,下面是斜率的变化过程:

bezier

图例上的曲线从左往右看,控制点在水平方向上逐渐分开,图例上的曲线从上往下看,控制点和曲线坐标之间离得越来越远。这里要注意观察,曲线沿着起点到第一控制点的方向伸出,逐渐弯曲,然后沿着第二控制点到终点的方向结束。

你可以将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线。通常情况下,一个点某一侧的控制点是它另一侧的控制点的对称(以保持斜率不变)。这样,你可以使用一个简写的贝塞尔曲线命令 S :

html
S x2 y2, x y

S

S 命令可以用来创建与前面一样的贝塞尔曲线,但是,如果 S 命令跟在一个 CS 命令后面,则它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。如果 S 命令单独使用,前面没有 CS 命令,那当前点将作为第一个控制点。下面是 S 命令的语法示例,图中左侧红色标记的点对应的控制点即为蓝色标记点。

S

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>

Q

另一种可用的贝塞尔曲线是二次贝塞尔曲线 Q,它比三次贝塞尔曲线简单,只需要一个控制点,用来确定起点和终点的曲线斜率。因此它需要两组参数,控制点和终点坐标。

html
Q x1 y1, x y

Q

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

T

就像三次贝塞尔曲线有一个 S 命令,二次贝塞尔曲线有一个差不多的 T 命令,可以通过更简短的参数,延长二次贝塞尔曲线。

html
T x y

和之前一样,快捷命令 T 会通过前一个控制点,推断出一个新的控制点。这意味着,在你的第一个控制点后面,可以只定义终点,就创建出一个相当复杂的曲线。需要注意的是,T 命令前面必须是一个 Q 命令,或者是另一个 T 命令,才能达到这种效果。如果 T 单独使用,那么控制点就会被认为和终点是同一个点,所以画出来的将是一条直线。

Q

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <path d="M 10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/>
</svg>

虽然三次贝塞尔曲线拥有更大的自由度,但是两种曲线能达到的效果总是差不多的。具体使用哪种曲线,通常取决于需求,以及对曲线对称性的依赖程度。

填充和边框

参考自MDN

上色

大多数基本的涂色可以通过在元素上设置两个属性来搞定:fill 属性和 stroke 属性。fill 属性设置对象内部的颜色,stroke 属性设置绘制对象的线条的颜色。你可以使用在 HTML 中的 CSS 颜色命名方案定义它们的颜色,比如说颜色名(像red这种)、rgb 值(像 rgb(255,0,0) 这种)、十六进制值、rgba 值,等等。

html
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
  <rect x="10" y="10" width="100" height="100" stroke="red" fill="black"
        fill-opacity="0.5" stroke-opacity="0.8"/>
</svg>

此外,在SVG中你可以分别定义填充色和边框色的不透明度,属性 fill-opacity 控制填充色的不透明度,属性 stroke-opacity 控制描边的不透明度。

描边

除了颜色属性,还有其他一些属性用来控制绘制描边的方式。

html
<svg width="160" height="140" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <line x1="40" x2="120" y1="20" y2="20" stroke="black" stroke-width="20" stroke-linecap="butt"/>
  <line x1="40" x2="120" y1="60" y2="60" stroke="black" stroke-width="20" stroke-linecap="square"/>
  <line x1="40" x2="120" y1="100" y2="100" stroke="black" stroke-width="20" stroke-linecap="round"/>
</svg>

stroke-width 属性定义了描边的宽度, stroke-linecap 属性控制边框终点的形状。

stroke-linecap 属性的值有三种可能值:

  • butt 用直边结束线段,它是常规做法,线段边界 90 度垂直于描边的方向、贯穿它的终点。
  • square 的效果差不多,但是会稍微超出实际路径的范围,超出的大小由 stroke-width 控制。
  • round 表示边框的终点是圆角,圆角的半径也是由 stroke-width 控制的。

还有一个stroke-linejoin属性,用来控制两条描边线段之间,用什么方式连接。

html
<svg width="160" height="280" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <polyline points="40 60 80 20 120 60" stroke="black" stroke-width="20" stroke-linecap="butt" fill="none" stroke-linejoin="miter"/>
  
  <polyline points="40 140 80 100 120 140" stroke="black" stroke-width="20" stroke-linecap="round" fill="none" stroke-linejoin="round"/>
  
  <polyline points="40 220 80 180 120 220" stroke="black" stroke-width="20" stroke-linecap="square" fill="none" stroke-linejoin="bevel"/>
</svg>

每条折线都是由两个线段连接起来的,连接处的样式由 stroke-linejoin 属性控制,它有三个可用的值,miter 是默认值,表示用方形画笔在连接处形成尖角,round 表示用圆角连接,实现平滑效果。最后还有一个值 bevel,连接处会形成一个斜接。

最后,你可以通过指定stroke-dasharray属性,将虚线类型应用在描边上。

html
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <path d="M 10 75 Q 50 10 100 75 T 190 75" stroke="black"
    stroke-linecap="round" stroke-dasharray="5,10,5" fill="none"/>
  <path d="M 10 75 L 190 75" stroke="red"
    stroke-linecap="round" stroke-width="1" stroke-dasharray="5,5" fill="none"/>
</svg>

stroke-dasharray 属性的参数,是一组用逗号分割的数字组成的数列。注意,和path不一样,这里的数字必须用逗号分割(空格会被忽略)。每一组数字,第一个用来表示填色区域的长度,第二个用来表示非填色区域的长度。所以在上面的例子里,第二个路径会先做 5 个像素单位的填色,紧接着是 5 个空白单位,然后又是 5 个单位的填色。如果你想要更复杂的虚线模式,你可以定义更多的数字。第一个例子指定了 3 个数字,这种情况下,数字会循环两次,形成一个偶数的虚线模式(奇数个循环两次变偶数个)。所以该路径首先渲染 5 个填色单位,10 个空白单位,5 个填色单位,然后回头以这 3 个数字做一次循环,但是这次是创建 5 个空白单位,10 个填色单位,5 个空白单位。通过这两次循环得到偶数模式,并将这个偶数模式不断重复。

另外还有一些关于填充和边框的属性,包括 fill-rule,用于定义如何给图形重叠的区域上色;stroke-miterlimit,定义什么情况下绘制或不绘制边框连接的 miter 效果;还有 stroke-dashoffset,定义虚线开始的位置。

小技巧

使用 stroke-dasharraystroke-dashoffset 可以实现非常炫酷的描边动画。 使用stroke-dashoffset 快速实现SVG描边动画

使用CSS

除了定义对象的属性外,你也可以通过 CSS 来样式化填充和描边。语法和在 HTML 里使用 CSS 一样,只不过你要把background-colorborder 改成 fill stroke。注意,不是所有的属性都能用 CSS 来设置。上色和填充的部分一般是可以用 CSS 来设置的,比如fillstrokestroke-dasharray等,但是不包括下面会提到的渐变和图案等功能。另外,widthheight,以及路径的命令等等,都不能用 CSS 设置。判断它们能不能用 CSS 设置还是比较容易的。

备注

SVG 规范将 属性区分成 properties 和其他 attributes,前者是可以用 CSS 设置的,后者不能。

CSS 可以使用行内样式插入到 SVG 元素:

html
 <rect x="10" height="180" y="10" width="180" style="stroke: black; fill: red;"/>

或者将 CSS 写入 <style>标签中,只不过我们需要将 <style> 标签写入 <defs> 标签中而不是 <head>中:

xml
<?xml version="1.0" standalone="no"?>
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <style><![CDATA[
       #MyRect {
         stroke: black;
         fill: red;
       }
    ]]></style>
  </defs>
  <rect x="10" height="180" y="10" width="180" id="MyRect"/>
</svg>

如上把样式放到一块你可以更轻松地调整一大组元素的样式,同样你也可以使用hover这样的伪类来创建翻转之类的效果:

css
#MyRect:hover {
  stroke: black;
  fill: blue;
}

一些可以在 HTML 里使用的CSS,在 SVG 里可能无法正常工作,比如 beforeafter 伪类。所以这里需要一点经验。

你也可以额外引入外部 CSS 样式文件:

xml
<?xml-stylesheet type="text/css" href="style.css"?>
<svg>...</svg>

小技巧

据我实战经验,现如今 <style> 标签已经可以直接在 <svg> 标签中写入样式了,且不用写在 <![CDATA[]]> 标签内。如果是内联 SVG,是可以直接在外部声明样式的,例如在 Vue中,你可以直接在 <style>标签内定义 SVG 样式

渐变

参考自MDN

要在 SVG 中实现渐变,可就不像在 SVG 中定义 SVG 样式可以直接写 CSS 了,你必须在 <defs> 中定义渐变节点,再使用 id 引入它。

线性渐变

线性渐变需要在 <defs> 元素内部创建一个 <linearGradient> 节点。

点击查看源代码
xml
<svg width="120" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <linearGradient id="Gradient1">
      <stop class="stop1" offset="0%" />
      <stop class="stop2" offset="50%" />
      <stop class="stop3" offset="100%" />
    </linearGradient>
    <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
      <stop offset="0%" stop-color="red" />
      <stop offset="50%" stop-color="black" stop-opacity="0" />
      <stop offset="100%" stop-color="blue" />
    </linearGradient>
    <style type="text/css">
      <![CDATA[
              #rect1 { fill: url(#Gradient1); }
              .stop1 { stop-color: red; }
              .stop2 { stop-color: black; stop-opacity: 0; }
              .stop3 { stop-color: blue; }
            ]]>
    </style>
  </defs>

  <rect id="rect1" x="10" y="10" rx="15" ry="15" width="100" height="100" />
  <rect
    x="10"
    y="120"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#Gradient2)" />
</svg>

以上是一个应用了线性渐变的 <rect> 元素的示例。线性渐变内部有几个 <stop> 结点,这些结点通过指定位置的 offset(偏移)属性和 stop-color(颜色中值)属性来说明在渐变的特定位置上应该是什么颜色;可以直接指定这两个属性值,也可以通过 CSS 来指定他们的值,该例子中混合使用了这两种方法。例如:该示例中指明了渐变开始颜色为红色,到中间位置时变成半透明的黑色,最后变成蓝色。虽然你可以根据需求按照自己的喜好插入很多中间颜色,但是偏移量应该始终从 0% 开始(或者 0 也可以,百分号可以扔掉),到 100%(或 1)结束。如果stop设置的位置有重合,将使用 XML 树中较晚设置的值。而且,类似于填充和描边,你也可以指定属性 stop-opacity 来设置某个位置的半透明度(同样,对于 FF3 你也可以设置 rgba 值)。

xml
<stop offset="100%" stop-color="yellow" stop-opacity="0.5" />

使用渐变时,我们需要在一个对象的属性 fill 或属性 stroke 中引用它,这跟你在 CSS 中使用 url 引用元素的方法一样。在本例中,url 只是一个渐变的引用,我们已经给这个渐变一个ID——“Gradient”。要想附加它,将属性 fill 设置为 url(#Gradient) 即可。现在对象就变成多色的了,也可以用同样的方式处理 stroke

<linearGradient> 元素还需要一些其他的属性值,它们指定了渐变的大小和出现范围。渐变的方向可以通过两个点来控制,它们分别是属性 x1x2y1y2,这些属性定义了渐变路线走向。渐变色默认是水平方向的,但是通过修改这些属性,就可以旋转该方向。在上面第二个例子中创建了一个垂直渐变。

备注

你也可以在渐变上使用 xlink:href 属性。如果使用了该属性时,一个渐变的属性和颜色中值(stop)可以被另一个渐变包含引用。在下例中,你就不需要在 "Grandient2" 中重新创建全部的颜色中值(stop)。

html
<linearGradient id="Gradient1">
  <stop id="stop1" offset="0%" />
  <stop id="stop2" offset="50%" />
  <stop id="stop3" offset="100%" />
</linearGradient>
<linearGradient
        id="Gradient2"
        x1="0"
        x2="0"
        y1="0"
        y2="1"
        xmlns:xlink="http://www.w3.org/1999/xlink"
        xlink:href="#Gradient1" />

尽管通常你可能在文档的顶部就定义了 "Gradient1",但我在结点上直接包含了 xlink 的命名空间,关于这点的更多信息我们会在讨论图片的时候详解。

径向渐变

径向渐变与线性渐变相似,只是它是从一个点开始发散绘制渐变。创建径向渐变需要在文档的 <defs> 中添加一个<radialGradient>元素。

点击查看源码
html
<?xml version="1.0" standalone="no"?>
<svg width="120" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <radialGradient id="RadialGradient1">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
    <radialGradient id="RadialGradient2" cx="0.25" cy="0.25" r="0.25">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
  </defs>

  <rect
    x="10"
    y="10"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#RadialGradient1)" />
  <rect
    x="10"
    y="120"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#RadialGradient2)" />
</svg>

中值(stop)的使用方法与之前一致,但是现在这个对象的颜色是中间是红色的,且向着边缘的方向渐渐的变成蓝色。跟线性渐变一样,<radialGradient> 节点可以有多个属性来描述其位置和方向,但是它更加复杂。径向渐变也是通过两个点来定义其边缘位置,两点中的第一个点定义了渐变结束所围绕的圆环,它需要一个中心点,由 cxcy 属性及半径 r 来定义,通过设置这些点我们可以移动渐变范围并改变它的大小,如上例的第二个 <rect> 所展示的。

第二个点被称为焦点,由 fxfy 属性定义。第一个点描述了渐变边缘位置,焦点则描述了渐变的中心,如下例。

中心和焦点

点击查看源码
html
<?xml version="1.0" standalone="no"?>

<svg width="120" height="120" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <radialGradient id="Gradient" cx="0.5" cy="0.5" r="0.5" fx="0.25" fy="0.25">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
  </defs>

  <rect
    x="10"
    y="10"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#Gradient)"
    stroke="black"
    stroke-width="2" />

  <circle
    cx="60"
    cy="60"
    r="50"
    fill="transparent"
    stroke="white"
    stroke-width="2" />
  <circle cx="35" cy="35" r="2" fill="white" stroke="white" />
  <circle cx="60" cy="60" r="2" fill="white" stroke="white" />
  <text x="38" y="40" fill="white" font-family="sans-serif" font-size="10pt">
    (fx,fy)
  </text>
  <text x="63" y="63" fill="white" font-family="sans-serif" font-size="10pt">
    (cx,cy)
  </text>
</svg>
(fx,fy) (cx,cy)

spreadMethod

点击查看源码
html
<?xml version="1.0" standalone="no"?>

<svg width="220" height="220" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <radialGradient
      id="GradientPad"
      cx="0.5"
      cy="0.5"
      r="0.4"
      fx="0.75"
      fy="0.75"
      spreadMethod="pad">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
    <radialGradient
      id="GradientRepeat"
      cx="0.5"
      cy="0.5"
      r="0.4"
      fx="0.75"
      fy="0.75"
      spreadMethod="repeat">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
    <radialGradient
      id="GradientReflect"
      cx="0.5"
      cy="0.5"
      r="0.4"
      fx="0.75"
      fy="0.75"
      spreadMethod="reflect">
      <stop offset="0%" stop-color="red" />
      <stop offset="100%" stop-color="blue" />
    </radialGradient>
  </defs>

  <rect
    x="10"
    y="10"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#GradientPad)" />
  <rect
    x="10"
    y="120"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#GradientRepeat)" />
  <rect
    x="120"
    y="120"
    rx="15"
    ry="15"
    width="100"
    height="100"
    fill="url(#GradientReflect)" />

  <text x="15" y="30" fill="white" font-family="sans-serif" font-size="12pt">
    Pad
  </text>
  <text x="15" y="140" fill="white" font-family="sans-serif" font-size="12pt">
    Repeat
  </text>
  <text x="125" y="140" fill="white" font-family="sans-serif" font-size="12pt">
    Reflect
  </text>
</svg>
Pad Repeat Reflect

两种渐变都有一个叫做 gradientUnits(渐变单元)的属性,它描述了用来描述渐变的大小和方向的单元系统。该属性有两个值:userSpaceOnUseobjectBoundingBox。默认值为 objectBoundingBox,我们目前看到的效果都是在这种系统下的,它大体上定义了对象的渐变大小范围,所以你只要指定从 0 到 1 的坐标值,渐变就会自动的缩放到对象相同大小。userSpaceOnUse 使用绝对单元,所以你必须知道对象的位置,并将渐变放在同样地位置上。上例中的 radialGradient 需要被重写成:

html
<radialGradient
  id="Gradient"
  cx="60"
  cy="60"
  r="50"
  fx="35"
  fy="35"
  gradientUnits="userSpaceOnUse"></radialGradient>

你也可以利用属性 gradientTransform 给渐变添加额外的变化,但是因为我们还没有介绍 transforms,所以我们将在后续的章节中介绍它。

如果对象边界框不是一个正方形,处理 gradientUnits="objectBoundingBox" 还有一些其他警告,但是这些方法特别复杂因此有待一些了解得更深的人来解释他们。

Pattern

参考自:SVG pattern 使用(patternUnits、patternContentUnits)

<pattern>SVG 的一个图案填充标签,在 pattern 中定义好图案,通过 id 引用来对图形进行填充 <pattern>width ,height 属性默认由根据所填充图形的百分比来确定。

pattern 标签另外的两个属性为:

  • patternUnits :默认值为 objectBoundingBox,xywidthheight 的值都是占外框(包裹 pattern 的元素)的百分比。
  • patternContentUnits :默认值为 userSpaceOnUse 一般用来设置 pattern 内图案的单位大小,如下面实例中的 circle、polygon。

Units 的取值范围:

userSpaceOnUse

  • xywidthheight 表示的值都是当前用户坐标系统的值。也就是说,这些值没有缩放,都是绝对值。

objectBoundingBox(默认值):

  • xywidthheight的值都是占外框(包裹 pattern 的元素)的百分比。

例子:

html
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="200">
    <defs>
      <pattern id="p1" x="0" y="0" width="0.2" height="0.2">
        <circle cx="10" cy="10" r="5" fill="red" />
        <polygon points="30 10 60 50 0 60" fill="green" />
      </pattern>
    </defs>
    <rect x="0" y="0" width="300" height="200" fill="url(#p1)" stroke="blue" />
</svg>

文本

text

SVG中使用 <text> 标签添加文本。

xml
<text x="10" y="10">Hello World!</text>

<text> 标签使用 xy 属性确定第一个字符的基线位置。常用的属性还有:

  • font-family 字体设置
  • font-size 字体大小
  • font-weight 字体粗体设置
  • font-style 字体样式
  • text-anchor 对齐方式。start(左对齐),middle(中间对齐),end(右对齐)
  • text-decoration 划线设置。underline(下划线),overline(上划线),line-through(删除线)

例如:

html
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="20">
  <text x="10" y="15" fill="red" text-decoration="underline">SVG从入门到入土</text>
</svg>

tspan

<text> 元素无法对文本进行换行,这时候就需要使用 <tspan> 元素,除了 <text> 元素的属性外,还有以下属性:

  • dx x方向的偏移。
  • dy y方向的偏移。
  • rotate 旋转字符,可设置多值。
  • baseline-shift 设置文本为上下标,值 super 上标,sub 下标。

例如:

html
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="80">
  <text x="10" y="30" fill="red">
    <tspan>SVG从入门到入土</tspan>
    <tspan font-size="12" baseline-shift="super">2</tspan>
    <tspan x="10" y="50" rotate="30">SVG从入门到入土</tspan>
  </text>
</svg>

textPath

<textPath>元素可使文本沿着某条路径排列。

html
<svg xmlns="http://www.w3.org/2000/svg" width="300" height="150">
  <defs>
    <path id="path" d="M 10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80"></path>
  </defs>
  <text>
    <textPath fill="red" xlink:href="#path" startOffset="50%" text-anchor="middle">
      SVG从入门到入土SVG从入门到入土SVG从入门到入土SVG从入门到入土SVG从入门到入土
    </textPath>
  </text>
</svg>

图片

SVG使用 <image> 标签引入图片,其有5个属性,分别是:srcxywidthheight

html
<svg>
  <image src="/logo.svg" x="0" y="0" width="100" height="100"></image>
</svg>

基础变形

CSS 中的 transform 一样,SVG 中的变形也是通过 transform 实现的,只不过 SVG 中的变形不用写到 CSS 中,而是通过 transform 属性直接设置,比如下面的例子。

平移

html
<rect x="10" y="10" width="20" height="20" transform="translate(60,60)"/>

旋转

和CSS中的 transform:rotate() 不同的的是,SVG中的 transform:rotate() 传3个值,第一个是角度必填,第2,3个是旋转中心点坐标,默认是左上角坐标原点。

html
<rect x="10" y="10" width="20" height="20" transform="rotate(45)"/>

缩放

html
<rect x="10" y="10" width="20" height="20" transform="scale(1.5,2)"/>

倾斜

skewX

html
<rect x="20" y="0" width="50" height="50" transform="skewX(30)"/>

skewY

html
<rect x="0" y="20" width="50" height="50" transform="skewY(30)"/>

滤镜效果

在很多情况下,基本的SVG图形并不能满足我们的需求,形状api实现出来图形看起来只是色块的生硬拼接,常见的例如阴影渐变等效果是不能实现的,所以便有了SVG滤镜。

SVG可以使用 <filter> 标签定义滤镜。SVG滤镜可以像PS处理照片那样为SVG添加滤镜效果,让SVG如虎添翼,能够实现出各种各样的效果。

SVG滤镜的种类

SVG中滤镜效果种类是相当丰富的,这里简单介绍一下:

名称描述
feBlend把两个对象组合在一起,使它们受特定的混合模式控制
feColorMatrix基于转换矩阵对颜色进行变换,每一像素的颜色值都经过矩阵计算出新颜色
feComponentTransfer重新定义所有四个颜色通道R、G、B和A
feComposite用于将两个图像相交,接受两个输入,in 和 in2
feConvolveMatrix应用了一个矩阵卷积滤镜效果
feDiffuseLightingSVG 滤波器原始灯使用alpha通道作为凹凸贴图的图像
feDisplacementMap是一个位置替换滤镜,用于改变元素和图形的像素位置
feFlood实用程序过滤器,用于使用颜色和不透明度基本填充过滤器子区域
feGaussianBlur显示模糊效果
felmage从外部来源取得图像数据,并提供像素数据作为输出
feMerge允许同时应用滤镜效果而不是按顺序应用滤镜效果
feMorphology用来腐蚀或扩张输入图像
feOffset显示阴影效果
feSpecularLighting使用alpha通道作为凹凸贴图源图形,生成的图像是基于浅色的 RGBA图像
feTile允许以填补输入图像的重复,平铺图案的目标矩形
feTurbulence利用Perlin噪声函数创建了一个图像
feDistantLight定义了一个距离光源,可以用在灯光滤镜<feDiffuseLighting>元素或<feSpecularLighting>元素的内部
fePointLight定义了一个光源,其允许创建一个点光源的效果
feSpotLight定义了一个光源,其允许创建一个聚光灯效果

SVG滤镜的语法

我们需要将 <filter> 标签定义在 <defs> 标签内。<defs> 是单词 "definitions" 的缩写,可以在其内定义以后需要重复使用的元素, 在 <defs> 元素中定义的元素不会直接呈现,而实需要在别处通过 id 引用他们。我们在 <defs> 中定义 <filter> 标签,并在 <filter> 中编写我们的滤镜。

先看一个简单的例子:

html

<svg width="300" height="150">
  <defs>
    <filter id="blur">
      <feGaussianBlur in="SourceGraphic" stdDeviation="5"/>
    </filter>
  </defs>
  <rect x="100" y="30" width="80" height="80" fill="red" filter="url(#blur)"></rect>
</svg>

我们定义了一个 <feGaussianBlur> 滤镜,它是一个简单的高斯模糊滤镜,它的 stdDeviation 属性定义了模糊程度。 in 属性定义了滤镜应用于的图像,这两个属性初学者见到这几个奇怪的单词一定是摸不着头脑,没关系,对于了解过的这些属性的我来说也是记不住,哈哈哈,所以才写下这篇文章以做记录。言归正传, 这些属性会在稍后介绍。

滤镜的通用属性

每个滤镜都有一些通用属性:

属性说明默认值
x滤镜应用于图像的x坐标0
y滤镜应用于图像的y坐标0
width绘制滤镜容器框的宽度100%
height绘制滤镜容器框的高度100%
in指定滤镜效果的输入源,可以是某个滤镜导出的 result,也可以是下面 6 个值
result用于定义一个滤镜效果的输出名字,以便将其用作另一个滤镜效果的输入

in的取值

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

滤镜的混合使用

参考自:有意思!强大的 SVG 滤镜

SVG是可以将多个滤镜效果混合使用的,我们先来看下面这个例子:

html

<svg width="200" height="200">
  <defs>
    <filter
        id="MyFilter"
        filterUnits="userSpaceOnUse"
        x="0"
        y="0"
        width="200"
        height="200">
      <feGaussianBlur in="SourceGraphic" stdDeviation="4" result="blur"/>
      <feOffset in="blur" dx="5" dy="5" result="offsetBlur"/>
      <feMerge>
        <feMergeNode in="offsetBlur"/>
        <feMergeNode in="SourceGraphic"/>
      </feMerge>
    </filter>
  </defs>
  <image filter="url(#MyFilter)" href="/logo.svg" x="20" y="20" height="150px" width="150px"/>
</svg>

这便是SVG中实现阴影的步骤,过程还是有点复杂,我们分步骤来介绍:

首先,我们定义了一个id 为 MyFilter 的滤镜,它的 filterUnits 属性定义了滤镜应用于的图像,它的 xywidthheight 属性定义了滤镜应用的区域。

接着在 <filter> 标签中定义了两个滤镜,<feGaussianBlur><feOffset><feGaussianBlur>stdDeviation 属性在上面已经讲过,作用为定义模糊程度,<feOffset>dx,dy 属性定义了模糊的偏移量。

剩下两个滤镜共有属性:inresult,是多个滤镜能够组合的关键,<feGaussianBlur>中的 in 属性为 SourceAlpha 值意为接收原图像的 alpha 通道,然后经过滤镜处理,将产生的结果储存在名为 blurresult 中;在 <feOffset> 中,inblur,意为接收上一步模糊处理的结果 blur ,经过偏移处理,将产生的结果储存在名为 offsetBlurresult 中。

可以看出,我们滤镜混合的过程就是将原始图形信息经过处理,产生结果,再将处理结果传递到下一个滤镜中,下一个滤镜将处理的结果传递到下一个滤镜中,以此类推。

上面的代码中我们还使用了 <feMerge> 滤镜,它可以用来对多个滤镜进行叠加。滤镜处理的结果可以用 result 存储输出。在 feMergeNode 元素中访问这些滤镜。feMerge 处理多个 feMergeNode 最后叠加输出。

滤镜详解


feBlend

<feBlend> 为混合模式滤镜,该滤镜接受两个输入源,然后通过一定方式将两个输入源叠加到一起,将结果输出。

属性:

  • in 输入源
  • in2 输入源
  • mode 混合模式

其中 inin2 属性定义的是输入源,mode 属性定义了混合模式,混合模式可选值有5种:

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

5种滤镜效果如下:

html
<svg width="200" height="200">
  <defs>
    <filter id="lighten" x="0" y="0" width="200" height="200">
      <feImage width="200" height="200" xlink:href="https://q.qlogo.cn/g?b=qq&nk=1615685977&s=100" result="img1" />
      <feImage width="200" height="200" xlink:href="/logo.svg" result="img2" />
      <feBlend mode="normal" in="img1" in2="img2"/>
    </filter>
  </defs>
  <rect width="200" height="200" x="0" y="0" fill="none" filter="url(#lighten)" stroke="red"/>
</svg>

feColorMatrix

<feColorMatrix> 滤镜基于转换矩阵对颜色进行变换。每一像素的颜色值 (一个表示为 [红,绿,蓝,透明度] 的矢量) 都经过 矩阵乘法 (matrix multiplated) 计算出的新颜色。

<feColorMatrix>有2个特有属性 typevalues,type 支持 4 种不同的类型:saturate | hueRotate | luminanceToAlpha | matrix

type 类型作用value 取值
saturate转换图像饱和度0.0 - 1.0
hueRotate转换图像色相0 - 360
luminanceToAlpha阿尔法通道亮度只有一个效果,使用亮度转 Alpha 效果将 Alpha 通道设置为图像的亮度并将颜色通道设置为 0
matrix使用矩阵函数进行色彩变换需要应用一个 4 x 5 的矩阵

以下是前三种属性展示的效果:

html
 <svg width="200" height="200">
      <defs>
        <filter id="saturate">
            <feColorMatrix id="saturate" type="saturate" :values="feColorMatrixValue"/>
        </filter>
        <filter id="hueRotate">
            <feColorMatrix id="hueRotate" type="hueRotate" :values="feColorMatrixValue"/>
        </filter>
        <filter id="luminanceToAlpha">
            <feColorMatrix type="luminanceToAlpha" />
        </filter>
      </defs>
        <image filter="`url(#saturate)`" href="https://q.qlogo.cn/g?b=qq&nk=1615685977&s=100" x="25" y="20" height="150px" width="150px"/>
    </svg>

<feColorMatrix> 的 type 为 matrix时,它的 values 需要传入一个 4x5 的矩阵,例如:

html
<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() 值。 计算逻辑为:

text
/* 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

这里不作过多的讲解,有兴趣的同学可以参考一下文章:

feComponentTransfer

<feComponentTransfer>滤镜表示元素分别在每个颜色通道上实现颜色操作,此元素的四个颜色通道是 <feFuncR><feFuncG><feFuncB><feFuncA>,在执行颜色操作时,该元素应仅包含每种类型的一个子元素。

可参考文章:

feComposite

<feComposite> SVG 滤波器基元使用 Porter-Duff 合成操作之一在图像空间中按像素执行两个输入图像的组合: overinatopoutxorlighterarithmetic

可参考文章:

feConvolveMatrix

<feConvolveMatrix> SVG过滤器原语将输入图像中的像素与相邻像素一起更改以生成结果图像。

可参考文章:

光照滤镜

参考自:svg 光照滤镜浅析

光照滤镜有几种: 按照明光线反射方式:

  • <feDiffuseLighting> 漫反射滤镜
  • <feSpecularLighting> 镜面反射滤镜

按光线效果:

  • <fePointLight> 光点源效果滤镜
  • <feSpotLight> 聚光灯效果滤镜
  • <feDistantLight> 远光源效果滤镜
点击查看源代码
html
<h2>feDiffuseLighting</h2>
<svg width="440" height="140" xmlns="http://www.w3.org/2000/svg">
    <!-- the light source is a fePointLight element -->
    <text text-anchor="middle" x="70" y="18">fePointLight</text>
    <filter id="lightMe1">
        <feDiffuseLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <fePointLight x="50" y="60" z="20" />
        </feDiffuseLighting>
    </filter>
    <circle cx="70" cy="80" r="50" fill="green"
            filter="url(#lightMe1)" />
    <!-- the light source is a feDistantLight element -->
    <text text-anchor="middle" x="180" y="18">feDistantLight</text>
    <filter id="lightMe2">
        <feDiffuseLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <feDistantLight azimuth="140" elevation="20"/>
        </feDiffuseLighting>
    </filter>
    <circle cx="180" cy="80" r="50" fill="green"
            filter="url(#lightMe2)" />
    <!-- the light source is a feSpotLight source -->
    <text text-anchor="middle" x="290" y="18">feSpotLight</text>
    <filter id="lightMe3">
        <feDiffuseLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <feSpotLight x="260" y="5" z="30" limitingConeAngle="20"
                         pointsAtX="290" pointsAtY="80" pointsAtZ="0"/>
        </feDiffuseLighting>
    </filter>
    <circle cx="290" cy="80" r="50" fill="green"
            filter="url(#lightMe3)" />
</svg>
<h2>feSpecularLighting</h2>
<svg width="440" height="140" xmlns="http://www.w3.org/2000/svg">
    <!-- the light source is a fePointLight element -->
    <text text-anchor="middle" x="70" y="18">fePointLight</text>
    <filter id="lightMe21">
        <feSpecularLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <fePointLight x="50" y="60" z="20" />
        </feSpecularLighting>
    </filter>
    <circle cx="70" cy="80" r="50" fill="green"
            filter="url(#lightMe21)" />
    <!-- the light source is a feDistantLight element -->
    <text text-anchor="middle" x="180" y="18">feDistantLight</text>
    <filter id="lightMe22">
        <feSpecularLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <feDistantLight azimuth="140" elevation="20"/>
        </feSpecularLighting>
    </filter>
    <circle cx="180" cy="80" r="50" fill="green"
            filter="url(#lightMe22)" />
    <!-- the light source is a feSpotLight source -->
    <text text-anchor="middle" x="290" y="18">feSpotLight</text>
    <filter id="lightMe23">
        <feSpecularLighting in="SourceGraphic" result="light"
                           lighting-color="red">
            <feSpotLight x="260" y="5" z="30" limitingConeAngle="20"
                         pointsAtX="290" pointsAtY="80" pointsAtZ="0"/>
        </feSpecularLighting>
    </filter>
    <circle cx="290" cy="80" r="50" fill="green"
            filter="url(#lightMe23)" />
</svg>

属性:

feDiffuseLighting与feSpecularLighting的属性

  • surfaceScale 是指的引用滤镜的元素(即示例中的circle元素)中透明度为1的几何位置的高度(即向屏幕方向突出的高度),因此下面的示例借助了渐变色。
  • diffuseConstant 是指的是漫反射的强度,值越大,光线越亮,值越小,光线越暗
  • specularConstant 是指的是镜面反射的强度,值越大,光线越暗,值越小,光线越亮
  • specularExponent 是指的是镜面的光洁程度,镜面越光洁那么光线的漫反射就越小

feDistantLight的属性

  • azimuth 指的是光源在xy平面上面上面的位置,这是一个角度值,指的是当前用户坐标系统下面相对于x轴顺时针方向的角度即为光源位置。
  • elevation 指的是光源与xy平面之间的角度

fePointLight

  • x 指的是在光源中心在当前用户坐标系统中的位置
  • y 指的是在光源中心在当前用户坐标系统中的位置
  • z 指的是光源距离xy平面的距离(显然距离越远光点看起来越大,可以回家用手电筒照照墙面试试)

feSpotLight

  • x,y,z 和上面的 fePointLight 的对应属性意义是相同的,都表示光源的坐标位置
  • pointsAtX, pointsAtY, pointsAtZ 指的是光源中心照向界面的坐标位置
  • limitingConeAngle 指的是从光源发射的光线与光照指向的目标点连线之间角度小于该属性指定的值的光线才会存在,超出这个角度的光线会被忽略掉.

feDisplacementMap

<feDisplacementMap> 一个位置替换滤镜,就是改变元素和图形的像素位置的。map含义和ES5中数组的map方法是一样的,遍历原图形的所有像素点,使用feDisplacementMap重新映射替换一个新的位置,形成一个新的图形,公式如下:

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

解释如下:

  • P'(x,y)指的是转换之后的x, y坐标。
  • x + scale * (XC(x,y) - 0.5), y + scale * (YC(x,y) - 0.5)指的是具体的转换规则。
  • XC(x,y)表示当前x,y坐标像素点其X轴方向上设置的对应通道的计算值,范围是0~1。
  • YC(x,y)表示当前x,y坐标像素点其Y轴方向上设置的对应通道的计算值,范围是0~1。
  • -0.5是偏移值,因此XC(x,y) - 0.5范围是-0.5~0.5,YC(x,y) - 0.5范围也是-0.5~0.5
  • scale表示计算后的偏移值相乘的比例,scale越大,则偏移越大。

一句话解释就是,根据设定的通道颜色对原图的x, y坐标进行偏移。

<feDisplacementMap> 的属性如下:

  • xChannelSelector 对应XC(x,y),表示X轴坐标使用的是哪个颜色通道进行位置偏移,可以是R、G、B、A中的任意一个。
  • yChannelSelector 对应YC(x,y),表示Y轴坐标使用的是哪个颜色通道进行位置偏移
  • scale 对应公式里面的缩放比例scale,表示偏移的比例。
  • color-interpolation-filters 表示滤镜对颜色进行计算时候采用的颜色模式类型。分为linearRGB(默认值)和sRGB。
  • in 表示输入的原始图形
  • in2 表示用来映射的图形

可参考文章:

feFlood

该滤镜用flood-color元素定义的颜色和flood-opacity元素定义的不透明度填充了滤镜子区域。 可参考文章:

feGaussianBlur

该滤镜用于定义模糊效果,前面文章已经使用过:#滤镜的混合使用这里不做过多介绍

felmage

<feImage> 滤镜从外部来源取得图像数据,并提供像素数据作为输出(意味着如果外部来源是一个 SVG 图像,这个图像将被栅格化。),通过 xlink:href 属性引用外部图像,例如:

html
<feImage xlink:href="./test.svg" />

feMerge

<feMerge> 滤镜允许同时应用滤镜效果而不是按顺序应用滤镜效果。利用result存储别的滤镜的输出可以实现这一点,然后在一个 <feMergeNode> 子元素中访问它,前面文章已经使用过:#滤镜的混合使用这里不做过多介绍。

feMorphology

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

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

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

示例如下:

html
<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>
<div>
  <p style="font-size: 64px;margin-bottom: 1em">feMorphology</p>
  <p class="dilate" style="filter:url(#dilate);font-size: 64px;margin-bottom: 1em">feMorphology</p>
  <p class="erode" style="filter: url(#erode);font-size: 64px;margin-bottom: 1em">feMorphology</p>
</div>

feOffset

该滤镜允许偏移输入图像,输入图像作为一个整体偏移 dxdy 属性中指定的值。前面文章已经使用过:#滤镜的混合使用这里不做过多介绍。

feTile

该滤镜允许使用输入图像的重复平铺图案填充目标矩形。效果类似于 <pattern> 的效果。

可参考文章:

feTurbulence

该滤镜利用 Perlin 噪声函数创建了一个图像。它实现了人造纹理比如说云纹、大理石纹的合成。

<feTurbulence>有三个属性:

  • type: 实现的滤镜的类型,可选 fractalNoise 分形噪声,或者是 turbulence 湍流噪声。
    • fractalNoise:分形噪声更加的平滑,它产生的噪声质感更接近云雾
    • turbulence:湍流噪声
  • baseFrequency: 表示噪声函数的基本频率的参数,频率越小,产生的图形越大,频率越大,产生的噪声越复杂其图形也越小越精细,如果提供两个数字,则第一数字代表X方向上的基频,第二数值代表Y方向上的基频。如果只提供了一个数字,那么X和Y都使用该值。
  • numOctaves:表示噪声函数的精细度,数值越高,产生的噪声更详细。 默认值为1
  • seed:表示feTurbulence滤镜效果中伪随机数生成的起始值
  • stitchTiles:定义了Perlin噪声在边框处的行为表现。

下面是我结合 <feTurbulence><feDisplacementMap> 实现的SVG波浪效果:

点击查看源码
vue
<script setup>
import {onUnmounted, ref} from "vue";

let process = 0.005
let sign = 1
const baseFrequency = ref('0.005 0.005')

let raf
function animate() {
  process += 0.0005 * sign
  if (sign === 1 && process > 1) {
    sign = -1
  }
  if (sign === -1 && process < 0) {
    sign = 1
  }
  baseFrequency.value = `${process * 0.005 + 0.015} ${process * 0.05 + 0.1}`
  raf = requestAnimationFrame(animate)
}

requestAnimationFrame(() => {
  animate()
})
onUnmounted(() => {
  cancelAnimationFrame(raf)
})
</script>

<template>
  <div id="wave-container">
    <div id="water"></div>
  </div>
  <svg xmlns="http://www.w3.org/2000/svg">
    <filter id="turbulence" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
      <feTurbulence id="feturbulence" type="fractalNoise" numOctaves="3" seed="2"
                    :baseFrequency="baseFrequency"></feTurbulence>
      <feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="20" in="SourceGraphic"></feDisplacementMap>
    </filter>
  </svg>
</template>

<style lang="scss" scoped>
#wave-container,
#water {
  background-image: url("https://6c73-lsj97-9giu4cj4abdc0985-1256331827.tcb.qcloud.la/imgs/2024_01/lake.jpg");
  background-position: center bottom;
  background-repeat: no-repeat;
  background-size: cover;
}

#wave-container {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  width: 100%;
  aspect-ratio: 550/412;
  overflow: hidden;
  box-shadow: 0 4px 20px #4f4f4f;
}

#wave-container #water {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 66%;
  filter: url("#turbulence");
}
@media (min-width: 992px) {
  #wave-container{
    height: 412px;
    width: 550px;
    aspect-ratio: unset;
  }
}
</style>

参考

元素

MDN/SVG/参考/元素

属性

MDN/SVG/参考/属性