在 Vue 中编写 SVG 图标组件的方法


Posted in Javascript onFebruary 24, 2020

在 Vue 中编写 SVG 图标组件的方法

在考虑了将矢量图标从图标字体迁移到内联 SVG 的 原因 之后,我在 Vue.js 中找到了一个用 SVG 替换图标字体的解决方案,同时仍能保持使用图标字体的灵活性和易用性——能够使用 CSS 轻松改变图标的大小、颜色以及其它属性。

一种流行的方法是使用 v-html 指令和 npm 模块 html-loader 来将 SVG 导入到我们的 Vue 模板中,并在 Vue 的生命周期函数 mounted() 中修改渲染的 <svg> 元素。CSS 样式可以直接应用到 <svg> 元素或者是其父元素上,并且这些能够组成一个可复用的组件。

创建 Svg-icon 组件

让我们创建 Svg-icon.vue 组件文件,并在里面接收三个 prop 变量。

  • icon 是一个字符串类型的 prop 变量用来传递 .svg 文件名的导入
  • hasFill 是一个布尔类型的 prop 变量来告诉组件 fill 属性是否用于更改 <svg> 元素的颜色,默认值为 false 即不使用 fill 属性
  • growByHeight 是一个布尔类型的 prop 变量来决定 height 或 width 是否相对于 font-size 进行缩放,默认值为 true 即使用 height
<script>
function recursivelyRemoveFill(el) {
 if (!el) {
  return;
 }
 el.removeAttribute('fill');
 [].forEach.call(el.children, child => {
  recursivelyRemoveFill(child);
 });
}
export default {
 name: 'svg-icon',
 props: {
  icon: {
   type: String,
   default: null
  },
  hasFill: {
   type: Boolean,
   default: false
  },
  growByHeight: {
   type: Boolean,
   default: true
  },
 },
 mounted() {
  if (this.$el.firstElementChild.nodeName === 'svg') {
   const svgElement = this.$el.firstElementChild;
   const widthToHeight = (svgElement.clientWidth / svgElement.clientHeight).toFixed(2);
   if (this.hasFill) {
    // recursively remove all fill attribute of element and its nested children
    recursivelyRemoveFill(svgElement);
   }
   // set width and height relative to font size
   // if growByHeight is true, height set to 1em else width set to 1em and remaining is calculated based on widthToHeight ratio
   if (this.growByHeight) {
    svgElement.setAttribute('height', '1em');
    svgElement.setAttribute('width', `${widthToHeight}em`);
   } else {
    svgElement.setAttribute('width', '1em');
    svgElement.setAttribute('height', `${1 / widthToHeight}em`);
   }
   svgElement.classList.add('svg-class');
  }
 }
}
</script>

<template>
 <div v-html="require(`html-loader!../assets/svg/${icon}.svg`)" class="svg-container"></div>
</template>

<style lang="scss" scoped>
.svg-container {
 display: inline-flex;
}
.svg-class {
 vertical-align: middle;
}
</style>

我们将 .svg 图标文件通过 require() 传递给 html-loader 方法,该方法会将文件字符串化,并且通过 v-html 指令将其渲染为 <svg> 元素。

所有对 <svg> 元素修改的地方都在 mounted() 生命周期方法里面。

  • 将由 growByHeight 定义的 <svg> 元素的 height 或 width 属性设置为 1em ( font-size 的一倍)并对另一个元素使用 widthToHeight 。由于并非所有的 SVG 都是正方形的,因此我们从渲染的元素计算 withToHeight 比率,以便 SVG 在父元素的 font-size 属性大小改变的时候按比例缩放到其原始尺寸。
  • 为了设置 <svg> 元素的 fill 属性,我们需要覆盖掉 SVG 文件内部附带的 fill 属性。当 hasFill 值为 true 的时候,我们从 <svg> 元素及其子元素中递归地删除 fill 属性。然后使用 CSS 选择器将 fill 值添加到其父元素或 <svg> 元素就可以了。
  • 还可以向元素中添加例如 class 等其它 DOM 属性,这些属性可用于设置组件中的范围样式

创建微笑图标

让我们使用 Font Awesome 和我们的 Svg-icon 组件中的图标字体创建一个微笑图标。

在 Vue 中编写 SVG 图标组件的方法

使用图标字体

<template>
 <i class="fas fa-smile smile-icon"></i>
</template>

<style lang="scss" scoped>
.smile-icon {
 font-size: 24px;
 color: #aaa;

 &:hover {
 color: #666;
 }
}
</style>

.smile-icon 类的 CSS 选择器以及伪类选择器 :hover 来设置图标的 font-size 和 color 属性。

使用 Svg-icon 组件

<script>
import SvgIcon from './components/Svg-icon';

export default {
 name: 'my-component',
 components: {
 'svg-icon': SvgIcon,
 },
}
</script>

<template>
 <div class="smile-icon">
 <svg-icon icon="smile-solid" :hasFill="true"></svg-icon>
 </div>
</template>

<style lang="scss" scoped>
.smile-icon {
 font-size: 24px;
 fill: #aaa;

 &:hover {
 fill: #666;
 }
}
</style>

上面的实现和图标字体方法相同,除了 .smile-icon 类在父元素中,在 Svg-icon 组件中, color 属性被替换为 fill 。我们还必须确保 smile-solid.svg 文件位于 Svg-icon 组件的 require() 方法指定的路径( ./assets/svg/ )中。

渲染成 HTML

这是由 v-html 的输出渲染的 HTML。注意:会删除掉所有的 fill 属性,并将 height 和 width 属性添加到 <svg> 中。

<div class="smile-icon">
 <svg height="1em" width="1em" aria-hidden="true" focusable="false" data-prefix="fas" data-icon="smile" class="svg-inline--fa fa-smile fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
 <path d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm80 168c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm-160 0c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zm194.8 170.2C334.3 380.4 292.5 400 248 400s-86.3-19.6-114.8-53.8c-13.6-16.3 11-36.7 24.6-20.5 22.4 26.9 55.2 42.2 90.2 42.2s67.8-15.4 90.2-42.2c13.4-16.2 38.1 4.2 24.6 20.5z">
 </path>
 </svg>
</div>

过渡到 SVG

在 Vue 中编写 SVG 图标组件的方法

由于 SVG 被认为是未来的发展方向,因此最好是在保留图标字体的易用性的基础上,逐步放弃使用图标字体。 Svg-icon 组件是一个例子,告诉了我们如何使用可用的库来抽离出 <svg> 元素中的混乱部分,同时模仿使用图标字体的好处!

总结

到此这篇关于在 Vue 中编写 SVG 图标组件的文章就介绍到这了,更多相关vue SVG 图标组件内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript在一段文字中的光标处插入其他文字
Aug 26 Javascript
Extjs4 GridPanel 的几种样式使用介绍
Apr 18 Javascript
jquery选择器之属性过滤选择器详解
Jan 27 Javascript
用循环或if语句从json中取数据示例
Aug 18 Javascript
javascript 实现map集合
Apr 03 Javascript
js实现仿京东2级菜单效果(带延时功能)
Aug 27 Javascript
JavaScript深度复制(deep clone)的实现方法
Feb 19 Javascript
玩转JavaScript OOP - 类的实现详解
Jun 08 Javascript
iscroll实现下拉刷新功能
Jul 18 Javascript
JS实现左边列表移到到右边列表功能
Mar 28 Javascript
JS中验证整数和小数的正则表达式
Oct 08 Javascript
深入详解JS函数的柯里化
Jun 09 Javascript
原生javascript中this几种常见用法总结
Feb 24 #Javascript
js实现坦克大战游戏
Feb 24 #Javascript
Vue中点击active并第一个默认选中功能的实现
Feb 24 #Javascript
如何在JavaScript中创建具有多个空格的字符串?
Feb 23 #Javascript
浅谈TypeScript的类型保护机制
Feb 23 #Javascript
原生javascript制作的拼图游戏实现方法详解
Feb 23 #Javascript
原生javascript运动函数的封装示例【匀速、抛物线、多属性的运动等】
Feb 23 #Javascript
You might like
php实现文章评论系统
2019/02/18 PHP
js jquery做的图片连续滚动代码
2008/01/06 Javascript
javascript 关闭IE6、IE7
2009/06/01 Javascript
javascript Array.sort() 跨浏览器下需要考虑的问题
2009/12/07 Javascript
nodejs入门详解(多篇文章结合)
2012/03/07 NodeJs
在Google 地图上实现做的标记相连接
2015/01/05 Javascript
jQuery获取标签文本内容和html内容的方法
2015/03/27 Javascript
jQuery-1.9.1源码分析系列(十)事件系统之事件包装
2015/11/20 Javascript
jQuery插件formValidator自定义函数扩展功能实例详解
2015/11/25 Javascript
Vue实现点击后文字变色切换方法
2018/02/11 Javascript
vue-cli 首屏加载优化问题
2018/11/06 Javascript
基于vue实现图片验证码倒计时60s功能
2019/12/10 Javascript
JS指定音频audio在某个时间点进行播放
2020/11/28 Javascript
JS实现鼠标移动拖尾
2020/12/27 Javascript
寻找网站后台地址的python脚本
2014/09/01 Python
浅析Python3爬虫登录模拟
2018/02/07 Python
在PyCharm环境中使用Jupyter Notebook的两种方法总结
2018/05/24 Python
python读取TXT每行,并存到LIST中的方法
2018/10/26 Python
python+logging+yaml实现日志分割
2019/07/22 Python
浅谈Python访问MySQL的正确姿势
2020/01/07 Python
Python reshape的用法及多个二维数组合并为三维数组的实例
2020/02/07 Python
图片上传插件ImgUploadJS:用HTML5 File API 实现截图粘贴上传、拖拽上传
2016/01/20 HTML / CSS
潘多拉珠宝俄罗斯官方网上商店:PANDORA俄罗斯
2020/09/22 全球购物
数字漫画:comiXology
2020/06/13 全球购物
路由表示做什么用的?在linux环境中怎么来配置一条默认路由?
2013/06/07 面试题
办公室主任岗位职责
2013/11/08 职场文书
公司端午节活动方案
2014/02/04 职场文书
安全生产承诺书范文
2014/05/22 职场文书
党的群众路线整改落实情况汇报
2014/10/28 职场文书
考试没考好检讨书(精选篇)
2014/11/16 职场文书
优秀党员个人总结
2015/02/14 职场文书
学校青年志愿者活动总结
2015/05/06 职场文书
MySQL一些常用高级SQL语句
2021/07/03 MySQL
【TED出品】天梯非主流开心游1700 划水骑士
2022/03/31 魔兽争霸
Spring Cloud Netflix 套件中的负载均衡组件 Ribbon
2022/04/13 Java/Android
Python 统计序列中元素的出现频度
2022/04/26 Python