为什么不切图标了
以前的图标我们非常喜欢用 ps 等工具切成一张张 xxx.PNG 图片, 如果稍微懂点移动端适配, 对 dpr(设备像素比) 有所了解的, 还会切出类似 home@1x.ppng,home@2x.PNG,home@3x.PNG 这样的图标, 其中 @1x 表示用来适配 dpr 为 1 的手机, 图像分辨率最低;@2x 表示用来适配 dpr 为 2 的手机, 图像分辨率稍高一些;@3x 用来适配 dpr 为 3 的手机, 图像分辨率最高.
最后, 借助媒体查询如 @media(min-device-pixel-ratio: 2){ } 这样的代码对不同 dpr 的手机写上对应分辨率的图片, 在大型项目中, 本地静态图片可能类似这样:
这是非常混乱且难以管理的.
而且, PNG 格式也好, jpg 格式也罢, 作为位图, 它们都有两个天然的缺陷: 一是图片资源体积小与清晰度高这两者不可兼得; 二是无论清晰度再怎么高, 放大后依然不清晰.
我们可以简单感性地理解一下: 位图是由很多个像素点构成的, 理论上, 位图的像素和屏幕上的物理像素一一对应, 就可以达到最佳的显示效果. 然而现在 dpr>1 的屏幕比比皆是, 在这种场景下, 位图的一个像素会由多个物理像素来渲染. 在网页缩放时是更是如此, 缩放厉害时一个位图像素可能横跨几百个物理像素.
这会带来两个问题: 一是多个物理像素对应一个位图像素会显得很大, 也就是所谓的 "锯齿感", 二是这些物理像素并不能准确获取对应位图像素的颜色, 很多取近似值的情况, 导致颜色很杂.
我们从 iconfont 中找一个图标文件, 下载其对应的 PNG 图片, 通过放大来看看效果:
可以看到放大后, 一是有不少 "大方块", 二是出现了一些 "近似色".
为什么矢量图会更清晰? 因为矢量图, 比如 svg, 是一种对图像的形状描述, 比如描述曲线, 形状, 颜色等, 本质上属于文本文件, 不仅体积小, 放大后质量也不会下降.
怎么专挑图标下手
对矢量图来说, 绘制的路径过于复杂, 颜色过于丰富, 也会导致绘制性能下降, 这是矢量图的缺点, 所以我们不可能拿矢量图去绘制一张广告背景图, 但是图标却很合适, 因为图标往往没有复杂的颜色信息, 也没有像风景人像图那样复杂的线条.
在 vue 项目中试试阿里图标
打开阿里图标官网 https://www.iconfont.cn/
登录
新建一个项目
找几个图标, 添加至项目
选择 "Font class" 方式引入, 并下载项目到本地
选择 Font class 的原因一是语义化, 二是通过类名来定义具体图标非常方便封装
6. 将解压出来的文件夹重命名后放到项目的 src/assets 目录下 (放在这里是因为 assets 目录在初始化的时候就被认为是一个用于存放静态资源的目录)
现在可以开始着手封装
如果你看过我之前关于 echart 封装那篇文章, 就会发现我非常不推荐将封装的公共组件的单文件直接写在 components 根目录下, 这是一个非常差的习惯. 强烈建议再建一级目录, 同时为你的公共组件写上 README 和使用示例, 以及入参说明, 这会极大的方便后续使用者的调用
封装之前的思考
在封装之前, 我们需要考虑以下几点:
可以通过 type 或者 name 入参来标识具体是哪个图标
要提供最常用的样式入参, 一个是图标颜色 color, 另外一个是图标大小 size
能响应事件
使用者能定制一些特殊样式如旋转角度 rotation, 缩放倍数 scale 等
如果在业务上有需要, 做的更好一点, 还可以提供 dot 和 badge 功能, 也就是图标右上角的小红点和数量
能做到前四点, 就已经完成了一个基础版图标组件的封装; 如果能加上第五点, 给这手封装打个 90 分, 我认为没有问题.
封装的源码及说明
下面这个是没有 dot 和 badge 的, 也就是一个青春版的图标组件封装, 如果点赞超过 10 个, 我会更新这篇文章, 封装一个带小红点功能和图标右上角显示消息数量的组件, 也就是尊享版的
- <template>
- <!-- 有两个注意点: 一是 iconfont 这个类名必须添加, 二是通过 $listeners 将父作用域中的事件监听器导入进来, 这样就可以正常相应点击事件 -->
- <i
- :class="name"
- class="iconfont"
- :style="{ fontSize: `${size}px`, color }"
- v-on="$listeners"
- ></i>
- </template>
- <script>
- export default {
- name: "Icon",
- props: {
- // 图标名称
- name: {
- type: String,
- default: ""
- },
- // 图标颜色
- color: {
- type: String,
- default: ""
- },
- // 图标大小
- size: {
- type: [String, Number],
- default: 20
- }
- }
- };
- </script>
- <style lang="less" scoped>
- // 这里加~是因为当你在一个资源路径最前面加了~时, webpack 会把这个路径对应的文件视为一个 module(模块) 去解析
- // 这样 @对应的 alias, 也就是项目的 src 目录才会被正确识别, 这步解析是 webpack 做的, 和网上很多文章说的 CSS-loader 没有一毛钱关系
- // 还有一种写法是 @import url(), 两种写法都可以, 没有对错之分
- @import "~@/assets/iconfont/iconfont.css";
- .iconfont {
- cursor: pointer;
- }
- </style>
在这里, 需要特别说明以下为什么类名一定要默认添加一个 "iconfont", 简单来说, 因为只有. iconfont 这个类名继承了特殊名为 "iconfont" 的字体家族. 我们可以在 iconfont.CSS 源码中验证这一点:
- @font-face {font-family: "iconfont";
- src: url('iconfont.eot?t=1586688927486'); /* IE9 */
- src: url('iconfont.eot?t=1586688927486#iefix') format('embedded-opentype'), /* IE6-IE8 */
- url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAQcAAsAAAAACEAAAAPQAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqEKINlATYCJAMMCwgABCAFhG0HNxssB1GULUqM7CMhN9F6CVFamUwxYBhOWrS38fC13+/c3f3vmwFJkniD7NNJopFEKiRIlWE6pTNdNNQ30OZCMTk7sLo2ozc2cY1cNN61VXFVvwrYriJ84t7pH3Ue2HyW5TLH1k9dgHFAge153gUScIEE5IE0jF3QEm8T6FjkSOo4OjETUFCYiwJxj7JLAIWMXWnghJapqthZxJOGVnpIFwAeg8/HD/dQIGlk5sqTuygmEPqu+bwfJGrCtUdQTOcF20fGFkAhbiv958AovwXUUfUyzbWVIqSpUv//E8g1Y7XRH14iKqa+B9lrunRRCkdE8NEvgwbXv48KLTzjH5pnhlZVmzvcdbUh86U2U5TeLKlFkZYjUzpEj9bZQa1WkmZNinXbhgOm5nRbjlkfkdDbxCFdkuxjlDZJardOG3ZPEpTf5ETbCtBJ0aMoiW77kSNdR5erHjXaqa/fKqG1Dzt3i2kkyaiiZbZAVTptuvQzYimJhE5RWSSZKeuMI6igVq1Mers2tQYeEYtp2VVtle3VrUQLpBx1F1dNVU5XTxITRRX8T2aM0Fvq5X9E7+TCYeaU085ToeelpK/+4y66VYlyn3qu3I0iCrnmW1sa86Wk1730UD7CKzIH481E6Dd3vfPqEWHsON1uBNh1gzoob5zk/+0ccdtw/qh+lu+6ffZVFkG28Z/Xbi6eOTvOGBgSKguzqszqHgsLnp5WPaWafAeojbJTTa8UuMnKZ3Y4ul92teL6ywx/urUKWhLPKjOc3H5zVeb2MHjrtGdg464F04Gubz+/kIJhSaaZJRIswfuGwdYt6NjkqOZRjWOKjopRHeSgpbneCgU1HDXgGk2gGIB6KF/KoUHxbp7LFv/6O+ZfKn8WqQZ+bXULeDus1Q9j9VABuju13kQg+LOKjj1FU5mOXBS5IzcS2Y2/l0voaG41DE/cG26c7dxMaJlNkDQtIGtZIgt7Cw1dO6i07KFjU8z+rikMKEobG3YDhJF5JAOvyEYukoV9Fw1z3qIyChAdp6F5YtdKoFAEhpTgCBMu3QijDAGfZbtwKGxNR9hruSVYWhVwaQhWgaZgN2fXfCkG4SPYFGsq1rHdcZwFszABD44G9yFcrgAWYoJyhIE7l+G40N/FhVX1JGeGgAdAjmAQJXAIJlipjWAoBgE+ljcYBxXenw7BthZXCayBURWnQWAqoKPD3Dhz7UDG6PmdGLcyvsI6bO5wOBaex4IR4IFFQy+Ca+UFYMLqaeUQDDhnZQOyQv5cUD9WV73z8kbe+x2CDvPCHClyFHlbJJJ3Wo+gG1A+AAA=') format('woff2'),
- url('iconfont.woff?t=1586688927486') format('woff'),
- url('iconfont.ttf?t=1586688927486') format('truetype'), /* Chrome, Firefox, opera, Safari, Android, iOS 4.2+ */
- url('iconfont.svg?t=1586688927486#iconfont') format('svg'); /* iOS 4.1- */
- }
- .iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-OS X-font-smoothing: grayscale;
- }
- .icon-qq:before {
- content: "\e613";
- }
- .icon-weixin:before {
- content: "\e79f";
- }
将 icon.vue 注册到全局
我们在使用 iconfont 的时候, 不可能每个使用到的组件都来手动引入一遍 icon.vue, 因为图标组件算是应用非常广的一个基础公共组件, 我们可以把它注册到全局, 方便使用, 在项目的 main.JS 里面引入并注册它, 注册全局组件的指令是 Vue.component, 它接收两个参数, 第一个是组件名称, 第二个是组件本身. main.JS 代码如下:
- import Vue from "vue";
- import App from "./App.vue";
- import router from "./router";
- import Icon from "./components/icon/icon.vue";
- Vue.config.productionTip = false;
- Vue.component(Icon.name, Icon);
- new Vue({
- router,
- render: h => h(App)
- }).$mount("#app");
使用效果
在任意地方使用如下:
- <template>
- <icon name="icon-weixin" size="40" @click="iconClickHandler" />
- </template>
- <script>
- export default {
- methods: {
- iconClickHandler() {
- console.log("点击了图标");
- }
- }
- };
- </script>
打开浏览器可以看到已经成功显示, 点击图标控制台也有打印
为你的公共组件添加说明文档
在书写公共组件时, 说明文档是非常重要的, 要让别人一看就懂, 实际工作中没有多少人会有兴趣去通过阅读你的源码来看看怎么使用, 这样效率太低了, 书写 README 文档如下:
实际项目怎么跟 UI 配合
在实际项目中, 一般来说一个 iconfont 项目的管理员就是 UI 设计师, 它会不定时将自己设计的图标上传到这个项目里面, 并通知开发者手动下载更新本地的静态资源文件.
来源: https://www.cnblogs.com/zhangnan35/p/12687718.html