深入理解SVG之Path

6,786 阅读8分钟

这是我参与更文挑战的第2天,活动详情查看: 更文挑战

前言

说来话长,一直想复习下svg的知识,一直找不到合适的理由。因为,前端的知识很多,如果学了不去用就会忘记。而且有一个很重要的点,就是你的理解不深。 工作中遇到问题也不不知道如何却解决。因为最近有个产品的需求打算去做3d文字,当时一听到这个就有点蒙,不过问题不大, 经过和组内讨论,打算通过svg做一层数据的转换,大体思路就是,通过输入的文字, 将font-to-svg,  拿到svg 之后,svg有path, 就可以转换出3维下对应的点。 然后可以形成区域, 然后通过拉伸,就可以形成一个立体的文字了。OK了解了上下文之后, 开始我们的复习之旅。

svg 

我们为什么要用svg?,难道仅仅是因为他帅? 是一门技术? 作为程序员的我们一定要知道为什么? 我什么场景可以用它。我们先看下官方的定义是啥?

可缩放矢量图形Scalable Vector Graphics,SVG),是一种用于描述二维的矢量图形,基于 XML 的标记语言。作为一个基于文本的开放网络标准,SVG能够优雅而简洁地渲染不同大小的图形,并和CSSDOMJavaScriptSMIL等其他网络标准无缝衔接。本质上,SVG 相对于图像,就好比 HTML 相对于文本。

总结我觉得比较重要的特点:

  • SVG 使用 XML 格式定义图形

  • SVG 图像在放大或改变尺寸的情况下其图形质量不会有所损失 

基本元素 :

  1. <svg> 包裹并定义整个矢量图。<svg> 标签之于矢量图就如同 <html> 标签之于一个 web 页面。
  2. 直线类型:  <line> 创建一条直线   <polyline>用于创建多边形   
  3. 图形类型:  <rect> 矩形 <circle><ellipse> 椭圆 <polygon> 多边形 
  4. 比较复杂的图形可以通过path 去定义svg 的路径

下面我就带你一步一步实操去画演示画一个图形, 我们先画一个圆形

<svg width='140' heiight='170' xmlns='http://wwww.w3.org/2000/svg'>    <title>Cat</title>    <desc>Stick Figure of Cat</desc>    <!-- 在这里绘制图像 -->    <circle cx='70' cy='95' r='50' stroke= 'black' fill='none'> </circle></svg>

cx, cy 代表的是圆心坐标 , r代表半径, stroke 表示描边颜色,也就是外轮廓, fill 表示圆的填充色。

我改变svg的描边和填充颜色

  <circle cx='70' cy='95' r='50' stroke= 'red' fill='blue'> </circle>

其他图形也类似,读者如果感兴趣话可以自己下面去调试一下,不在本文的重点考虑范围内,本文主要是对path 这个属性的深度探讨。试想一下,如果要你画出一个半圆,这个还算是规则的,如果不是规则的曲线,你去怎么实现呢? 这时候就去引入贝塞尔曲线

贝塞尔曲线

Bézier curve(贝塞尔曲线)是应用于二维图形应用程序的数学曲线。 曲线定义:起始点、终止点(也称锚点)、控制点。通过调整控制点,贝塞尔曲线的形状会发生变化。

线性贝塞尔曲线

从图上可以看到 P0 P1  t 的范围是[0,1],  在P0 和P1两个点的线段之间的所有的点都满足这个变化。

数学公式

由于这个是线性变化,所以满足我们的数学公式y= kx + b, 其中k 代表的是斜率,因为P1和P0都是向量, 所以向量的相减的数学意义其实是两个点之间的斜率。 

二阶贝塞尔曲线

数学公式:

首先我们要明白一个道理,n阶的贝塞尔曲线对应的是n-1个控制点,二阶贝塞尔曲线是怎么画出这么平滑的曲线的呢? 是这样的我在 P0P1线段上找一点A 然后以相同比例在P1P2找一个B,我连接AB这条直线,我再去直线上找一个点C同样以相同比例。这样不断重复上面的过程,就是图中这个动画这个结果。 我在去画图用公式给大家推导一下, 学知识一定要知道为什么。

其实就是不断降为降阶的问题。

三阶贝塞尔曲线

三阶贝塞尔曲线对应的是两个控制点,也是不断地去找比例相同的点,然后不断降低阶级,知道最后方程 只有4个参数。

通用公式如下图:

复习完贝塞尔曲线这样我们就可以开心地画曲线了。

Path 与曲线

path元素的形状是通过属性`[d](https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/d)`定义的,属性d的值是一个“命令+参数”的序列,我们将讲解这些可用的命令,并且展示一些示例。

M x y   

M 是Move to 的简写,简单点讲就是移动画笔到路径的起点位置 后面的 x,y 就是起点的坐标,并不会画出图形。

L x y 

L 就是Line to 就是画出一条直线。 L命令将会在当前位置和新位置(L前面画笔所在的点)之间画一条线段。

H x
V y

H,绘制水平线。V,绘制垂直线。这两个命令都只带一个参数,标明在x轴或y轴移动到的位置,因为它们都只在坐标轴的一个方向上移动。H 20 表示前面画笔所在的点的 y 不变, x的位置加 20这两个点之间画一条直线。 V 20 就是 x 不变, y增加20。

Z or z

Z 就是路径闭合。命令会从当前点画一条直线到路径的起点。

接下我们就用path 实操一下 画一个正方形。

对应的path 代码如下:

 <path d='M 10 10  L 10 30  L 30 30 L 30 10 Z' fill='red' stroke='black'></path>

 <path d='M 60 60  V 80  H 80 V 60 Z' fill='red' stroke='black'></path>

你也可以使用这些命令的相对坐标形式来绘制相同的图形,如之前所述,相对命令使用的是小写字母,它们的参数不是指定一个明确的坐标,而是表示相对于它前面的点需要移动多少距离。 相对位置对应的path 如下:

<path d='M 100 100  v 80  h 80 v 80 z' fill='red' stroke='black'></path>

接下来我们就进入曲线的一些属性了。

Q x1 y1, x y

Q表示二阶贝塞尔曲线  x1 y1, 就是二阶贝塞尔曲线的控制点, x, y是曲线的终点,曲线的起点是由画笔的上一个点构成, 这就形成了二阶贝塞尔曲线。

代码如下:

<path d="M100 100 Q 25 10 180 80" stroke="black" fill="transparent"/>

这时候有人就要问了? 如果我想画连续的二阶贝塞尔曲线呢? 我该怎么去做呢这就要引入我们的参数T 了 

T x y
  • T  的这个属性 必须跟在 Q 的后面 如果单独使用 直接画多个T 就是一条直线
  • T x y 表示下一段贝塞尔曲线的终点。他会自动根据前一个控制点, 去推断。

有了这个参数 连续的波浪线了

代码如下:

    <path d="M10 80 Q 52.5 10, 95 80 T 180 80 T 280 80 T 380 80 T 480 80" stroke="black" fill="transparent"/>

看完二阶贝塞尔曲线,我们来看下三阶贝塞尔曲线

 C x1 y1, x2 y2, x y

三阶贝塞尔曲线 用C表示 x1 y1 , x2, y2  表示两个端点 x,y表示贝赛尔曲线的终点, 同样的我如果想要画出连续的曲线呢, 这时候就需要用到 S这个参数了

 S x2 y2, x y

S x2, y2 表示第二个控制点 。x,y 表示 终点**。那么它的第一个控制点会被假设成前一个命令曲线的第二个控制点的中心对称点。**

弧线

svg 为了方便我们画椭圆弧,也提供了A 参数。

 A rx ry x-axis-rotation large-arc-flag sweep-flag x y

椭圆弧顾名思义有长轴和短轴, OK rx, ry 就表示长轴半径 和短轴半径  x-axis-rotation 顾名思义就是x轴旋转角度, 这个怎么去理解呢。

上面两张图我代码的改动其实就是角度变化。代码如下

<path d ='    M 10 315    L 110 215    A 30 50 -45 0 1 162.55 162.45    L 172.55 152.45'stroke='black'fill='green' > </path>

large-arc-flag决定弧线是大于还是小于180度,0表示小角度弧,1表示大角度弧。sweep-flag表示弧线的方向,0表示从起点到终点沿逆时针画弧,1表示从起点到终点沿顺时针画弧。下面的例子展示了这种情况:

代码如下:

<path d="M80 80 A 45 45, 0, 0, 0, 125 125" fill="green"/><path d="M230 80 A 45 45, 0, 1, 0, 275 125" fill="red"/>

这里我将椭圆的长轴半径和短轴半径相等, 这时候画出来的其实是一个圆弧。两个唯一的区别就是一个优弧一个劣弧。 并且是同一方向的。 如果需要改变方向就用到sweep-flag, 将它设置为1。然后我在调整下位位置将他们合二为一。

代码如下:

<path d="M230 80 A 45 45, 0, 0, 1, 275 125" fill="green"/><path d="M230 80 A 45 45, 0, 1, 0, 275 125" fill="red"/>

A 最后一个参数表示弧线的终点。 

到此Svg关于path 所有的知识应该全部覆盖到了。 其实还有SVG中还有组的概念,标签

大家感兴趣的可以自行去了解。

本篇文章测试例子都在我的github 这个仓库下, 欢迎star。写作不易,主要是画图不易,看完对你有帮助的点个👍再走吧!