web开发对于svg的简单应用
web开发对于svg的简单应用
@jarringslee
文章目录
- web开发对于svg的简单应用
- 认识svg
- SVG绘制图形
- 基本形状绘制
- text 绘制文本
- path 绘制路径
- g 公共属性标签
- 小案例: 绘制二次贝塞尔曲线模拟静态图
- SVG描边属性
- 模糊和阴影效果
- 线性渐变和径向渐变
- clip-path 裁剪
- JS操作SVG:简单的DOM操作和事件绑定
- 获取并设定属性
- 设置常规样式
- 绑定事件
- 小案例:控制矩形的缩放和移动
- 小案例:环形进度条
认识svg
SVG,可缩放矢量图,是解决网站图标问题的最佳方案,是基于XML语法的一种图像格式
平常使用的图片或者图标一般是基于像素处理的,放大会失真,而SVG是对图片的一种形状描述,本质上是一种文本文件,体积较小,也不会失真。
svg的表现属性(fill、font-size等)可以利用css选择器进行修改,但是几何属性(x、y、cx、cy、rotate等)不能通过css修改,只能写死在svg标签中或利用js获取DOM节点进行修改。
我们不仅利用svg来绘制图标,还用它来创造出更复杂的装饰或图案。
来自于 unDraw - Open source illustrations for any idea 首页
SVG在html的基本格式是:
<svg ......><图形元素a></图形元素a><图形元素b/>......
</svg>
在svg标签中,我们可能会添加到以下常用属性:
属性 | 说明 | 是否必须 | 常用取值示例 |
---|---|---|---|
xmlns | 声明默认命名空间,告诉浏览器“这是 SVG 文档” | 必须(文件独立时) | xmlns="http://www.w3.org/2000/svg" |
xmlns:xlink | 声明 xlink 前缀命名空间,后面可用 <use xlink:href=""> 等 | 用到 xlink 时加 | xmlns:xlink="http://www.w3.org/1999/xlink" |
width / height | 画布外在尺寸;不写时默认 300×150 px | ❌ | width="200" 或 width="100%" |
viewBox | 逻辑坐标系 “minX minY width height”,实现缩放+居中 | 强烈建议写 | viewBox="0 0 24 24" |
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
</svg>
SVG绘制图形
svg类似于行内块元素,可以设置大小、移动位置、动画等,同时有自己的特殊性样式属性
属性 | 描述 | 取值示例 |
---|---|---|
height width | 定义画布的高和宽 | 单位可以不带。默认为像素 |
X Y | 定义画布距离上级元素左侧 上部的值 | 单位可以不带。默认为像素 |
fill | 填充颜色(支持颜色值、渐变、图案)不需要改为none | fill: #foo; |
fill-opacity | 填充颜色的不透明度 | 0(完全透明) ~ 1(完全不透明) |
opacity | 整体图形的不透明度(包括内容和描边) | 0(完全透明) ~ 1(完全不透明) |
rx ry | 边角的圆度值 | 默认像素 |
cx cy r | 定义圆(椭圆)形中心的x、y坐标和半径 | 坐标默认(0,0)单位全都默认像素 |
rx ry | 定义椭圆形的水平半径和垂直半径 | 坐标默认(0,0)单位全都默认像素 |
x1 y1 x2 y2 | 定义直线line的起点坐标和终点坐标 | 单位默认像素 |
points | 定义多边形polygon每个角的坐标、多线条polyline每个拐点的坐标 | 横竖坐标用逗号隔开,每组坐标用空格隔开 |
dx dy | 元素相对于已确认坐标(x,y)的偏移量 | 默认不偏移,默认单位为像素一般用于直接给某个元素旁边添加元素 |
基本形状绘制
基本形状全都可以作为单标签使用。例如:
<rect x="20" y="10" width="180" height="80" rx="10" ry="10" fill="#4fc" fill-opacity="0.6" stroke="#333" stroke-width="4" stroke-dasharray="8,4" transform="rotate(15 110 50)" opacity="0.9" class="myRect"/>
其中,所有属性均可挪到css中编辑(html中写的属性权重最高),下面的单标签和形状标签也可以有该属性。
同一个svg标签中写多个图形可以进行堆叠,堆叠顺序由html书写顺序决定(后来者居上)。
svg所有图形坐标都是根据画布(包裹它的svg标签)来确定的,水平向右为x轴正方向,竖直向下为y轴正方向。如(0,0)在画布的左上角。
以下图形的示例代码均默认已被svg标签
<svg width="200" height="300"> </svg>
包裹。
-
矩形 rect
<rect width="300" height="100" fill="blue" stroke-width="3" stroke="black" />
-
圆形circle
<circle fill="blue" cx="50" cy="100" r="30" />
-
椭圆 ellipse 具有水平和垂直两种半径
<ellipse cx="100" cy="80" rx="50" ry="30" fill="yellow" stroke-width="2" />
-
线条 line fill属性一般情况下无效
<line x1="10" y2="25" x2="60" ry="90" stroke="blue" stroke-width="2" />
-
多边形 polygon 用于创建至少包含三个边的封闭图形
<polygon points="100,10 40,198 198,78 10,78 160,198" fill="yellow" stroke="blue" stroke-width="2" />
-
多线条 polyline 创建具有拐点的折线。这里的fill会默认为黑色,填充区域为始终点连线围城的封闭区域
<polyline points="0,40 40,40 40,80 80,80 80,120 120,120 120,160" fill="none" stroke="blue" stroke-width="4" />
-
路径 path
text 绘制文本
基本标签:text标签(双标签)
<text x="100" y="100" font-size="30" text-anchor="start">我是一段文本</text>
- x y:文本定位坐标
- text-anchor:文本根据坐标的对齐方式(start/middle/end)
相比于p等标签的优点:可以使用svg特有的属性(如旋转)来控制文本。
旋转属性:transform="rotate(旋转角度
旋转点坐标
)"
其中,角度和坐标用空格隔开,坐标间用逗号隔开,旋转点坐标省略默认(0,0)
text标签中可以包含多个相互独立的文本,每个文本用tspan标签包裹,每个tspan标签都可以有独立的属性。
<text><tspan x="10" y="10">111</tspan><tspan x="40" y="50">222</tspan><text>
给text添加超链接属性: 用a标签包裹text标签,链接属性和普通a标签略有不同(因为svg是基于XML的语言),且需要在外层的svg标签添加一个链接。
<svg width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><a xlink:href="https://pvp.qq.com" target="_blank"><text x="10" y="30" transform="rotate(30 20,40)" text-anchor="start">欢迎来到王者荣耀</text></a></svg>
path 绘制路径
基本属性: <path d="M150 0 l75 200">
其中,d为绘制命令(draw),以下单个字母都是包含在d中的常用命令。
最基础的命令:
- M:起点坐标(moveto) 大写字母M后接空格隔开的坐标,代表图形绘制的起点坐标
- L:朝向坐标(lineto) 小写字母l后接空格隔开的坐标,代表上一个结束点(通常是M点)到该点绘制一条直线
- 字母大小写意义不同大写表示绝对定位(根据画布定位),小写表示相对定位(根据上一个结束点,大小写都可以)
- 字母和横坐标之间也可以有一个空格
较为高级的常用绘制命令:
path绘制图形相比于polydgon的优点:可以绘制更加复杂的、包含曲线的矢量图形。
-
q:绘制二次贝塞尔曲线 q后跟两组坐标(全部用空格隔开,q和第一个坐标之间也可以有空格)第一个坐标表示控制点,第二个坐标表示终点(起点为上一个结束点)。
控制点分别和起点、终点相连,可构成绘制曲线的“辅助三角形”:
- 曲线始终从起点出发,向控制点方向拉伸,但不会经过控制点(除非退化成直线)。
- 曲线在起点处与 起点→控制点 线段相切;在终点处与 控制点→终点 线段相切。
- 控制点离曲线越远,曲线被“拉”得越弯;三点共线时,曲线退化成一条直线。
g 公共属性标签
在svg中可以用g标签把一些具有相同属性的标签包裹起来,在g标签上定义公共的属性。
<g><path /><circle /><text> </text>
</g>
g标签只能放置fill、stroke、font-size等属性这些通用属性支持继承,不能放置某些标签特有的属性如偏移量dx、定位属性等。
小案例: 绘制二次贝塞尔曲线模拟静态图
模拟贝塞尔曲线的点位和辅助线
- 绘制起点、控制点和终点并利用偏移量标注各点字母;
- 两条线段分别连接起点和控制点、控制点和终点,且两条线段上有一条切线;
- 根据三点绘制贝塞尔曲线本体。
<svg width="450" height="400"><!-- 把点位绘制放在最后的原因: 让点不被线段压住 方便观察--><!--4. 绘制AB、BC两条线段 --><g stroke="red" stroke-width="3" fill="none"><path d="M100 350 l150 -300" /><path d="M250 50 l150 300" /></g><!--5. 绘制连接AB与BC的曲线切线 --><path d="M175 200 l150 0" stroke="green" stroke-width="3" fill="none" /><!-- 6. 绘制贝塞尔曲线 --><path d="M100 350 q150 -300 300 0" stroke="blue" stroke-width="5" fill="none" /><!-- 2. 绘制出三个点 --><g fill="darkblue"><circle cx="100" cy="350" r="3" /><circle cx="250" cy="50" r="3" /><circle cx="400" cy="350" r="3" /></g><!-- 3. 利用偏移量绘制三个点的名称 --><g dx="-30" font-size="25"><text x="100" y="350" dx="-30">A</text><text x="250" y="50" dx="-30">B</text><text x="400" y="350" dx="-30">C</text></g></svg>
SVG描边属性
基本属性:
属性 | 描述 | 取值示例 |
---|---|---|
stroke 笔画属性 | 定义描边颜色(支持颜色值、渐变、图案) | stroke:#fo0; |
stroke-width 笔画宽度属性 | 描边宽度(支持像素、百分比、px、em等单位) | stroke-width: 2px; |
stroke-linecap 笔画笔帽属性 | 定义描边线段两边的线头形状 | butt(无线帽)、round(圆形线帽)、square(方形线帽) |
stroke-dasharray | 虚线模式(实线长度+间隔长度) | stroke-dasharray: 10 ; |
stroke-dashoffset | 调整虚线与间隔的起始位置 | stroke-dashoffset: 100; |
stroke-opacity | 描边颜色的不透明度 | 0(完全透明) ~ 1(完全不透明) |
stroke-dasharray 虚线笔画属性可以定义一个或多个数字 调整虚线属性,实现和虚线的间隔值。
-
stroke-dasharray:100;
默认px,实现和虚线间隔都是100 -
stroke-dasharray:100 50;
100px是实线长度,50px是实现间隔(虚线长度) -
stroke-dasharray:100 50 5 10 10 5;
分别为实线和虚线交替的长度
stroke-dasharray的应用 鼠标经过添加描边动画效果
- 使用@keyframes创造动画效果:设置两个(0% 100%)或多个关键帧的不同虚线模式的数值
- 使用:hover为svg添加该动画效果。
stroke-dashoffset 用于调整虚线模式的起始偏移量。增大该属性能让虚线模式向路径起点移动,视觉上像线条被擦除。
stroke-dashoffset的应用 鼠标经过添加描边动画效果 做法同上
模糊和阴影效果
SVG效果标签都要包含在defs标签中
给svg添加特殊效果的基本标签:defs嵌套filter 一个filter元素可包含一个或多个效果滤镜
filter有一个必要的id属性用于识别过滤器,图形通过id指向要使用的过滤器
图形中用filter="url(# )"来声明id,id属性的值要保持唯一性
x、y为滤镜的起始点坐标
<defs><filter id="f111" x="" y=""></filter>
</defs><rect filter="url(#f111)"/>
模糊效果: feGaussianblur 高斯模糊效果(单标签),利用stdDeviation属性定义模糊的数值(数值越高模糊效果越明显)
阴影效果: feOffset feBlend 其本质为将图像根据原位置偏移作为阴影
<filter><feOffset dx="" dy="" in="" /><feBlend in="" />
</filter>
feOffset 阴影偏移
- dx dy 表示根据原位置偏移的量,单位为像素
- in 表示阴影图像来源,通常有两个值
- SourceAlpha 原图像的黑色形状作为阴影
- SourceGraphic 原始图像作为阴影
feBlend 在偏移图像上方混合原始图像
in属性一般为原始图像SourceGraphic
线性渐变和径向渐变
线性渐变 linearGradient 可实现水平渐变、垂直渐变或角度渐变等从某点向另一点的线性渐变。
同样也需要id属性。
<defs><linearGradient x1="" y1="" x2="" y2="" id=""><stop offset="10%" stop-color="yellow"/><stop offset="" stop-color=""/><stop offset="" stop-color=""/></linearGradient>
</defs>
- x1 y1 x2 y2表示渐变的起点坐标和终点坐标。
- x1=x2 y1≠y2 产生垂直渐变
- x1≠x2 y1=y2 产生水平渐变
- x1≠x2 y1≠y2 产生角度渐变
- linearGradient标签中需要嵌套一个或多个stop标签来表示u原色
- offset一般为百分数,表示该颜色所在渐变轨迹中的相对位置(0%为起点处,100%为终点处)
- stop-color为一个颜色值
径向渐变 radialGradiet 可实现某个点由内向外圆形渐变的效果
<defs><radialGradient id="" cx="" cy="" r="" fx="" fy=""> <stop offset="10%" stop-color="yellow"/><stop offset="" stop-color=""/><stop offset="" stop-color=""/></radialGradient>
</defs>
其中,cx cy r属性定义了外层圆的基本特征,fx fy定义了内层圆的基本特征。
clip-path 裁剪
创建复杂的裁剪形状,使元素仅显示被裁剪区域内的部分
语法: clip-path: 内置几何形
内置几何形如: circle()、polygon()等
.circle {clip-path:circle(40% at 50% 50%)
}
可视化工具: https://tools.jb51.net/static/api/css3path/index.h
相关图性参数在里面查阅:选中并调节好所需图形,粘贴下面的代码到目标盒子的选择器即可。
JS操作SVG:简单的DOM操作和事件绑定
获取并设定属性
对于常用的svg标签,基本的svg属性都会写死在html标签中,一般不会用css选择器进行操作,此时若想要设置或修改svg属性,就需要用到js来获取DOM节点并调用。
获取DOM节点
一般我们给svg标签设置 class、id或**自定义id **来方便我们使用js调用。均使用常规的文档对象调用方式 document.querySelector 即可。
我们想操作下面矩形的svg属性:
<svg width="350" height="300"><rect x="50" y="50" width="100" height="75" class="rect" id="r111" data-ljlid="myrect" stroke="red" fill="none"></rect>
</svg>
只需通过以上任一方式获取该矩形svg的DOM节点即可:
-
class属性
var rect1 = document.querySelector('.rect')
-
id属性
var rect1 = document.querySelector('#r111')
-
自定义id属性
var rect1 = document.querySelector('[data-ljlid = "myrect"]')
设置属性
我们利用 getAttribute 来获取标签内的属性。获取到的数据一般为字符串形式,所以在获取数字类型的值时,需要提前加上 parseFloat。
var width = parseFloat(rect.getAttribute("width"))
var height = parseFloat(rect.getAttribute("height"))
我们获取到了矩形的DOM节点和想要操作的属性值后,可以直接使用 setAttribute 来修改该属性的值。
节点名.setAttribute("属性名", 属性值)
对于属性值,如果时数字类型变量则需添加引号,纯使用已声明的变量进行算术运算则不用。此外,也支持字符串拼接、反引号等操作。
rect.setAttribute("width", width *= 1.1)
var red = 200
var green = 50
var blue =0
rect.setAttribute('fill', `rgb(${red}, ${green}, ${blue})`)
设置常规样式
不仅css可以调用的font-size、display、visibility等基本属性还有以下svg样式属性也可以利用 节点名.style.
直接调用
- fill
- opacity
- stroke
注意一些属性名有连接符的,在调用时应该改成驼峰命名法。
- strokeWidth strokeLinecap
- fillOpacity fillRule
像 transform x y rx ry points 等几何属性则只能用 setAttribute 读写。
rect.style.fill = "aqua"
绑定事件
对于svg的一些简单页面交互,只需绑定相应事件即可。
如最常见的设置按钮并绑定点击事件:
<button class="btn1">我是按钮</button><script>var btn1 = document.querySelector('.btn1')btn1.onclick = function (){// 添加svg操作}</script>
onclick
写法简单,但换成 addEventListener('click', ...)
更通用,可多次绑定。
另外,也可以在其他函数如计时函数中操作svg属性:
var move = setInterval(function () {// 添加svg操作// 也可设置清除计时器函数if (......) {clearInterval(move)}}, 20)}
我们经常利用 setInterval 每一定毫秒执行像素级别移动、收缩等svg任务的特性来创造动画效果。
后面可用 requestAnimationFrame
替代 setInterval
,刷新率同步,动画更顺滑。
小案例:控制矩形的缩放和移动
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>svg {overflow: visible;outline: 1px dashed gray;}</style>
</head><body><svg width="350" height="300"><rect x="50" y="50" width="100" height="75" class="rect" fill="aqua"></rect></svg><br><button class="btn1">放大矩形</button><button class="btn2">移动矩形</button><button class="btn3">动画移动</button><script>var btn1 = document.querySelector('.btn1')var btn2 = document.querySelector('.btn2')var btn3 = document.querySelector('.btn3')var rect = document.querySelector('.rect')btn1.onclick = function () {console.log(rect)var width = parseFloat(rect.getAttribute("width"))var height = parseFloat(rect.getAttribute("height"))rect.style.fill = "orange"if (width > 400) {width = 100, height = 75rect.style.fill = "aqua"}rect.setAttribute("width", width *= 1.1)rect.setAttribute("height", height *= 1.1)}btn2.onclick = function () {console.log(rect)var x = parseFloat(rect.getAttribute("x"))var y = parseFloat(rect.getAttribute("y"))rect.style.fill = "pink"if (x > 230) {x = 5, y = 5rect.style.fill = "aqua"}rect.setAttribute("x", x += 10)rect.setAttribute("y", y += 10)}btn3.onclick = function () {console.log(rect)var x = parseFloat(rect.getAttribute("x"))var y = parseFloat(rect.getAttribute("y"))// var position = 2var move = setInterval(function () {// position += 2;rect.setAttribute("x", x++)rect.setAttribute("y", y++)if (x > 250) {clearInterval(move)rect.setAttribute("x", 50)rect.setAttribute("y", 50)}}, 20)}</script>
</body></html>
小案例:环形进度条
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>循环进度条</title><style>.text {/* 文字水平居中 */text-anchor: middle;dominant-baseline: middle;}body {text-align: center;}</style>
</head><body><svg xmlns="http://www.w3.org/2000/svg" height="700" width="700"><!-- 设置底色圆环 --><circle cx="350" cy="350" r="300" fill="none" stroke="grey" stroke-width="40" stroke-linecup="round"></circle><!-- 设置进度条 transform旋转:让圆环起点在正上方--><circle class="progress" transform="rotate(-90, 350, 350)" cx="350" cy="350" r="300" fill="none" stroke="blue"stroke-width="40" stroke-linecap="round" stroke-dasharray="0,10000" /><!-- 设置文本 --><text class="text" x="350" y="350" font-size="200" fill="red">36</text></svg><script>// 获取进度圆环对象var progressDom = document.querySelector('.progress')// 获取文本对象var textDom = document.querySelector('.text')function rotateCircle(persent) {// 获取圆环总长:通过半径计算var circleLength = Math.floor(2 * Math.PI * parseFloat(progressDom.getAttribute("r")))// 按照百分比算出圆环当前长度var value = persent * circleLength / 100// 配置颜色变化// 红色rgb:255,0,0// 蓝色rgb:0,191,255var red = 255 + parseInt((0 - 255) / 100 * persent)var green = 0 + parseInt((191 - 0) / 100 * persent)var blue = 0 + parseInt((255 - 0) / 100 * persent)// 设置dark-dasharray和路径颜色progressDom.setAttribute("stroke-dasharray", value + ', 10000')progressDom.setAttribute("stroke", `rgb(${red}, ${green}, ${blue})`)// 填充文本并设置颜色textDom.innerHTML = persent + '%'textDom.setAttribute('fill', `rgb(${red}, ${green}, ${blue})`)}// 设置动画:每间隔30ms百分比+1let num = 0setInterval(() => {num++if (num > 100) {num = 0}rotateCircle(num)}, 60)</script>
</body></html>