SVG 路径(path)_IT和尚的博客-程序员宅基地_svg路径

技术标签: H5画布  

<path>元素是SVG基本形状中最强大的一个,它不仅能创建其他基本形状,还能创建更多其他形状。

另外,path只需要设定很少的点,就可以创建平滑流畅的线条(比如曲线)。虽然polyline元素也能实现类似的效果,但是必须设置大量的点 (点越密集,越接近连续,看起来越平滑流畅),并且这种做法不能够放大(放大后,点的离散更明显)。为了更好的理解path,你最好用path实际画一个 SVG图形,虽然用XML或文本编辑器来编辑path元素不是很容易,但可以帮助我们理解path是如何工作的,所以,你就忍了吧。

上一章提到过,path元素的形状是通过d属性定义的,d属性的值是一个“命令+参数”的序列,我们将学习这些命令,并且看到很多相关的示例。

每一个命令都用一个关键字母来表示,比如,字母“M”表示的是“Move to”命令,当解析器读到这个命令时,它就知道你是打算移动到某个点。跟在命令字母后面的,是你需要移动到的那个点的x和y轴坐标。比如移动到 (10,10)这个点的命令,应该写成“M 10 10”。这一段字符结束后,解析器就会去读下一段命令。每一个命令都有两种表示方式,一种是用大写字母,表示采用绝对定位。另一种是用小写字母,表示采用相对定位(例如:从上一个点开始,向上移动10px,向左移动7px)。

d属性采用的是用户坐标系统,不需标明单位。在后面的教程中,我们会学到如何让变换path,以满足更多需求。

直线命令 Line commands

<path>元素有5个画直线的命令,如名字所示,直线命令就是在两个点之间画直线。首先是“Move to”命令,M,前面已经提到过,它有两个参数,分别是需要移动到的点的x轴和y轴的坐标。假设,你的画笔当前位于一个点,在使用M命令移动画笔后,只会 移动画笔,但不会在两点之间画线。因为M命令仅仅是移动画笔,但不画线。所以M命令经常出现在路径的开始处,用来指明从何处开始画。

M x y

or

m dx dy

这有一个比较好的例子,不过我们没画任何东西,只是将画笔移动到路径的起点,所以我们不会看到任何图案。但是,我把我们移动到的点标注出来了,所以 在下面的例子里会看到(10,10)坐标上有一个点。注意,如果只画path,这里什么都不会显示。(这段不太好理解,说明一下:为了更好地展示路径,下 面的所有例子里,在用path绘制路径的同时,也会用circle标注路径上的点。)

<?xml version="1.0" standalone="no"?>

<svg width="200px" height="200px" version="1.1" xmlns="http://www.w3.org/2000/svg">

  <path d="M10 10"/>

  <!-- Points -->
  <circle cx="10" cy="10" r="2" fill="red"/>

</svg>

能够真正画出线的命令有3个(M命令是移动画笔位置,但是不画线),最常用的是“Line to”命令,LL有两个参数,分别是一个点的x轴和y轴坐标,L命令将会在该点和当前点(L前面画笔所在的点)之间画一条线段。

L x y (or l dx dy)

另外还有两个简写命令,用来绘制平行线和垂直线。H,绘制平行线。V,绘制垂直线。它们都只带一个参数,标明在x轴或y轴移动到的位置,因为它们都只在一个坐标轴的方向上移动。

H x (or h dx)
 V y (or v dy)

现在我们已经掌握了一些命令,可以开始画一些东西了。先从简单的地方开始,画一个简单的矩形(同样的效果用<rect/>元素可以更简单的实现),矩形是由水平线和垂直线组成的,所以这个例子可以很好地展现前面讲的画线的方法。

<?xml version="1.0" standalone="no"?>

<svg width="100px" height="100px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  
  <path d="M10 10 H 90 V 90 H 10 L 10 10"/>

  <!-- Points -->
  <circle cx="10" cy="10" r="2" fill="red"/>
  <circle cx="90" cy="90" r="2" fill="red"/>
  <circle cx="90" cy="10" r="2" fill="red"/>
  <circle cx="10" cy="90" r="2" fill="red"/>

</svg>

最后,我们可以通过一个“闭合路径命令”Z来简化上面的path,Z命令会从当前点画一条直线到路径的起点,尽管我们不总是需要闭合路径,但是它还是经常被放到路径的最后。另外,Z命令不用区分大小写。

Z (or z)

所以上面例子里用到的路径,可以简化成这样:

<path d="M10 10 H 90 V 90 H 10 Z" fill="transparent" stroke="black"/>

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

<path d="M10 10 h 80 v 80 h -80 Z" fill="transparent" stroke="black"/>

上述路径是:画笔移动到(10,10)点,由此开始,向右移动80像素构成一条水平线,然后向下移动80像素,然后向左移动80像素,然后再回到起点。

你可能会问这些命令有什么用,因为 <polygon> 和 <polyline> 可以做到画出一样的图形。答案是,这些命令可以做得更多。如果你只是画直线,那么其他元素可能会更好用,但是,path却是众多开发者在SVG绘制中惯用 的。据我所知,它们之间不存在性能上的优劣。但是通过脚本生成path可能有所不同,因为另外两种方法只需要指明点,而path在这方面的语法会更复杂一 些。

曲线命令 Curve commands

绘制平滑曲线的命令有3个,其中两个用来绘制贝塞尔曲线,另外一个用来绘制弧形或者说是圆的一部分。如果你用过Inkscape, Illustrator 或者 Photoshop,你可能对贝塞尔曲线有一定程度的了解。关于贝塞尔曲线的数学解释,你可以在Wikipedia的文档中获取到。其中的很多知识都可以用在这里。贝塞尔曲线的类型有很多,但是在path元素里,只存在两种:三次贝塞尔曲线C,和二次贝塞尔曲线Q。

贝塞尔曲线

我们从稍微复杂一点的三次贝塞尔曲线入手,三次贝塞尔曲线需要定义一个点和两个控制点,所以用C命令创建三次贝塞尔曲线,需要设置三组坐标参数:

C x1 y1, x2 y2, x y (or c dx1 dy1, dx2 dy2, dx dy)

这里的最后一个坐标(x,y)表示的是曲线的终点,另外两个坐标是控制点,(x1,y1)是起点的控制点,(x2,y2)是终点的控制点。如果你熟 悉代数或者微积分的话,会更容易理解控制点,控制点描述的是曲线起始点的斜率,曲线上各个点的斜率,是从起点斜率到终点斜率的渐变过程。(文字描述不好, 维基百科上有图示,更直观。) 

<?xml version="1.0" standalone="no"?>

<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">

  <path d="M10 10 C 20 20, 40 20, 50 10" stroke="black" fill="transparent"/>
  <path d="M70 10 C 70 20, 120 20, 120 10" stroke="black" fill="transparent"/>
  <path d="M130 10 C 120 20, 180 20, 170 10" stroke="black" fill="transparent"/>
  <path d="M10 60 C 20 80, 40 80, 50 60" stroke="black" fill="transparent"/>
  <path d="M70 60 C 70 80, 110 80, 110 60" stroke="black" fill="transparent"/>
  <path d="M130 60 C 120 80, 180 80, 170 60" stroke="black" fill="transparent"/>
  <path d="M10 110 C 20 140, 40 140, 50 110" stroke="black" fill="transparent"/>
  <path d="M70 110 C 70 140, 110 140, 110 110" stroke="black" fill="transparent"/>
  <path d="M130 110 C 120 140, 180 140, 170 110" stroke="black" fill="transparent"/>

</svg>

上面的例子里,创建了9个三次贝塞尔曲线。有一点比较遗憾,标记控制点的代码会比较庞大,所以在这里舍弃了。(之前所有点都用circle标记,此 处一样,只不过没把代码列出来)。如果你想更准确地控制它们,可以自己动手把他们画出来。图例上的曲线从左开始,控制点在水平方向上逐渐分开,最右侧的曲 线,控制点之间离得更远。这里要注意观察,曲线沿着起点到第一控制点的方向伸出,逐渐弯曲,然后沿着第二控制点到终点的方向结束。(这个翻译的不太好,没 弄清原文想要表达的意思。曲线的原理,还是看维基百科比较好。)

你可以将若干个贝塞尔曲线连起来,从而创建出一条很长的平滑曲线。如果将一个点的控制点在它的另一侧建立一个对称点(斜率不变),可以通过一个简写 的命令来实现,这个命令是S(或s)。(这段话可以这样理解:S命令前面可以是一条C命令创建的三次贝赛尔曲线,这时候,S命令跟在C命令的后面,就可以 用比较简单的参数,生成一个与前面那个相对称的三次贝塞尔曲线。仔细看一下后面的图例,可以帮助理解。另外,S命令也可以跟在S命令后面。)

S x2 y2, x y (or s dx2 dy2, dx dy)

S命令可以用来创建与之前那些曲线一样的贝塞尔曲线,但是,如果S命令跟在一个C命令或者另一个S命令的后面,它的第一个控制点,就会被假设成前一 个控制点的对称点。如果S命令单独使用,前面没有C命令或者另一个S命令,那么它的两个控制点就会被假设为同一个点。下面是S命令的语法示例,如图所示, 左侧的控制点用红色标示,与它对称的控制点用蓝色标示。

<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 C 40 10, 65 10, 95 80 S 150 150, 180 80" stroke="black" fill="transparent"/>
</svg>

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

Q x1 y1, x y (or q dx1 dy1, dx dy)

<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg"">
  <path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

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

T x y (or t dx dy)

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

<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 80 Q 52.5 10, 95 80 T 180 80" stroke="black" fill="transparent"/>
</svg>

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

弧形Arcs

弧形命令A是另一个创建SVG曲线的命令。基本上,弧形可以视为圆形或椭圆形的一部分。假设,已知椭圆形的长轴半径和短轴半径,另外已知两个点(它 们的距离在圆的半径范围内),这时我们会发现,有两个路径可以连接这两个点。每种情况都可以生成出四种弧形。所以,为了保证创建的弧形唯一,A命令需要用 到比较多的参数:

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

弧形命令A的前两个参数分别是x轴半径和y轴半径,它们的作用很明显,不用多做解释,如果你不是很清楚它们的作用,可以参考一下椭圆ellipse命令中的相同参数。弧形命令A的第三个参数表示弧形的旋转情况,下面的例子可以很好地解释它:

<?xml version="1.0" standalone="no"?>
<svg width="320px" height="320px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M10 315
           L 110 215
           A 30 50 0 0 1 162.55 162.45
           L 172.55 152.45
           A 30 50 -45 0 1 215.1 109.9
           L 315 10" stroke="black" fill="green" stroke-width="2" fill-opacity="0.5"/>
</svg>

如图例所示,画布上有一条对角线,中间有两个椭圆弧被对角线切开(x radius = 30, y radius = 50)。第一个椭圆弧的x-axis-rotation(x轴旋转角度)是0,所以弧形所在的椭圆是正置的(没有倾斜)。在第二个椭圆弧中,x- axis-rotation设置为-45,所以这是一个旋转的椭圆,并以短轴为分割线,形成了两个对称的弧形。参看图示中的第二个椭圆形。

上面提到的四种不同路径将由接下来的两个参数决定。如前所讲,还有两种可能的椭圆用来形成路径,它们给出的四种可能的路径中,有两种不同的路径。这 里要讲的参数是large-arc-flag(角度大小) 和sweep-flag(弧线方向),large-arc-flag决定弧线是大于还是小于180度,0表示小角度弧,1表示大角度弧。sweep- flag表示弧线的方向,0表示从起点到终点沿逆时针画弧,1表示从起点到终点沿顺时针画弧。下面的例子展示了这四种情况。

<?xml version="1.0" standalone="no"?>
<svg width="325px" height="325px" version="1.1" xmlns="http://www.w3.org/2000/svg">
  <path d="M80 80
           A 45 45, 0, 0, 0, 125 125
           L 125 80 Z" fill="green"/>
  <path d="M230 80
           A 45 45, 0, 1, 0, 275 125
           L 275 80 Z" fill="red"/>
  <path d="M80 230
           A 45 45, 0, 0, 1, 125 275
           L 125 230 Z" fill="purple"/>
  <path d="M230 230
           A 45 45, 0, 1, 1, 275 275
           L 275 230 Z" fill="blue"/>
</svg>

你应该已经猜到了,最后两个参数是指定弧形的终点,弧形可以简单地创建圆形或椭圆形图标,比如你可以创建若干片弧形,组成一个饼图

如果你是从Canvas过 渡到SVG,那么弧形会比较难以掌握,但它也是非常强大的。用路径来绘制完整的圆或者椭圆是比较困难的,因为圆上的任意点都可以是起点同时也是终点,无数 种方案可以选择,真正的路径无法定义。通过绘制连续的路径段落,也可以达到近似的效果,但使用真正的circle或者ellipse元素会更容易一些。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013040887/article/details/83107658

智能推荐

在linux系统中如何使用命令行获取本机的公网IP_codemanship的博客-程序员宅基地

最近本地配置了服务器,使用了家用的带宽没有固定的IP地址。因此,需要获取到自己的公网IP地址。机器是CentOS 6.5的服务器版本自然就没有办法通过浏览器访问的方式获取到公网IP地址。因此记录一些从命令行获取公网IP的方法。首先,需要在机器上按照curl:sudo yum install curl其次,使用下列命令中得一个来获取本地地址:curl http://members

[Cherno C++ 笔记 P11~P20]判断,循环,指针,引用,类_人工智能护发素的博客-程序员宅基地

[Cherno C++ 笔记 P11~P20]判断,循环,指针,引用,类前言视频链接P11 如何在Visual Studio中调试代码P12 C++条件与分支(if语句)if else汇编中的if elseP13 Visual Studio的最佳设置P14 C++循环(for 、while)for,while,do while汇编中的循环P15 C++控制流语句(continue, break, return)汇编中的break,continue,returnP16 C++指针什么是指针P17 C++引用P

Ionic 组件 tabs 控件学习_咱丶一根草的博客-程序员宅基地_ionic tabs

直接上代码,然后说明。<ion-tabs id="mainTab" selectedIndex="2"> <ion-tab [root]="tab1Root" tabTitle="诊断指标" tabIcon="information-circle"></ion-tab> <ion-tab [root]="tab2Root" tabTitle="诊断分析" tabIcon="informat

Ubuntu 1804 安装Opencv Dlib zbar_weixin_35867652的博客-程序员宅基地

把源代码一字排开放到同一个目录确保存在dlib/build和opencv-4.1.0/buildgedit setup.shecho 将安装opencv dlib zbar glutsudo cp ./sources.list /etc/apt/sources.listsudo apt updatesudo apt upgradesudo apt install gcc g++ c...

Python关于%matplotlib inline 报错解决办法_毕加索来喽的博客-程序员宅基地_%matplotlib inline报错

Python关于%matplotlib inline报错解决办法https://www.jianshu.com/p/2dda5bb8ce7d

linux下ssh框架搭建教程,手动搭建SSH框架_丁道骏的博客-程序员宅基地

之前的一篇文章说明了如何用myeclipse提供的方法搭建ssh框架(见 http://www.linuxidc.com/Linux/2012-07/64859.htm),本文介绍如何手动编码搭建此框架。我的环境为jdk1.7.0,Java EE 6 Libraries,myeclipse 10(其他版本也可)。1.新建web project。工程名为SSH,将index.jsp删掉(没有用到)...

随便推点

1069:乘方计算_胖虎很忙的博客-程序员宅基地_1069:乘方计算

1069:乘方计算时间限制: 1000 ms 内存限制: 65536 KB【题目描述】给出一个整数a和一个正整数n(-1000000≤a≤1000000,1≤n≤10000),求乘方an,即乘方结果。最终结果的绝对值不超过1000000。【输入】一行,包含两个整数a和n。-1000000≤a≤1000000,1≤n≤10000。【输出】一个整数,即乘方结果。题目保证最终结果的绝对值不超过1000000。【输入样例】2 3【输出样例】8说明:主要考查乘方问题在循环中的应用。

oracle学习01_zouwenhuan的博客-程序员宅基地

1、oracle常用的数据类型有 varchar2(100)、date、number。疑问:也有varchar类型,和varchar2的区别是什么?解:2、Oracle只用NUMBER(m,n)就可以表示任何复杂的数字数据。number可以存储浮点数,也可以存储整数;number(8,1)存储小数位为1位,总长度为8的浮点数,如果小数位数不足,则用0补全;number(8)存储总长度为8的整数;这里有详细参考Unicode与UTF-8的关系:...

[转]android 条码识别软件开发全解析(续2详解绝杀!)_weixin_34237596的博客-程序员宅基地

本文转自:http://www.cnblogs.com/tankaixiong/archive/2010/10/31/1865807.html关于andriod上实现条码识别,我前面已经讲了一些简单的应用,上篇讲过的应用zxing时必需多安装其组件这是不能接受的,我想大家都已经想到解决的方法了吧,呵呵,其实正真学习一个东西,从其最基本的东东开始--大家都知道就是源码了。OK,我们要深入条码的识...

android音乐播放器!关于Android性能优化的几点建议,终获offer_NathanEmily的博客-程序员宅基地

开头通常作为一个Android APP开发者,我们并不关心Android的源代码实现,不过随着Android开发者越来越多,企业在筛选Android程序员时越来越看中一个程序员对于Android底层的理解和思考,这里的底层主要就是Android Framewok中各个组件的运行原理,例如Binder的运行机制、ServiceManager的作用等等。在Android面试中,关于 Framework 的问题是必备的,但是这些关于字节跳动 Framework 的高频面试题你都掌握了吗?所以,今天,我将献上

Android 中使用MediaRecorder进行录像详解(视频录制)_weixin_30319153的博客-程序员宅基地

在这里给出自己的一个测试DEMO,里面注释很详细。简单的视频录制功能.package com.video;import java.io.IOException;import android.app.Activity;import android.content.pm.ActivityInfo;import android.graphics.PixelFormat;...

推荐文章

热门文章

相关标签