svg半小时全方位入门

1 - svg 应用场景和优势

canvas 快速入门

canvas 是 HTML5 的新特性,它允许我们使用 canvas 元素在网页上通过 JavaScript 绘制图像。

  • 入门案例:绘制矩形、线段、圆、点
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <!-- 声明canvas画布宽高为800 -->
    <canvas id="canvas" width="800" height="800"></canvas>
  </body>
  <script>
    // 根据id获取dom元素
    const canvas = document.getElementById('canvas')
    // 通过dom元素获取到canvas对象
    const ctx = canvas.getContext('2d')
    // 修改填充色为红色
    ctx.fillStyle = 'red'
    // 在画布(0, 0)位置为起点绘制长宽为50的矩形
    ctx.fillRect(0, 0, 50, 50)

    // 开启一条路径
    ctx.beginPath()
    // 线宽为1px
    ctx.lineWidth = 1
    // 边框颜色为蓝色
    ctx.strokeStyle = 'blue'
    // 起点为(100, 100)
    ctx.moveTo(100, 100)
    // 画到(250, 75)的位置
    ctx.lineTo(250, 75)
    // 接着画到(300, 100)的位置
    ctx.lineTo(300, 100)
    // 绘制线段
    ctx.stroke()

    // 开启一条路径
    ctx.beginPath()
    // 线宽为2px
    ctx.lineWidth = 2
    // 边框颜色为绿色
    ctx.strokeStyle = 'green'
    // 填充色为红色
    ctx.fillStyle = 'red'
    // 设置圆心为(200, 200) 半径50 起始角度为0 结束角度为2pi 即360°
    ctx.arc(200, 200, 50, 0, 2 * Math.PI)
    // 绘制线段
    ctx.stroke()
    // 填充
    ctx.fill()

    // 绘制一个点 本质上是1px的线段
    ctx.beginPath()
    ctx.lineWidth = 1
    ctx.strokeStyle = 'blue'
    ctx.moveTo(300, 300)
    ctx.lineTo(301, 301)
    ctx.stroke()
  </script>
</html>

canvas绘图流程

  1. 编写 canvas 标签(注意指定宽高)
  2. 获取 cancas DOM 对象
  3. 获取 canvas 对象
  4. 设置绘图属性
  5. 调用绘图 API

SVG 快速入门

SVG 是一种基于 XML 的图像文件格式,它的英文全称为 Scalable Vector Graphics,意思为可缩放的矢量图形。

  • 入门案例:绘制矩形、线段、圆、点
<!DOCTYPE html>
<html>
  <head></head>
  <body>
    <svg width="800" height="800">
      <!-- 绘制一个50x50的矩形, 填充红色, 边框1px, 黑色 -->
      <rect width="50" height="50" style="fill: red;stroke-width: 1px;stroke: rgb(0,0,0)" />
      <!-- 绘制一条线段, 坐标从(100,100)到(250,75), 边框1px, 蓝色 -->
      <line x1="100" y1="100" x2="250" y2="75" style="stroke-width: 1px;stroke: blue" />
      <!-- 绘制另一条线段, 坐标从(250,75)到(300,100), 边框1px, 蓝色 -->
      <line x1="250" y1="75" x2="300" y2="100" style="stroke-width: 1px;stroke: blue" />
      <!-- 绘制一个圆, 圆心位于(200,200), 半径50px, 边框绿色2px, 填充红色 -->
      <!-- stroke,stroke-width,fill等样式属性也可以放到style中 -->
      <circle cx="200" cy="200" r="50" stroke="green" stroke-width="2" fill="red" />
      <!-- 绘制一个点, 本质其实是线段 -->
      <line x1="300" y1="300" x2="301" y2="301" style="stroke-width: 1px;stroke: blue" />
    </svg>
  </body>
</html>

对比

  • canvas 和 SVG 单从绘图的角度来看,从实现效果上看是比较类似的,但两者实际的编码过程完全不同。
  • SVG 通过 dom 进行编程,canvas 通过 JavaScript API 的方式进行编程
  • SVG 可以选中具体的dom,因为他们本身就是 html 标签;而 canvas 的交互全部都需要通过 JavaScript API 进行
  • 所以 canvas 有更高的绘图效率,尤其在绘制动画的场景下可以更胜一筹,但是 svg 的矢量图特性,可以让我们在放大的场景下,仍然可以获得较清晰的图像,所以特别适合作为图标和矢量图的场景。

SVG 应用场景

  • 绘制icon
  • 绘制动画

SVG 优势

  • SVG 占用更小的容量

    由于使用 XML 格式声明,因此通过 SVG 制作的图标或图像,其容量会比较小。对于一个纯色的200 x 200的简单图标,使用 png 格式图片需要大约8422字节,而使用 SVG 仅需1828字节。更小的容量意味着更快的加载速度,会有更好的用户体验。

  • SVG 具有更高的灵活性

    对于 png 或其它图片格式而言,无法直接修改其颜色;而 SVG 通过在绘制路径上指定 fill 属性即可方便地更改颜色

2 - svg viewport和viewBox用法讲解

  • viewport 是 svg图像的可见区域,即画布大小
  • viewBox 是用于在画布上绘制 svg 图形的坐标系统

示例:

<!DOCTYPE html>
<html>
<head>
  <style>
    .parent {
      color: green;
    }
    .parent div {
      color: black;
      margin-top: 10px;
    }
  </style>
</head>
<body>
  <!-- svg 中的 currentColor 会继承父容器的 color -->
  <div class="parent">
    <div>viewport + viewBox 显示效果</div>
      <!-- svg 会将 viewBox 指定的显示区域缩放为viewport大小 -->
      <svg width="500" height="200" viewBox="0 0 50 20" style="border: 1px solid red">
        <rect x="20" y="10" width="10" height="5" stroke="currentColor" fill="none"></rect>
      </svg>
    <div>不指定 viewBox 显示效果</div>
    <svg width="500" height="200" style="border: 1px solid red">
    <!-- 不指定 viewBox 则显示区域为画布大小 -->
       <rect x="20" y="10" width="10" height="5" stroke="currentColor" fill="none"></rect>
    </svg>
    <div>在不指定 viewBox 的情况下手动使矩形显示效果与指定 viewBox 时显示一致</div>
    <svg width="500" height="200" style="border: 1px solid red">
    <!-- 手动将内容放大10倍 -->
      <rect x="200" y="100" width="100" height="50" stroke-width="10" stroke="currentColor" fill="none"></rect>
    </svg>
  </div>
</body>
</html>

3 - 剖析 svg 中 preserveAspectRatio 属性

preserveAspectRatio 用于当 viewport 和 viewBox 宽高比不相同时,指定 viewBox 坐标系在 viewport 中是否完全可见,同时也可以指定它在 viewport 坐标系统中的位置

用法:

<svg preserveAspectRatio="<align> [<meetOrSlice>]"></svg>
  • <align> 属性值
    使用以下对齐方式会保持宽高比:

    • 水平对齐方式
      • xMin 左侧
      • xMid 居中(默认值)
      • xMax 右侧
    • 垂直对齐方式
      • YMin 顶部
      • YMid 居中(默认值)
      • YMax 底部

    不保持宽高比,直接拉伸:

    • none
  • <meetOrSlice> 可选属性值
    如果提供的话,与 <align> 间隔一个或多个空格,参数为以下二者之一:

    • meet (默认值)
      保留宽高比,使整个 viewBox 能在 viewport 中可见,以 x 和 y 中较小的缩放比进行整体的缩放,类似css中的background-size: contain
    • slice
      保留宽高比,使用 viewport 覆盖 viewBox,以 x 和 y 中较大的缩放比进行整体的缩放,类似css中的background-size: cover

由于 <align> 属性值比较简单,就是 viewBox 在 viewport 的上中下左中右的位置,此处不再赘述。

属性值说明

meet示例
<svg width="500" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMidYMax meet">
  <rect x="100" y="100" width="100" height="50" stroke-width="10" stroke="green" fill="none"></rect>
</svg>

上述配置的原理如下图:

meet 会取 x 和 y 中缩放比例较小的值,本例中
500/200=2.5
200/200=1
因此缩放比例为1,故对 viewBox 的大小进行缩放,由为缩放比例为1,所以没有变化
取值为水平方向居中垂直方向居底部
由为缩放比例为1,因此图形大小没有变化
图形位置由于是相对 viewBox 的坐标系确定的,因此在图中所示位置

slice 示例
<svg width="500" height="200" viewBox="0 0 200 200" preserveAspectRatio="xMidYMax slice">
  <rect x="100" y="100" width="100" height="50" stroke-width="10" stroke="green" fill="none"></rect>
</svg>

上述配置的原理如下图:

slice 会取 x 和 y 中缩放比例较大的值,本例中
500/200=2.5
200/200=1
因此缩放比例为2.5,故对 viewBox 的大小进行缩放,viewBox 宽高均缩放为500
取值为水平方向居中垂直方向居底部
图形大小与相对坐标也按照2.5的比例进行缩放,因此绘制于图中位置
阴影部分超出了 viewport 区域因此不会显示

最后附上MDN链接:https://developer.mozilla.org/zh-CN/docs/Web/SVG/Attribute/preserveAspectRatio

4 - svg 组件库最佳实践

在 vue 工程中创建一个组件叫做 SvgIcons
把上面的 svg 矩形复制到组件中稍作修改
组件代码如下:

<!-- src/components/SvgIcons/SvgIcons.vue -->
<template>
  <svg width="500" height="200" viewBox="0 0 50 20" style="border: 1px solid red">
    <rect x="10" y="10" width="10" height="5" stroke="green" fill="none" />
  </svg>
</template>
<script>
  export default {
     name: 'SvgIcons'
  }
</script>

在 App.vue 中使用

<!-- src/App.vue -->
<template>
  <div id="app">
    <svg-icons></svg-icons>
  </div>
</template>

<script>
import SvgIcons from './components/SvgIcons.vue'

export default {
  name: 'App',
  components: {
    SvgIcons
  }
}
</script>

很简单就完成了 svg 组件的使用,然而实际上 svg 组件库的封装并没有这么简单

svg组件库

svg官方为我们提供了一些标签:

  • defs
    用于定义以后需要重复使用的图形
  • g
    将图形进行分组
  • use
    用于取出要使用的图形
  • symbol
    与 g 标签类似,但图形开发人员可以指定 viewBox 属性

用于帮助我们完成组件库的封装

首先,编写一些 svg 图标作为 svg 组件

修改 SvgIcons 组件

<!-- src/components/SvgIcons/SvgIcons.vue -->
<template>
  
  <svg width="0" height="0" viewBox="0 0 100 100">
    <defs>
      <!-- 一个类似菜单按钮的图标 -->
      <g id="menu">
        <circle r="5" cx="20" cy="25" fill="currentColor" />
        <circle r="5" cx="20" cy="50" fill="currentColor" />
        <circle r="5" cx="20" cy="75" fill="currentColor" />
        <line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="currentColor" />
        <line x1="40" y1="50" x2="90" y2="50" stroke-width="8" stroke="currentColor" />
        <line x1="40" y1="75" x2="90" y2="75" stroke-width="8" stroke="currentColor" />
      </g>
    </defs>
  </svg>
</template>
<script>
  export default {
     name: 'SvgIcons'
  }
</script>

修改 App.vue

<!-- src/App.vue -->
<template>
  <div id="app">
    <svg-icons></svg-icons>
    <svg width="30" height="30">
      <use href="#menu" />
    </svg>
  </div>
</template>

<script>
import SvgIcons from './components/SvgIcons.vue'

export default {
  name: 'App',
  components: {
    SvgIcons
  }
}

可以看到 svg 图标显示出来了,但只显示了一部分,这是因为 g 标签中是没有指定 viewBox 的,而最外层使用的 svg 也没有指定 viewBox,因此显示区域的大小与 viewport 相同为 30x30,图标实际大小为 100x100

在 App.vue 的 svg 加上 viewBox="0 0 100 100" 即可正常显示

由此可见 g 标签的局限,外层的 svg 必须指定 viewBox,但是我们作为图标的使用者,往往是不知道 viewBox 大小的,尤其是当使用一些来自外部的图标的时候,此时使用图标还需要阅读图标的源码就非常麻烦。

为了解决这个问题,出现了 symbol 标签,官方建议我们所有的 svg 图标都用 symbol 进行封装。

修改 SvgIcons.vue 的 g 标签为 symbol 标签,并加上 viewBox="0 0 100 100"
删除 App.vue 的 svg 上的 viewBox 属性
增加一些图标

最终代码如下

<!-- SvgIcons.vue -->
<template>
  <svg width="0" height="0" viewBox="0 0 100 100">
    <defs>
      <!-- 一个类似菜单按钮的图标 -->
      <symbol id="menu" viewBox="0 0 100 100">
        <circle r="5" cx="20" cy="25" fill="currentColor" />
        <circle r="5" cx="20" cy="50" fill="currentColor" />
        <circle r="5" cx="20" cy="75" fill="currentColor" />
        <line x1="40" y1="25" x2="90" y2="25" stroke-width="8" stroke="currentColor" />
        <line x1="40" y1="50" x2="90" y2="50" stroke-width="8" stroke="currentColor" />
        <line x1="40" y1="75" x2="90" y2="75" stroke-width="8" stroke="currentColor" />
      </symbol>
      <!-- 填充的右箭头 -->
      <symbol id="filledArrowRight" viewBox="0 0 100 100">
        <polyline points="20 10, 80 50, 20 90" fill="currentColor" />
      </symbol>
      <!-- 右箭头 -->
      <symbol id="arrowRight" viewBox="0 0 100 100">
        <polyline points="30 20, 70 50, 30 80" fill="transparent" stroke="currentColor" stroke-width="3" />
      </symbol>
    </defs>
  </svg>
</template>
<script>
  export default {
     name: 'SvgIcons'
  }
</script>

<!-- App.vue -->
<template>
  <div id="app">
    <!-- 以组件形式引入图标 -->
    <svg-icons></svg-icons>
    <svg width="30" height="30">
      <use href="#menu" />
    </svg>
    <svg width="50" height="50" style="color: green">
      <use href="#filledArrowRight" />
    </svg>
    <svg width="50" height="50" style="color: grey">
      <use href="#arrowRight" />
    </svg>
  </div>
</template>

<script>
import SvgIcons from './components/SvgIcons.vue'

export default {
  name: 'App',
  components: {
    SvgIcons
  }
}
</script>

5 - 通用 svg 组件封装

经过对 svg 组件库的编写,在使用 svg 图标的时候会写很多类似 <svg ...><use ... /></svg> 的代码,可以将其封装为组件。
创建一个 Icon 组件

创建 Icon.vue 组件

<!-- src/components/Icon/Icon.vue -->
<template>
  <svg :style="{...style}">
    <use :href="iconName"></use>
  </svg>
</template>
<script>
export default {
  name: 'Icon',
  props: {
    name: String,
    style: Object
  }
  setup(ctx) {
    const iconName = `#${ctx.name}`
    return {
       iconName
    }
  }
}

在 App.vue 中使用

<template>
  <div id="app">
    <!-- 以组件形式引入图标 -->
    <svg-icons></svg-icons>
    <icon name="arrowRight" :style="{width: '30px', height: '30px'}" />
    <icon name="menu" class="common-icon" />
  </div>
</template>

<script>
import SvgIcons from './components/SvgIcons'
import Icon from './components/Icon'

export default {
  name: 'App',
  components: {
    SvgIcons,
    Icon
  }
}
</script>
<style scoped>
.common-icon {
  width: 50px;
  height: 50px;
  color: red;
}
</style>

6 - iconfont最佳实践

  1. 在 iconfont 上新建一个项目
  2. 搜索需要的图标添加入购物车
  3. 将购物车中的图标加入项目
  4. 对于有的图标自带颜色,而你不需要的情况,可以先将图标下载到本地,然后上传至项目,选择去除颜色并提交
  5. 使用官方推荐的 Symbol 方式生成图标,对于测试使用可以直接将生成的链接放入 public.html 中,通过js引入
<script src="//at.alicdn.com/t/c/font_4436236_b4vj4cc2vy7.js"></script>
  1. 加入通用css代码(全局引入一次即可)
    修改 Icon 组件

代码修改如下

<!-- src/components/Icon/Icon.vue -->
<template>
  <div class="icon-wrapper" :style="{...style}">
    <svg class="icon">
      <use :href="iconName"></use>
    </svg>
  </div>
</template>
<script>
export default {
  name: 'Icon',
  props: {
    name: String,
    style: Object
  }
  setup(ctx) {
    const iconName = `#${ctx.name}`
    return {
       iconName
    }
  }
}
</script>
<style type="text/css">
.icon-wrapper {
  display: inline-block;
}

.icon {
  width: 100%;
  height: 100%;
  vertical-align: -0.15em;
  fill: currentColor;
  overflow: hidden;
}
  1. 在 Icon 组件中根据图标名称进行使用
<icon name="icon-scan" class="common-icon" />

以上就是 iconfont 最近实践,快去试一试吧~

posted on 2024-02-17 11:57  是林克呀  阅读(24)  评论(0编辑  收藏  举报