SVG 的使用

图片来源: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">
    <!-- Generator: Sketch 51 (57462) - http://www.bohemiancoding.com/sketch -->
    <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 (
    <Svg
      width={props.width || 60}
      height={props.height || 24}
      viewBox='0 0 60 24'>
      <G stroke='none' strokeWidth='1' fill='none' fillRule='evenodd'>
        <Rect fill={fillColor} x='0' y='0' width='60' height='24' rx='12' />
        <G strokeWidth='1' transform='translate(8, 8)' 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' />
        </G>
        <Text fontSize='12' lineSpacing='18' fill='#FFFFFF' x='24' y='16'>
          {props.text || '关注'}
        </Text>
      </G>
    </Svg>
  )
}

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 <Shape {...props}
      d={`M${r},0 h${w} a${r},${r} 0 0,1 ${r},${r} v${h} a${r},${r} 0 0,1 ${-r},${r} h${-w}  a${r},${r} 0 0,1 ${-r},${-r} v${-h} a${r},${r} 0 0,1 ${r},${-r} z`}
    />
  } else {
    return <Shape {...props} d={`h${w} v${h} h${-w} z`} />
  }
}

export default RectART
复制代码

接下来转换按钮

  1. 将 svg 换为 Surface,宽高为显示端宽高,不指定画布大小
  2. Surface 内层写上一层 Group ,添加属性 scale 用于整体缩放,缩放倍数 X 基于画布原始大小来计算
  3. g 转换为 Group, Group 里的属性只支持 fill 和 transform,其他的都写到子标签里去
  4. path 转换为 Shape,d 仍然是路径,复制过来即可(react-art 内置了一套 svg 转换器,源码位于路径 node_modules/art/core/path.js
  5. transform 转换为这种形式,注意 scale 要放在左边,与 css3 相同还支持 rotate
transform = new Transform().scale(2).translate(2, 3)
复制代码
  1. 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 (
    <View style={props.style}>
      <Surface width={props.width || 60} height={props.height || 24} >
        <Group scale={X} >
          <Rect fill={fillColor} r='12' width='60' height='24' />
          <Shape fill='#FFFFFF' transform={new Transform().translate(8, 8)} 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' />
          <Text font={'normal 12 PingFangSC-Regular'} fill='#FFF' transform={new Transform().translate(24, 4)}>
            {props.text || '关注'}
          </Text>
        </Group>
      </Surface>
    </View>
  )
}

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

PropertyTypeDescription
widthstring
heighstring-

Group

PropertyTypeDescription
opacitynumber
scalenumber
scaleXnumber
scaleYnumber
transformtransform
visiblebooleanfalse equals opacity 0

Shape

PropertyTypeDescription
dpath
fillstringColor
opacitynumber
scalenumber
scaleXnumber
scaleYnumber
strokeCapstringbutt, square, round(default)
strokeDash
strokeJoinstringmiter, bevel, round
strokeWidthnumber1(default)
transformtransform
visiblebooleanfalse equals opacity 0

Text

PropertyTypeDescription
alignmentstringright, center, left(default)
fillstringColor
fontstringnormal 10 PingFangSC-Regular
fontobject{fontFamily,fontSize,fontWeight,fontStyle}
opacitynumber
pathpath
scalenumber
scalenumber
scaleXnumber
scaleXnumber
scaleYnumber
scaleYnumber
strokeCapstringbutt, square, round(default)
strokeDash
strokeJoinstringmiter, bevel, round
strokeWidthnumber1(default)
transformtransform-

Transform

PropertyparamsDescription
transformxx, yx, xy, yy, x, y
translatex, y
movex, y
moveTox, y
scalex, y
scaleTox, y
rotatedeg, x, y
rotateTodeg, x, y
resizeTowidth, heightthis.scaleTo(width / w, height / h)
pointx, y
inversePointx, 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 (与前一个字母相同时,字母可以省略)

字母主要有以下几种,可以对应到上面的表格

CommandNameParametersDescription
Mmovetox, y移动
Llinetox, y画线
Hhorizontal linetox画水平线
Vvertical linetoy画垂直线
Aelliptical arc椭圆
Zclosepath连接到起始点,只能在最后使用

椭圆 A 有6个参数 rx ry x-axis-rotation large-arc-flag sweep-flag x y 含义分别为

ParametersDescription
rxx 轴半径
ryy 轴半径
x-axis-rotation相对于 x 轴的旋转角度,例如 30 表示 30 度
large-arc-flag1 绘制大圆,0 绘制小圆
sweep-flag旋转方式: 1 顺时针,0 逆时针
x结束点的 x 坐标
y结束点的 x 坐标

转载于:https://juejin.im/post/5b5ddbe75188251b157bc164

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值