SVG

学习 svg - 进阶篇

Posted by franki on June 18, 2024

svg 进阶

填充和边框

svg 可以使用多种方法来着色,指定对象的属性使用内联 css 样式、内嵌 css 样式,或者使用外部 css 样式文件

fill 和 stroke 属性

fill 属性设置对象内部的颜色

stroke 属性则会设置对象的线条颜色

可以使用 HTML 中的 css 颜色命名方案,如颜色名 red、十六进制值、rgba 值等等

 <rect x="10" y="10" width="100" height="100" stroke="blue" fill="purple" fill-opacity="0.5" stroke-opacity="0.8"/>

除此之外,你还可以分别定义填充色和边框色的不透明度,属性 fill-opacity 控制填充色的透明度,属性 stroke-opacity 控制边框线条的透明度

描边

控制绘制描边的方式

alt text

<?xml version="1.0" standalone="no"?>
<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-linecap 控制边框终点的形状

  • butt 直角边结束线段
  • square 效果跟 butt 差不多,不过会稍微超出实际路径的范围,超过的大小由 stroke-width 来控制
  • round 圆角结束线段,圆角的半径也是由 stroke-width 来控制

stroke-linejoin 控制两条描边之间,用什么方式连接

alt text

<?xml version="1.0" standalone="no"?>
<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>

  • miter 方形画笔在连接处形成尖角
  • round 表示用圆角连接
  • bevel 表示连接处形成一个斜接

stroke-dasharray 将虚线类型应用在描边上

alt text

<?xml version="1.0" standalone="no"?>
<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>

参数是一组用逗号分隔的数字形成的数列。每一组的数字,第一个数字代表的是填色区域长度,第二个数字代表的是非填色区域的长度(可以理解为间距),如果一组数字中的个数是单数的话,会先循环两次变成偶数个,然后继续绘制

使用 css

可以使用 css 来样式化填充和描边。语法跟 html 里面使用 css 一样,只不过需要把 backgound、border 改成 fill 和 stroke。

注意,不是所有的属性都可以用 css 来设置。像 width、height,以及路径的命令等等,都不能用 css 设置

利用 css 的 style 属性插入到元素的行间

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

或者,可以移入到一个特殊的样式部分,包含在一个叫做 <defs> 的区域

<defs> 表示定义,可以定义一些不会在 svg 图形中出现,但是可以被其他元素引用的元素

<?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>

同样地,你可以定义一个外部的样式表

<?xml version="1.0" standalone="no"?>
<?xml-stylesheet type="text/css" href="style.css"?>

<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <rect height="10" width="10" id="MyRect"/>
</svg>

渐变

并非只能简单地填充和描边,还可以创建并在填充和描边上应用渐变色

有两种渐变,一种是线性渐变,一种是径向渐变,必须给渐变内容指定一个 id 属性,否则文档内的其他元素就不能引用它

线性渐变

在 defs 元素内部定义一个 <linearGradient> 元素

<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>

alt text

线性渐变内部有几个 <stop> 结点,这些结点通过指定位置的 offset 属性和 stop-color 属性来说明渐变在特定位置上应该是什么颜色;offset 偏移量始终应该以 0 (0%) 开始 到 1 (100%)结束,stop-opacity 可以设置某个位置的透明度

使用渐变,可以在一个对象属性的 fill 或者属性 stroke 中引用它,与 css 使用 url 应用元素的方法一致

fill: url(#gradient1);

渐变的方向可以使用两个点来控制,分别是 x1、y1、x2、y2

下面是创建一个垂直渐变的例子

<linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1"></linearGradient>

径向渐变

使用 <radioGradient> 元素来创建径向渐变

<?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>

alt text

对象中间是红色的,且向着边缘渐渐变成蓝色。跟线性渐变一样,径向渐变也是通过两个点来确认边缘位置,cx cy 为第一个点,定义了渐变结束围绕的圆环,第二个点称为焦点,由 fx 和 fy 属性定义

spreadMethod 属性,控制当渐变到达终点的行为

  • pad 最终的偏移颜色将用于填充对象剩下的空间
  • reflect 会让渐变一直持续下去,是反向渐变
  • repeat 跳回最初的颜色继续渐变

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

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

patterns

图案,功能非常强大,作用是 svg 填充

<patterns> 同样要放到 defs 内部

<?xml version="1.0" standalone="no"?>
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg" version="1.1">
  <defs>
    <linearGradient id="Gradient1">
      <stop offset="5%" stop-color="white" />
      <stop offset="95%" stop-color="blue" />
    </linearGradient>
    <linearGradient id="Gradient2" x1="0" x2="0" y1="0" y2="1">
      <stop offset="5%" stop-color="red" />
      <stop offset="95%" stop-color="orange" />
    </linearGradient>

    <pattern id="Pattern" x="0" y="0" width=".25" height=".25">
      <rect x="0" y="0" width="50" height="50" fill="skyblue" />
      <rect x="0" y="0" width="25" height="25" fill="url(#Gradient2)" />
      <circle
        cx="25"
        cy="25"
        r="20"
        fill="url(#Gradient1)"
        fill-opacity="0.5" />
    </pattern>
  </defs>

  <rect
    fill="url(#Pattern)"
    stroke="black"
    x="0"
    y="0"
    width="200"
    height="200" />
</svg>

alt text

在 pattern 元素内部,你可以定义任何之前包含过的其他基本形状,并且每个形状都可以使用之前学习过的任何样式样式化,包括渐变和半透明

关于 pattern 容易混淆的事是,pattern 定义了一个单元系统以及他们的大小。上例中,我们在 pattern 元素上定义了width和height属性,用于描述在重复下一个图案之前应该跨过多远。如果你想要在绘制时偏移矩形的开始点,也可以使用 x 和 y 属性,原因如下。

就像前面使用了gradientUnits属性,同样的pattern也有一个属性patternUnits用于描述我们使用的属性单元。这同之前使用的objectBoundingBox默认值一样,所以当一个值为 1 时,它被缩放到应用 pattern 对象的宽高值。因此,我们希望 pattern 垂直和水平的重复 4 次,所以宽高被设置位 0.25,这一位置 pattern 的宽高仅为总外框大小的 0.25。

与渐变不同,pattern 有第二个属性patternContentUnits,它描述了pattern元素基于基本形状使用的单元系统,这个属性默认值为userSpaceOnUse,与patternUnits属性相反,这意味着除非你至少指定其中一个属性值(patternContentUnits或patternUnits),否则在pattern中绘制的形状将与pattern元素使用的坐标系不同,当你手写这部分时会容易混淆。为了使上例生效,我们必须考虑我们的边框(200 像素)大小和我们实际希望pattern垂直和水平重复 4 次的需求。这意味着每个 pattern 单元应该是 50x50的方形,pattern 中的两个矩形和圆形的大小会被缩放适应到一个 50x50 的边框里,任何我们绘制在边框外的内容都不会显示。因为我们希望 pattern 从边框的左上角里开始,所以 pattern 也必须偏移 10 像素,也就是 pattern 的 x 和 y 属性需要调整为 10/200=0.05。