欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

前端SVG基础与深入01

程序员文章站 2022-03-02 10:20:30
...

引言

欢迎来到svg教程,在这里我期望用四周时间讲清楚,关于svg,canvas,像素屏幕适配以及其他与前端图形图像展示相关的内容。您可以通过联系我或者在评论区进行讨论来参与到此文章的编写之中。

图形图像入门

1. 在前端中 我们通常说的像素是什么

一个页面是由显示器上的多个晶体管同时渲染获得的,一个像素就是一个图像展示的最小的一个小方格。
在支持css2的图形系统中, 每英寸为96像素,1英寸2.54厘米

2. 为什么使用svg,栅格化图片与矢量图的区别

栅格图形系统: 图片在其中被表示为图片元素或者像素得长方形数组。大多数现代显示设备也是栅格设备。
栅格:画布上得绘画
矢量图形系统: 图像被描述为一系列几何形状,通过指定坐标得坐标集来绘制形状而不是计算好得像素数组。
矢量:可伸缩材料构成得直线和形状。可用于计算机辅助绘图,高分辨率打印图像,打印和成像语言,SVG可以进行嵌套。

SVG入门

1. 坐标系统

1.1. 视图

使用width、height定义的svg在dom中的视图大小,使用viewbox定义在svg视图中的坐标系统中的方位和大小。
viewbox 0 0 xwidth ywidth。x,y>0,则认为图像在视图中被缩小是放大视图缩小图像。x,y<0,则认为图像在视图中是放大是缩小视图放大图像。 x,y分别代表视图的宽高比。svg中不带单位默认为px像素。

1.2 viewport与viewbox(视图盒子)

张鑫旭详解viewport与viewbox
坐标系统是直角坐标系,左上角为0,0点

2. 基本形状

  1. 线
 <!-- 画一条从(0,0)到(10,10)的黑色直线 线宽10px 同时关闭抗锯齿(默认开启) -->
 <!-- stroke-dasharray: x,y,x1,y1......  x代表几个像素的虚线,y代表几个像素的空隙  总是为偶数个,如果为奇数个则svg本身会进行复制 -->
 <line x1="0" x2="0" y1="10" y2="10" style="stroke: black;shape-rendering: crispEdges;stroke-width:10; stroke-dasharray: 10,5;"></line>
 <!-- 线帽(设置一根横线两头的样式)stroke-linecap:butt(精确对齐)/round(圆角超出)/square(平角超出) -->
 <!-- 线帽(设置两根横线连接点的样式)stroke-linejoin:miter(尖的)/round(圆的)/bevel(平的) -->
  1. 矩形
 <!-- rect中的stroke指的是rect的边框样式,其效果与line中的效果相同 -->
 <rect x="0" y="0" width="20" height="20">
 <!-- 圆角矩形 rx,ry是可选项 设置了矩形每个角的半径值 -->
 <rect x="0" y="0" width="20" height="20" rx="10" ry="10"></rect>
  1. 圆&椭圆
<!-- cx,cy是圆心坐标 r代表圆的半径 -->
<circle cx="0" cy="0" r="10">
<!-- rx代表x轴的半径 y代表y轴的半径 -->
<ellipse cx="0" cy="0" rx="10" ry="20"></ellipse>
  1. 多边形
<!-- points代表一组构成图形的点的合集 polygon中的直线会自动闭合 -->
<polygon points="15,10 55,10 45,20 5,20"></polygon>
<!-- or -->
<polygon points="15 10, 55 10, 45 20, 5 20"></polygon>
<!-- 填充规则定义 fill-rules:nonzero/evenodd 定义多边形中相交线的内容是在图形内还是图形外 -->
  1. 折线
<!-- 如果不设定 fill:none 就会让阅读器去尝试填充形状,与polygon写法相同 所以使用polyline可以构建闭合和直线两种图像 直线不自动闭合-->
<polyline points=""></polyline>

3. 结构与表现

开发推崇结构与表现互相分离。
svg使用样式的三种方式 内联样式,内部样式表,外部样式表。
表现属性:SVG中的样式,可以变为元素属性的方式,进行使用,这些属性被称为表现属性。表现属性等级比其他三种样式方式要低,但是高于继承属性
**使用g,symbol分组得时候use 中的属性不能覆盖defs下的g中的已有属性 **

4. 分组和引用对象

  1. 分组元素
<!-- g标签上声明 一部分图形作为一个分组 可以应用样式 -->
<g id="groupname">
	<desc>description</desc>
	... other svgtag
<g>
  1. 使用分组元素
<!-- use用来直接使用一个分组中的样式 use是以引用元素的位置作为(0,0)点,并以此位置作为复用位置的基础 -->
<use href="#groupname" x="0" y="0"></use>
  1. 独特的元素–>定义在其中的元素在SVG中是只定义不进行展示 包括元素进入流式环境
<defs>svg element</defs>
  1. 组合元素–>永远不会进行展示 可以单独指定viewbox和preserAspectRatio
<symbol>svg element</symbol>
  1. 图像元素 可以设置pAR
<image href="imgpath" x="0" y="0" widht="w" height="h"/>

5. 坐标变换系统

svg中的变换其实是 对于整个坐标系统产生的变化

  1. transform 将坐标的原点按照transform的属性进行平移,在坐标平移之后的新坐标上进行绘画
  2. scale 保持远点不变 将已有的所有坐标点按照坐标轴倍数进行相乘 也就是原本以100为终点的对应轴,变成以100的指定倍数为终点的对应轴
transform = "translate(-centerX*(factor-1), -centerY*(factor-1), scale(factor))"
  1. rotate 按坐标原点进行旋转rotate(numdeg) 按照指定原点进行旋转rotate(numdeg,x,y)
  2. skewX和skewY 将x,y轴按照顺时针方向倾斜 指定的角度

6. 使用path构建图像路径

path d属性中的属性值代表了一个路径的构建 其中大写M代表moveTo,大写L代表lineTo,小写m和小写l分别是相对与上一个点的相对位置节点 path路劲可以说是 svg元素实现的快捷方式

// 绝对坐标点
path(d="M 10 10 L 20 10 L 20 30 M 40 40 L 55 35")
// 相对坐标点
path(d="M 10 10 l 10 0 l 0 20 m 20 10 l 15 -5")
// 两个路径描述的图像相同

水平以及垂直线快捷命令

项目 等价效果
H 20 以当前点画一条到 x为20 y值不变的水平线,多个坐标取最大值
h 20 以当前点画一条x增加20个坐标 y值不变的水平线,多个坐标取坐标相加的值
V 20 以当前点画一条y为20 x值不变的垂直线
v 20 以当前点画一条y值增加20 x值不变的垂直线

缩写规则

// 绝对坐标 开头为大写的M 则其后所有lineTo缩写都是绝对坐标
path(d="M 30 30 L 55 5 L 80 30 L 55 55 Z")
// 缩写后
path(d="M 30 30 55 5 80 30 55 55 Z")
// 相对坐标 开头为小写的m 则其后所有lineTo缩写都是相对坐标
path(d="m 30 30 l 25 -25 l 25 25 l -25 25 Z")
// 缩写后
path(d="m 30 30 25 -25 25 25 -25 25 Z")

可以取消 字母和命令之间的空白 以及正数和负数之间的空白

// 绝对极限压缩
path(d="M30 30 55 5 80 30 55 55Z")
// 相对极限压缩
path(d="m30 30 25-25 25 25-25 25 z")
// 矩形压缩示例
path(d="M12 24h15v25h-15z")

绘制圆弧
圆弧以圆弧起点到这个圆弧终点之间的垂直水平线相较构成的点 以相交点为圆心画椭圆 取顺时针或逆时针以及大于或小于180o 的圆弧

path(d="Mstartx startyAradiusx radiusy skewx is180 clockwise endx endy")

通过二次贝塞尔曲线绘制曲线
二次贝塞尔曲线通过三个点进行曲线的构建 起始点,控制点以及终点 控制点是由起始点以及终点在圆弧上过点的两条切线的相交点 在Q字母之后每个节点是一个二次贝塞尔曲线的构造点,多个构造点构造成一个多边贝塞尔曲线

//- 绘制二次贝塞尔曲线 Mstartx startyQcontrolx controly endx endy
path(d="M30 75Q240 30 300 120" fill="none" stroke="black")
//- 绘制多边贝塞尔曲线
path(d="M30 100Q80 30 100 100 130 65 200 80" fill="none" stroke="black")

**绘制平滑贝塞尔曲线 **
多边贝塞尔曲线的变种 使新的控制点与上一条命令中的控制点相对于当前点中心对称 快捷命令T

//- 平滑的贝塞尔曲线 平滑控制点x = startx*2 - controlx | y = starty*2 - controlY
path(d="M30 100Q80 30 100 100T200 80" fill="none" stroke="black")

绘制三次贝塞尔曲线
三次贝塞尔曲线是由一个圆弧的两个端点作为圆弧的控制点 快捷命令C 在C命令之后每两个点为一组三次贝塞尔曲线的控制点 可以有多个控制点组 构建多边三次贝塞尔曲线 绘制平滑的三次贝塞尔曲线使用快捷命令S

 //- 三次贝塞尔曲线 两个控制点是切点
 path(d="M20 80C50 20 150 60 200 120" fill="none" stroke="black")
 path(d="M30 100C50 50 70 20 100 100 110 130 45 150 65 100" fill="none" stroke="black")
 //- 三次贝塞尔曲线中心点对称
 path(d="M30 100C50 30 70 50 100 100S150 40 200 80" fill="none" stroke="black")

绘制marker标记
标记应该存储在svg流中进行使用,所以必须构建在defs元素中,marker元素拥有自己私有的坐标,所以需要在构建marker元素的时候为每个marker元素指定自有的坐标

defs
	//- markerWidth 指定marker元素坐标中的width
	//- markerHeight 指定marker元素坐标中的height
	//- refX 指定marker元素的绘制开始坐标X位于当前所在坐标的X位置
	//- refY 指定marker元素的绘制开始坐标Y位于当前所在坐标Y的位置
	//- orient="auto" 让标记自动旋转指向正确的方向
    marker#mCircle(markerWidth="10" markerHeight="10" refX="5" refY="5")
      circle(cx="5" cy="5" r="4" fill="none" stroke="black")
    marker#mArrow(markerWidth="4" and markerHeight="8" refX="0" refY="4" orient="auto")
      path(d="M0 0 4 4 0 8" fill="none" stroke="black")
    marker#mTriangle(markerWidth="5" markerHeight="10" refX="5" refY="5" orient="auto")
      path(d="M0 0 5 5 0 10Z" fill="black")
//- 在元素中使用marker marker-start是在路径开始的位置 marker-end是在路径结束的位置 marker-mid是在路径的变换的中间,如果中间位置是一个圆弧,则会在圆弧的起始和终点进行定义
path(d="M10 20 100 20A20 30 0 0 1 120 50L120 110" style="marker-start: url(#mCircle);marker-mid: url(#mArrow);marker-end:url(#mTriangle)" fill="none" stroke="black")
//- 使用marker在路径的开始、结束、以及中间位置都进行标记的定义
path(d="M10 20 100 20A20 30 0 0 1 120 50L120 110" style="marker: url(#mCircle);" fill="none" stroke="black")

7.图案以及渐变

图案

pattern属于一个单独的构建图案坐标系,其属性有:

  1. patternUnits(指定如何平铺图像)[objectBoundingBox(默认)要求图案元素宽高为百分比,按照百分比铺满元素/userSpaceOnUse要求图案元素宽高为用户单位,完全铺满当前元素,超出部分进行裁剪]
  2. patternContentUnits(指定以什么形式的数据平铺图像)[objectBoundingBox完全适应元素进行平铺,每个图案进行拉伸以满足平铺/userSpaceOnUse裁剪平铺和使用单个效果相同]
defs
    symbol#sber
      path(d="M0 0Q5 20 10 10T20 20" fill="none" stroke="black")
      path(d="M0 0h20 v20 h-20z" fill="none" stroke="gray")
    //- patternUnits(填充图案以什么比例在父元素中进行填充) objectBoundingBox(default)按照比例进行平铺-元素本身百分比 userSpaceOnUse一个个依次堆叠超出裁剪-元素本身定宽高
    pattern#otile(x="0" y="0" width="20%" height="20%" patternUnits="objectBoundingBox")
     use(xlink:href="#sber")
    pattern#utile(x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse")
     use(xlink:href="#sber")
    //- patternContentUnits(用什么单位表达填充图案数据本身)
    pattern#cctile(x="0" y="0" width="20%" height="20%" patternUnits="objectBoundingBox" patternContentUnits="objectBoundingBox")
     use(xlink:href="#sber")
    pattern#cutile(x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse" patternContentUnits="userSpaceOnUse")
     use(xlink:href="#sber")
  //- 在需要使用pattern图案的元素中使用fill: url(图案id) 调用对应的图案
  rect(x="20" y="20" width="100" height="100" style="fill: url(#cutile); stroke:black")
  rect(x="135" y="20" width="70" height="80" style="fill: url(#cutile); stroke:black")
  rect(x="220" y="20" width="150" height="130" style="fill: url(#cutile); stroke:black")

渐变

与css3相同分为线性和径向两种渐变 并可以指定渐变的方向以及渐变每个百分比的色值

  //-7.2.2 使用图案渐变--线性
  defs
    //- x1,y1 定义渐变开始的点 到x2,y2渐变结束的点 gradientUnits="userSpaceOnUse使用坐标点标注渐变范围/objectBoundingBox(default)使用小数或者百分比定义整个渐变范围内的渐变方向"
    //- 图案的变换通过patterntransform进行设置
    linearGradient#three_hues(x1="0.2" y1="0.3" x2="0.4" y2="0.8" patterntransform="skewY(15)")
      stop(offset="0%" style="stop-color: #906; stop-opacity: 1.0")
      stop(offset="50%" style="stop-color: #906; stop-opacity: 0.3")
      stop(offset="100%" style="stop-color: #906; stop-opacity: 0.1")
    //- 渐变点之间的距离并不规律 pad从对应开始位置的颜色扩散到边缘 repeat渐变重复终点到起点的位置 reflect渐变在起点到终点,终点到起点的渲染中不断重复排列
    linearGradient#padded(xlink:href="#three_hues" spreadMethod="pad")
    linearGradient#repeated(xlink:href="#three_hues" spreadMethod="repeat")
    linearGradient#reflected(xlink:href="#three_hues" spreadMethod="reflect")
    line#show-line(x1="20" y1="30" x2="40" y2="80" stroke="white")
  rect(x="20" y="20" width="200" height="100" style="fill: url(#padded);stroke:black;")
  use(xlink:href="#show-line" transform="translate(20, 20)")
  //-7.2.2 使用图案渐变--径向
  defs
    //- 1. 径向渐变默认在中心点展示
    //- 2. 设置径向渐变的中心点和半径范围(cx,cy,r) 默认三个属性值都为50%
    //- 3. 设置径向渐变的焦点(焦点和圆的中心点不同,焦点是圆中渐变的中心,取值与圆的坐标取值相同)(fx,fy)
    //- 4. 径向渐变也具有和线性一样的spreadMethod效果 pad,repeat和reflect
    //- 5. 渐变效果通过gradienttransform 进行设置
    radialGradient#three_stops(cx="0%" cy="0%" r="141%" fx="50%" fy="50%" gradienttransform="skewY(15)")
      stop(offset="0%" style="stop-color: #f96;")
      stop(offset="50%" style="stop-color: #9c9;")
      stop(offset="100%" style="stop-color: #906;")
  rect(x="20" y="20" width="100" height="100" style="fill: url(#three_stops);stroke: black")

8. 文本

用于与图案配合显示提示文字
文本分为正常文本显示以及文本路径跟随

9. 裁剪和蒙版(需要定义在defs中作为文档流图像)

裁剪是指定一个图形,将对应的图形依照裁剪图形进行裁剪

  defs
    //- 默认裁剪路径坐标被指定为用户坐标 若要根据对象边界框表示坐标 设置clipPathUnits为 objectBoundingBox
    //- 裁剪标签为clipPath
    clipPath#curveClip
      path#curve1(d="M5 55C25 5 45-25 75 55 85 85 20 105 40 55Z" stroke="black" fill="none")
    clipPath#textClip
      text#text1(x="20" y="20" transform="rotate(60)" style={'font-size':'48pt', 'stroke':'black', 'fill': 'none'}) Clip
    g#shapes
      rect(x="0" y="50" width="90" height="60" fill="#999")
      circle(cx="25" cy="25" r="25" fill="#666")
      polygon(points="30 0 80 0 80 100" fill="#ccc")
  //- 对引用元素使用裁剪 clip-path: url(裁剪图像的id)
  use(xlink:href="#shapes" style="clip-path:url(#curveClip)")
  use(xlink:href="#shapes" transform="translate(100, 0)" style="clip-path:url(#textClip)")
  g(transform="translate(0, 150)")
    use(xlink:href="#shapes")
    use(xlink:href="#curve1")
  g(transform="translate(100, 150)")
    use(xlink:href="#shapes")
    use(xlink:href="#text1")

蒙版是指定一个图像,遮罩到指定的图像上 成为该图像的蒙层,图像的新样式为该图像的样式+该蒙版的样式,蒙版透明的部分将不会进行展示

defs
    radialGradient#fade
      stop(offset="0%" style={'stop-color': 'white','stop-opacity': '1.0'})
      stop(offset="85%" style={'stop-color': 'white','stop-opacity': '0.5'})
      stop(offset="100%" style={'stop-color': 'white','stop-opacity': '0.0'})
    //- maskContentUnits="objectBoundingBox" 以蒙版内容作为对象坐标系起点 而不是使用默认坐标系作为蒙版对象坐标系
    mask#fademask(maskContentUnits="objectBoundingBox")
      rect(x="0" y="0" width="1" height="1" style="fill: url(#fade)")
image(xlink:href="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1540190808794&di=a2a93fa2a9ce9d9241692c51db693004&imgtype=0&src=http%3A%2F%2Ff.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fd8f9d72a6059252d21946778399b033b5ab5b9cf.jpg" x="72" y="92" width="160" height="120" style="mask: url(#fademask)")

10. 滤镜

通过处理图像中的图像流基元,将基元处理之后重新拼接为一个图像

defs
    filter#drop-shadow
      //- 定义当前基元的结果可以用过id为blur进行引用
      feGaussianBlur(in="SourceAlpha" stdDeviation="2" result="blur")
      //- 引入id为blur的基元进行处理 输出id为offsetBlur的基元
      feOffset(in="blur" dx="4" dy="4" result="offsetBlur")
      //- 一个合并feMerge基元组 按照从下往上合并基元信息
      feMerge
        feMergeNode(in="offsetBlur")
        //- SourceGraphic代表原始基元信息
        feMergeNode(in="SourceGraphic")
g#spring-flower(style="filter: url(#drop-shadow)")
      image(xlink:href="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1540201055229&di=67fd1b6178bd37ae607a14ca00435156&imgtype=0&src=http%3A%2F%2Ff.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fd8f9d72a6059252d21946778399b033b5ab5b9cf.jpg" x="0" y="0" width="160" height="120")

滤镜1 色值滤镜
滤镜2 图像滤镜

相关标签: 教程