图片来源:pixiv 38190692
SVG 图标是矢量图,可以很方便的转换颜色,修改文字,画质无损进行缩放,而且文件非常小,相比同样的 png 图片,大小仅为一成
SVG 通过可配置颜色和配置文字的方式,有效减少了图标文件的个数。从而使设计师和前端工程师工作量都减少,还能提升了加载速度
更新于 2018.7.29
首发于夏味的博客: xiaweiss.com
介绍
说到性能优化,常见的一种方式就是压缩图片,通常图片最多压缩到原文件的 50%。而且如果一个图标有 7 种颜色呢?那么设计就得给出 7 种颜色的图片,前端代码也得引用不同的 7 个路径。。。想想都觉得复杂。
SVG 图完美地解决了这一痛点,大小仅为原文件 10% 左右。还可以通过改写代码来任意改变颜色,甚至支持在代码中动态地传入颜色。动态地传入颜色时,即便有 1万种颜色,也仅仅一个文件。
而生成 SVG 文件也非常简单,设计师可以使用 矢量图 绘图软件直接导出 svg 格式的文件,如 Adobe Illustrator(简称 AI)、sketch
当然对于 Adobe Photoshop 中绘制的图片,手工转换矢量图比较困难耗时,在线工具解决了这一难题
其中目前免费在线转换工具里,这款是最好的 https://www.vectorizer.io/
转换后,可能会有一些冗余的代码,可以使用命令行工具 svgo 进行批量压缩。当然如果文件数量不多,直接使用在线工具 https://jakearchibald.github.io/svgomg/ 即可。
这些个 SVG 压缩工具只是静态工具,不会被上传到网络上去,不需要担心被盗图,有兴趣的同学可以研究下源码,以及那个web 压缩工具的源码
SVG 图标示例
首先 svg 在浏览器里和手机上,与图片一样的,可以正常显示出来 下面这个就是一个完整的 SVG 图,包括文字
xiaweiss.com/images/2018…
下面将它改写为为 400px 宽度的 SVG 图,仍然可以看到很清晰
xiaweiss.com/images/2018…
接下来,使用微信屏幕截图,看看同样 400px 宽度的 png 图,并且使用智图 压缩图片
接下来看看这三个文件的大小
可以看出 SVG 图可以任意改变尺寸,不损失清晰度 相比 png 图体积小很多的情况下,仍然比 png 图更清晰
接下来使用代码编辑器打开 SVG 文件,可以看到如下代码
也许看到这一堆代码要头晕了。别担心,实际应用时,并不需要自己手写 SVG 代码,只是改改就足够了 仔细看很类似前端常用的 html 标签
xml version="1.0" encoding="UTF-8"?>
<svg width="60px" height="24px" viewBox="0 0 60 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>按钮/关注-红色底title>
<desc>Created with Sketch.desc>
<defs>defs>
<g id="按钮/关注-红色底" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle-3" fill="#FF4C6A" x="0" y="0" width="60" height="24" rx="12">rect>
<g id="icon/关注加号" transform="translate(3.000000, 0.000000)">
<g id="Group" stroke-width="1" transform="translate(8.000000, 8.000000)" fill="#FFFFFF">
<path d="M3,3 L3,1 C3,0.44771525 3.44771525,1.01453063e-16 4,0 C4.55228475,-1.01453063e-16 5,0.44771525 5,1 L5,3 L7,3 C7.55228475,3 8,3.44771525 8,4 C8,4.55228475 7.55228475,5 7,5 L5,5 L5,7 C5,7.55228475 4.55228475,8 4,8 C3.44771525,8 3,7.55228475 3,7 L3,5 L1,5 C0.44771525,5 6.76353751e-17,4.55228475 0,4 C-6.76353751e-17,3.44771525 0.44771525,3 1,3 L3,3 Z" id="Combined-Shape">path>
g>
<rect id="Rectangle-10" fill="#D8D8D8" opacity="0" x="0" y="0" width="24" height="24">rect>
g>
<text id="哈哈" font-family="PingFangSC-Regular, PingFang SC" font-size="12" font-weight="normal" line-spacing="18" fill="#FFFFFF">
<tspan x="24" y="16">哈哈tspan>
text>
g>
svg>
复制代码
SVG 代码说明
<svg width="60px" height="24px" viewBox="0 0 60 24" ... > ... svg>
复制代码
svg 标签来控制宽高,width,height 是实际显示的宽高,可以修改为你想要显示的大小 而 viewBox 里的大小,则是原始大小,可以理解为画纸的大小位置,其中左上角坐标为 0 0,右下角坐标为 60 24(方向分别为 x, y)
<g id="Group" stroke-width="1" transform="translate(8.000000, 8.000000)" fill="#FFFFFF"> ... g>
复制代码
g 标签表示分组,也就是绘图软件中的图层。类似代码中的继承,它的属性,如果子标签里没有规定,就会使用它的属性设置 例如下面这两段代码是同样的效果
<g fill="#FF4C6A">
<rect x="0" y="0" width="60" height="24" rx="12">rect>
g>
复制代码
<g >
<rect fill="#FF4C6A" x="0" y="0" width="60" height="24" rx="12">rect>
g>
复制代码
path 表示线条, reat 表示方形,text 表示文字 代码 d="M3,3 L3,1 ...'
是绘制线条的代码,也称作路径(path)
fill 表示填充色,类似于 css 里的背景色(background-color)
stroke 表示描边,类似于 css 的边框颜色(border-color)
所以修改色值时,只需要修改这两个颜色即可 色值与 css 相同,可以使用透明色 transparent,以及 rgba(0,0,0,0.5) 也可以添加属性 opacity='0.5'
来控制透明度,值为 0 ~ 1
至于修改文字,找到对应的文字,直接替换即可
动态渲染 SVG
由于最近正在做 react-native, SVG 的配置难度较大,就用它来示例一下
这里使用 react-native-svg 库来渲染 SVG 图片
注意设计师导出 SVG 图标前,请清除掉蒙层(mask)、颜色叠加和滤镜(filter)、阴影(shadow),目前 react-native 是不支持的
首先使用 msvgc 库来一键把 SVG 文件转换为 React 组件
nodejs 环境里安装 msvgc,源文件放置到 App/Svg/目录下,配置脚本运行即可 导出的组件位于 ./App/Components/Svg/svg
目录
"scripts": {
"svg": "msvgc --react-native -f ./App/Svg/ -o ./App/Components/Svg && standard --fix './App/Components/Svg/svg/*.js'"
}
复制代码
再把修正属性的语法,全部改为驼峰写法,例如 fill-rule
改为 fillRule
修正后将文件移动到 App/Components/Svg
(其他目录也昆虫,因为每次新转换时,会覆盖App/Components/Svg/svg/
目录)
接下来,进一步修改代码,就可以通过组件的 props 值里动态传参了
import React from 'react'
import Svg, { G, Rect, Path, Text } from 'react-native-svg'
import { path } from 'ramda' // 根据键名取值,取不到或错误时,返回 undefined
// path([键名],被取值的对象)
const ButtonFollow = props => {
const Color = {
red: '#ff4c6a',
grey: '#6c6c6c'
}
const fillColor = path([props.color], Color) || props.color || Color.red
return (
)
}
export default ButtonFollow
复制代码
调用
<ButtonFollow width={300} height={120} color='red' />
<ButtonFollow /> 取默认值 64,24,默认色 red
<ButtonFollow width={300} height={120} color='#ccc' /> 任意颜色
复制代码
react-art
(基于 react-naive 0.55.4 介绍)
facebook 自家也出品了一个库 react-art,并且有 react-native-art,二者大体上使用的同一套 API 它支持的元素标签交少,例如没有方形。但更简洁,不需要引入额外库 (react-native-svg 压缩后大约 200KB)
相关语法可以看 react-native-art-绘图入门 了解一下 源码位于 node_modules/react-native/Libraries/ART/ReactNativeART.js
缺点是还没有找到现成的转换插件,需要手工转换
首先来封装一个方形组件,width、height 控制宽高,r 控制圆角半径(border-radius)
import React from 'react'
import { ART } from 'react-native'
const { Shape } = ART
function extractNumber (value, defaultValue) {
if (value == null) {
return defaultValue
}
return +value
}
const RectART = props => {
let w = extractNumber(props.width, 0)
let h = extractNumber(props.height, 0)
let r = extractNumber(props.r, 0)
if (w === 0 || h === 0) return null
if (r > 0) {
h -= r * 2
w -= r * 2
return
} else {
return
}
}
export default RectART
复制代码
接下来转换按钮
- 将 svg 换为 Surface,宽高为显示端宽高,不指定画布大小
- Surface 内层写上一层 Group ,添加属性 scale 用于整体缩放,缩放倍数 X 基于画布原始大小来计算
- g 转换为 Group, Group 里的属性只支持 fill 和 transform,其他的都写到子标签里去
- path 转换为 Shape,d 仍然是路径,复制过来即可(react-art 内置了一套 svg 转换器,源码位于路径
node_modules/art/core/path.js
) - transform 转换为这种形式,注意 scale 要放在左边,与 css3 相同还支持 rotate
transform = new Transform().scale(2).translate(2, 3)
复制代码
- text 转换为 Text,直接包裹文字即可,
其中 font 指定字体格式(注意必须指定字体) (react-native 里指定多个字体只有第一个字体生效)
font='normal 12 PingFangSC-Regular'
复制代码
也可以写为(注意必须指定字体)
font={fontFamily: 'PingFangSC-Regular',fontSize: '12'}
复制代码
还有两个属性是 fontWeight、fontStyle
import React from 'react'
import { path } from 'ramda' // 根据键名取值,取不到或错误时,返回 undefined
// path([键名],被取值的对象)
import { ART, View } from 'react-native'
import Rect from './RectART.js'
const { Group, Shape, Surface, Transform, Text } = ART
const ButtonFollow = props => {
const Color = {
red: '#ff4c6a',
grey: '#6c6c6c'
}
const fillColor = path([props.color], Color) || props.color || Color.red
const X = (Math.min(props.width / 60, props.height / 24)) || 1
return (
{props.text || '关注'}
)
}
export default ButtonFollow
复制代码
<ButtonFollow width={300} height={120} />
复制代码
react-native-art API
(基于 react-naive 0.55.4 介绍) 下面是我看了源码之后罗列的 API,供参考使用
相关语法可以看 react-native-art-绘图入门 了解一下 源码位于 node_modules/react-native/Libraries/ART/ReactNativeART.js
Surface
Property | Type | Description |
---|---|---|
width | string | |
heigh | string | - |
Group
Property | Type | Description |
---|---|---|
opacity | number | |
scale | number | |
scaleX | number | |
scaleY | number | |
transform | transform | |
visible | boolean | false equals opacity 0 |
Shape
Property | Type | Description |
---|---|---|
d | path | |
fill | string | Color |
opacity | number | |
scale | number | |
scaleX | number | |
scaleY | number | |
strokeCap | string | butt, square, round(default) |
strokeDash | ||
strokeJoin | string | miter, bevel, round |
strokeWidth | number | 1(default) |
transform | transform | |
visible | boolean | false equals opacity 0 |
Text
Property | Type | Description |
---|---|---|
alignment | string | right, center, left(default) |
fill | string | Color |
font | string | normal 10 PingFangSC-Regular |
font | object | {fontFamily,fontSize,fontWeight,fontStyle} |
opacity | number | |
path | path | |
scale | number | |
scale | number | |
scaleX | number | |
scaleX | number | |
scaleY | number | |
scaleY | number | |
strokeCap | string | butt, square, round(default) |
strokeDash | ||
strokeJoin | string | miter, bevel, round |
strokeWidth | number | 1(default) |
transform | transform | - |
Transform
Property | params | Description |
---|---|---|
transform | xx, yx, xy, yy, x, y | |
translate | x, y | |
move | x, y | |
moveTo | x, y | |
scale | x, y | |
scaleTo | x, y | |
rotate | deg, x, y | |
rotateTo | deg, x, y | |
resizeTo | width, height | this.scaleTo(width / w, height / h) |
point | x, y | |
inversePoint | x, y | - |
上面表格含 To 的都是绝对坐标,例如 move 是相对坐标,moveTo 是绝对坐标
其他存在但不常用的 API Path ClippingRectangle LinearGradient Pattern RadialGradient
SVG path 说明
具体可以看 w3c 官方文档 https://www.w3.org/TR/SVG2/paths.html 大写字母都代表绝对坐标 小写字母都代表相对坐标
下面这几个写法效果相同 M 10 20 M 15 25 v 5 M 10,20 M 15,25 v 5 M10,20 M15,25 v5 M10,20 15,25 v5 (与前一个字母相同时,字母可以省略)
字母主要有以下几种,可以对应到上面的表格
Command | Name | Parameters | Description |
---|---|---|---|
M | moveto | x, y | 移动 |
L | lineto | x, y | 画线 |
H | horizontal lineto | x | 画水平线 |
V | vertical lineto | y | 画垂直线 |
A | elliptical arc | 椭圆 | |
Z | closepath | 连接到起始点,只能在最后使用 |
椭圆 A 有6个参数 rx ry x-axis-rotation large-arc-flag sweep-flag x y 含义分别为
Parameters | Description |
---|---|
rx | x 轴半径 |
ry | y 轴半径 |
x-axis-rotation | 相对于 x 轴的旋转角度,例如 30 表示 30 度 |
large-arc-flag | 1 绘制大圆,0 绘制小圆 |
sweep-flag | 旋转方式: 1 顺时针,0 逆时针 |
x | 结束点的 x 坐标 |
y | 结束点的 x 坐标 |