Vue(十一)-在Vue中使用SVG

7,083 阅读8分钟

不想了解svg的直接看第六标题即可~

一、SVG是什么

参考文章-网道

SVG 是一种基于 XML 语法的图像格式,全称是可缩放矢量图(Scalable Vector Graphics)。其他图像格式都是基于像素处理的,SVG 则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。

SVG 文件可以直接插入网页,成为 DOM 的一部分,然后用 JavaScript 和 CSS 进行操作。

<svg width="100%" height="100%">
  <circle id="mycircle" cx="50" cy="50" r="50" />
</svg>

上面代码直接插入网页

SVG 代码也可以写在一个独立文件中,然后用<img><object><embed><iframe>等标签插入网页。

<img src="circle.svg">
<object id="object" data="circle.svg" type="image/svg+xml"></object>
<embed id="embed" src="icon.svg" type="image/svg+xml">
<iframe id="iframe" src="icon.svg"></iframe>

二、SVG的顶层标签

SVG 代码都放在顶层标签<svg>之中。

width和height属性,指定了 SVG 图像在 HTML 元素中所占据的宽度和高度。如果不指定这两个属性,SVG 图像的大小默认为300像素(宽)x 150像素(高)。

如果只想展示 SVG 图像的一部分,就要指定viewBox属性。

<svg width="100" height="100" viewBox="50 50 50 50">
  <circle id="mycircle" cx="50" cy="50" r="50" />
</svg>

<viewBox>属性的值有四个数字,分别是左上角的横坐标和纵坐标、视口的宽度和高度。上面代码中,SVG 图像是100像素宽 x 100像素高,viewBox属性指定视口从(50, 50)这个点开始。所以,实际看到的是右下角的四分之一圆。

注意,视口必须适配所在的空间。上面代码中,视口的大小是 50 x 50,由于 SVG 图像的大小是 100 x 100,所以视口会放大去适配 SVG 图像的大小,即放大了四倍。

如果不指定width属性和height属性,只指定viewBox属性,则相当于只给定 SVG 图像的长宽比。这时,SVG 图像的大小默认是所在的 HTML 元素的大小。

svg定义世界,width定义视窗,viewbox,preserveAspectRatio定义视野,世界是无限的,当视野和视窗不一致时preserveAspectRatio决定怎么样填充的问题

三、SVG的内部标签

<path>d属性表示绘制顺序,它的值是一个长字符串,每个字母表示一个绘制动作,后面跟着坐标。

  • M:移动到(moveto)
  • L:画直线到(lineto)
  • Z:闭合路径

以上标签全部是用于绘制具体图案,注意一点

SVG 里面标签的 CSS 属性与网页元素有所不同。

  • fill:填充色 
  •  stroke:描边色 
  •  stroke-width:边框宽度

四、SVG样式

如果 SVG 代码直接写在 HTML 网页之中,它就成为网页 DOM 的一部分,可以直接用 DOM 操作。

<svg
  id="mysvg"
  xmlns="http://www.w3.org/2000/svg"
  viewBox="0 0 800 600"
  preserveAspectRatio="xMidYMid meet"
>
  <circle id="mycircle" cx="400" cy="300" r="50" />
<svg>

上面代码插入网页之后,就可以用 CSS 定制样式。

circle {
  stroke-width: 5;
  stroke: #f00;
  fill: #ff0;
}

circle:hover {
  stroke: #090;
  fill: #fff;
}

SVG四种样式:

  • 内联样式
  • 内部样式表
  • 外部样式表
  • 表现属性

内联样式

用法跟 css 一样,如下所示:

<line style="fill:yellow;stroke:blue;stroke-width=4" x1="10" y1="10" x2="100" y2="100"/>*

内部样式表

用法也跟 css 的类名一样,如下所示:

.linestyle{
stroke:red;
stroke-width:2;
}
// 那么在使用标签时,指定此样式即可:
<line class="linestyle" x1="10" y1="10" x2="100" y2="100"/>

外部样式表

跟 CSS 用法一样,把样式写在另外文件中,然后导入使用。

表现属性

咱们可能通过 style 属性修改样式,当然 style 里面的属性值,可以单独写,这种也叫表现属性:

<circle cx='10' cy='10' r='5'
  fill='red' stroke='black' stroke-width='2'/>

SVG内部有fill就不会被覆盖

svg原来的颜色 fill: currentColor; 当前字体颜色

.eyeball {
  fill: red;
}

Remember:

  • This will override a presentation attribute <path fill="#fff" ... />
  • This will not override an inline style e.g. <path style="fill: #fff;" ... />

继承优先级,“直接样式”比“祖先样式”优先级高

<div style="color: red">
    <div class="son" style="color: blue"></div>
</div>


五、使用SVG

一般呢我们不会去写一个SVG静态图案,我们一般只是去操作svg文件,下面介绍几个API

1.创建图形: document.createElementNS(ns,tagname)--创建一个具有指定的命名空间URI和限定名称的元素。

SVG命名空间 - 参阅 http://www.w3.org/2000/svg

2.添加图形:element.appendChild(childElement)

3.设置获取属性:element.set/getAttribute(name,value)

xml命名空间用来避免不同来源名称相同的标签发生冲突。XML 命名空间在 XML 文档顶部使用 xmlns 属性定义

下面三步是icon网站的教程,我们看看是怎么样实现的:

第一步:拷贝项目下面生成的symbol代码:

//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js

第二步:加入通用css代码(引入一次就行):

<style type="text/css">
    .icon {
       width: 1em; height: 1em;
       vertical-align: -0.15em;
       fill: currentColor;
       overflow: hidden;
    }
</style>

第三步:挑选相应图标并获取类名,应用于页面:

<svg class="icon" aria-hidden="true">
    <use xlink:href="#icon-xxx"></use>
</svg>

关键点就在于use标签是什么,xlink明显是命名空间里的,相当于一个herf,#icon-xxx明显在引入js代码时写在里面的,把相应的svg的xml文件抽成symbol标签放到use标签中

六、Vue中使用SVG-雪碧图

原理参考这个文章

一个集合了三个SVG图标的SVG元素的代码结构会是这样:

<svg>
    <symbol>
        <!-- 第1个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第2个图标路径形状之类代码 -->
    </symbol>
    <symbol>
        <!-- 第3个图标路径形状之类代码 -->
    </symbol>
</svg>

但是只要上面代码是无法实现icon的,差一个“使用”,也就是SVG中的<use>元素。我们上面已经了解过use,<use>href属性指定所要复制的节点

use元素是SVG中非常强大,非常重要的一个元素

  1. 可重复调用;(多写几个use即可)
  2. 跨SVG调用;(SVG中的use元素可以调用其他SVG文件的元素,只要在一个文档中。只要href的id写对就行)

跨SVG调用就是“SVG Sprite技术”的核心所在,试想下,我们只要在页面某处载入一个充满Sprite(symbol)的SVG文件(或直接include SVG代码),于是,在页面的任何角落,只要想使用这个图标,只要简单这一点代码就可以了:

<svg class="size">
    <use xlink:href="#target" />
</svg>


关键就在于怎么把多个svg文件/标签放到一个svg标签里,这就用到了 svg-sprite-loader技术

总结:利用svg的symbol元素,将每个icon包括在symbol中,通过use元素使用该symbol.

七、 svg-sprite-loader

参考文章

svg-sprite-loader会把你的svg塞到一个个symbol中,symbol的id如果不特别指定,就是你的文件名。它最终会在你的html中嵌入这样一个svg,一般插在body顶部


svg-sprite-loader配置如下:

{
  test: /\.svg$/,
  loader: 'svg-sprite-loader',
}

有一点需要注意的是,我们并不是所有的svg都要放在我们的雪碧图里,有的也许我就想当做图片用。这时候在我们的webpack配置中,我们需要对这两种svg区别对待。
首先,我们要把所有要作为icon的svg团结在一起,放在某个文件夹中,例如assets/icons。其他的svg就随你便啦。

然后对于想要用作图片的:

{
  test: /\.svg$/,
  loader: 'file-loader',
  exclude: path.resolve(__dirname, './src/assets/icons') // 不带icon 玩
}

官方文档说的是在webpack开头的文件中操作,但是我们在Vue中没有,所以我们就要将Vue-cli的内容翻译成svg-sprite-loader文档的内容

chainWebpack: config => {
    const dir = path.resolve(__dirname, "src/assets/icons");
    config.module
      .rule("svg-sprite")
      .test(/\.svg$/)
      .include.add(dir)
      .end() // 包含 icons 目录
      .use("svg-sprite-loader")
      .loader("svg-sprite-loader")
      .options({ extract: false })
      .end();
    // eslint-disable-next-line @typescript-eslint/no-var-requires
    config
      .plugin("svg-sprite")
      // eslint-disable-next-line @typescript-eslint/no-var-requires
      .use(require("svg-sprite-loader/plugin"), [{ plainSprite: true }]);
    config.module.rule("svg").exclude.add(dir); // 其他 svg loader 排除 icons 目录
  }


svg-sprite-loader把引入为.svg后缀的文件,集合在一起,插入到body头部,我们用use直接使用即可,由于没有指定id,所以文件名就是href

关于自动导入以及icon使用的历史

八、个人实例

<template>
    <svg v-if="icon in icons" class="icon">
        <use :xlink:href="'#i-'+icon"></use>
    </svg>
</template>
<script>
    // import 'src/assets/icons/down.svg'
    // import 'src/assets/icons/download.svg'
    // import 'src/assets/icons/right.svg'
    // import 'src/assets/icons/left.svg'
    // import 'src/assets/icons/like.svg'
    // import 'src/assets/icons/setting.svg'

    export default {
        data: function () {
            return {
                icons: {
                    down: "down",
                    setting:"setting",
                    download:"download",
                    right:"right",
                    left:"left",
                    like:"like",
                    loading:"loading"
                }
            }
        },
        props: ['icon']
    }
</script>
<style lang="scss" scoped>
    .icon {
        width: 1em; height: 1em;
        vertical-align: -0.15em;
        fill: currentColor;
        overflow: hidden;
    }
</style>

这个是采用script标签的,采用雪碧loader也可以,标签或者loader会把svg变成symbol都弄到一个svg里,注意v-if的作用

如果你不写v-if,那么如果你想传的svg不存在,还是会出现1em宽高的空白,写了当传错icon名字时就不会有svg这个标签

import Icon from './icon'
Vue.component('g-icon',Icon);

<g-icon :icon="icon"></g-icon>

<script src="//at.alicdn.com/t/font_1660402_gwll4l9vydc.js"></script>

你可以由以下script标签把svg都变成symbol集合到一个svg里,也可以自己下载svg自己import自己用雪碧loader,阿里的服务器坏的概率极小,看个人选择

还有一种方法(最推荐),把阿里链接的所有代码复制到svg.js,在Icon组件引入即可