1 概述及使用方法
svg是可缩放矢量图形
使用xml格式定义图像
优点:在放大或者改变尺寸的情况下图形质量不会有所损失 属于w3c标准
注:svg图像必须使用.svg后缀来保存
在html中嵌入svg
1 使用 标签 :pluginspage 属性指向下载插件的 URL。
<embed src="rect.svg" width="300" height="100"
type="image/svg+xml"
pluginspage="http://www.adobe.com/svg/viewer/install/" />
2 使用 标签 codebase 属性指向下载插件的 URL。
<object data="rect.svg" width="300" height="100"
type="image/svg+xml"
codebase="http://www.adobe.com/svg/viewer/install/" />
3 使用 标签
<iframe src="rect.svg" width="300" height="100">
</iframe>
4 目前最新的已经可以使用svg标签了 SVG2
5 工具:
\
2 SVG内置形状
1 预定义的形状
矩形
fill:填充颜色 ;stroke-width:边框宽度 ; strock 边框颜色 ; x:距离左侧位置 ;y:距离顶部位置;
opacity :透明度;rx 和 ry 属性可使矩形产生圆角
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg x="20" y="20" width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<rect width="300" height="100"
style="fill:rgb(0,0,255);stroke-width:1;
stroke:rgb(0,0,0);opacity:0.9"/>
</svg>
注:x和y的坐标是矩形左上角坐标
圆形
cx 和 cy 属性定义圆点的 x 和 y 坐标 默认是(0,0) r 属性定义圆的半径。
<circle cx="100" cy="50" r="40" stroke="black"
stroke-width="2" fill="red"/>
椭圆
cx cy 圆点的x坐标和y坐标;rx:水平半径 ry:垂直半径;
多个椭圆重叠的时候后面的会覆盖前面的
<ellipse cx="300" cy="150" rx="200" ry="80"
style="fill:rgb(200,100,50);
stroke:rgb(0,0,100);stroke-width:2"/>
\
线
- x1在 x 轴定义线条的开始
- y1 在 y 轴定义线条的开始
- x2在 x 轴定义线条的结束
- y2 在 y 轴定义线条的结束
<line x1="0" y1="0" x2="300" y2="300"
style="stroke:rgb(99,99,99);stroke-width:2"/>
折线
使用points定义每个点的坐标 x坐标和y坐标
<polyline points="0,0 0,20 20,20 20,40 40,40 40,60"
style="fill:white;stroke:red;stroke-width:2"/>
多边形
不少于三边的图形: 使用points定义每个角的坐标 下面这个是四边形
<polygon points="220,100 300,210 170,250 123,234"
style="fill:#cccccc;
stroke:#000000;stroke-width:1"/>
路径
最强大的功能
通过d(属性)+参数来实现线的效果 能够划线的只有三个 相对命令可以使用小写
- M = moveto 从当前点直线移动到某个点 两个参数分别是移动后的坐标 注:M只是移动点不划线 M xy m dx dy
- L = lineto 画线 从当前点到参数点位置画线段(直线) L x y (or l dx dy)
两个简写: H x (or h dx);水平移动到x点 V y (or v dy) 垂直划线到y点
- H = horizontal lineto 水平线
- V = vertical lineto 垂直线到
- C = curveto 弯曲
- S = smooth curveto 平滑曲线
- Q = quadratic Belzier curve 二次Belzier曲线
- T = smooth quadratic Belzier curveto 光滑二次Belzier曲线
- A = elliptical Arc 椭圆弧
- Z = closepath 闭合路径(不区分大小写)
<path d="M250 150 L150 350 L350 350 Z" />
实际上是一个三角形
曲线 弧形
图案
patterns 必须在defs内部使用linearGradient
texts
有两种字体,svg字体和写在图案中的字体
基础变形
g元素 里面可以是一个元素集合 形都会用一个元素的transform属性总结
平移: transform="translate(30,40)"
旋转:transform="rotate(45)"
斜切:skewX skewY
缩放:scale()
复杂:matrix
剪切和遮罩
3滤镜 模糊
4 渐变
SVG 渐变必须在 标签中进行定义。 主要有两种渐变:线性渐变和放射性渐变
线性渐变
有两个方向 水平和垂直
水平:
可用来定义 SVG 的线性渐 标签必须嵌套在 的内部。 标签是 definitions 的缩写,它可对诸如渐变之类的特殊元素进行定义。
条件:
y1=y2 ;x1!=x2 水平渐变 ( x不相等就是水平渐变 )
x1=x2;y1!=y2 垂直渐变( y不相等就是垂直渐变 )
x1!=x2 y1!=y2 角行渐变
水平渐变:x值不一样
<defs>
<linearGradient id="orange_red" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);
stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255,0,0);
stop-opacity:1"/>
</linearGradient>
</defs>
<ellipse cx="200" cy="190" rx="85" ry="55"
style="fill:url(#orange_red)"/>
id是唯一值 需要使用fill:url 获取id连接图形于填充
- 标签的 x1、x2、y1、y2 属性可定义渐变的开始和结束位置
- 渐变的颜色范围可由两种或多种颜色组成。每种颜色通过一个 标签来规定。offset 属性用来定义渐变的开始和结束位置。
\
垂直渐变:y轴不一样
<defs>
<linearGradient id="orange_red" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(255,255,0);
stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255,0,0);
stop-opacity:1"/>
</linearGradient>
</defs>
<ellipse cx="200" cy="190" rx="85" ry="55"
style="fill:url(#orange_red)"/>
反射性渐变
用来定义放射性渐变。 标签必须嵌套在 中
cx、cy 和 r 属性定义外圈,而 fx 和 fy 定义内圈 渐变的颜色范围可由两种或多种颜色组成。每种颜色通过一个 标签来规定。offset 属性用来定义渐变的开始和结束位置。
<defs>
<radialGradient id="grey_blue" cx="50%" cy="50%" r="50%"
fx="50%" fy="50%">
<stop offset="0%" style="stop-color:rgb(200,200,200);
stop-opacity:0"/>
<stop offset="100%" style="stop-color:rgb(0,0,255);
stop-opacity:1"/>
</radialGradient>
</defs>
<ellipse cx="230" cy="200" rx="110" ry="100"
style="fill:url(#grey_blue)"/>
\
5 兼容性及使用
1 SVG 2之前,version属性和baseProfile属性用来供其他类型的验证识别SVG的版本。SVG 2不推荐使用version和baseProfile这两个属性
2 作为XML的一种方言,SVG必须正确的绑定命名空间 (在xmlns属性中绑定)。 请阅读命名空间速成 页面获取更多信息。
3 SVG特点是后来居上 后面的会覆盖前面的
4 支持HTML5 的可以直接嵌入SVG
5 如果HTML是XHTML并且声明类型为application/xhtml+xml,可以直接把SVG嵌入到XML源码中。
6 理论上可以使用img 在低于4,0版本的Firefox不支持
7 SVG有两种形式 普通SVG文件是包含SVG标记的简单文本文件
压缩的SVG 后缀是 :.svgz
区别是在上传web文件到服务器时http请求头会多一个 Content-Encoding: gzip 这里可能需要配置一下
Content-Type: image/svg+xml
Vary: Accept-Encoding
\
Content-Type: image/svg+xml
Content-Encoding: gzip
Vary: Accept-Encoding
8 填充颜色大部分使用svg内联样式 也可以使用css
要把background-color、border改成fill和stroke
6 SVG原理
SVG文档中1个像素对应的是输出设备上的一个像素,但是这中情况是可以改变的 这也是SVG为什么可以缩放的原因,
在使用svg的时候可以使用绝对像素和相对像素,也就是说设置宽高的时候是否带单位, 如果不带单位 那么就会使用用户的单位,
在默认情况下一个用户单位等于1像素 (可以改变)
<svg width="100" height="100">
<svg width="200" height="200" viewBox="0 0 100 100">
这里定义的画布尺寸是200200px。但是,viewBox属性定义了画布上可以显示的区域:从(0,0)点开始,100宽100高的区域。这个100100的区域,会放到200200的画布上显示。于是就形成了放大两倍的效果。
用户单位和屏幕单位的映射关系被称为用户坐标系统
坐标系统还可以旋转、倾斜、翻转。
默认的用户坐标系统1用户像素等于设备上的1像素(但是设备上可能会自己定义1像素到底是多大)。在定义了具体尺寸单位的SVG中,比如单位是“cm”或“in”,最终图形会以实际大小的1比1比例呈现。
7 SVG拖拽和缩放
transform属性,操作里面的translate和scale就可以了,就是计算方面要用到一些数学变换的知识。
拖拽
首先:可以实现 但是svg元素不能太多 否则会造成卡顿:
大图svg拖拽性能优化:
失败:
1,纯svg嵌入html,利用transform属性,结果拖拽卡顿
(这种方案的确是没有加will-change以及开启硬件加速,简直愚蠢hh)
2,canvas+svg,利用drawImage,结果绘制太慢(实时绘制,火焰图里面看到drawImage调用时间最长)
3,svg直接作为img的src,结果CPU占用过高,浏览器卡死
will-change :css实验的一个功能,作用是告知浏览器有那些变化 然后让浏览器提前准备
思路:鼠标按下》鼠标移动》鼠标松开 对应的事件分别是mousedown、mousemove、mouseup
把这三个事件注册到svg元素上
遇到问题:
但是当鼠标拖到svg外面时,在svg外面放开了鼠标,鼠标回到svg中,图形会随着鼠标移动,这样是不应该的,所以应该把最后一个事件mouseup注册到最外面的元素上,那么鼠标在svg外放开也可以触发。
注册两个全局变量
data() {
return {
dragging:false,
mousePos:{x:0, y:0},
}
}
鼠标按下事件
mousedown(e) {
const event = window.event || e
this.dragging = true;
this.mousePos.x = event .clientX;
this.mousePos.y = event .clientY;
},
//鼠标按下,准备拖拽,记录当前鼠标位置
鼠标移动事件
mousemove(e) {
const event = window.event || e
if(!this.dragging) {
return ;
}
let nx = event .clientX;
let ny = event .clientY;
//计算偏移坐标
let offsetX = nx - this.mousePos.x;
let offsetY = ny - this.mousePos.y;
//drag方法会把svg删除再重绘
this.drag(offsetX, offsetY);
//继续记录鼠标位置
this.mousePos.x = nx;
this.mousePos.y = ny;
},
最后放开鼠标
mouseup(e) {
this.dragging = false;
},
\
缩放
注意:缩放是根据鼠标滚轮的事件触发,但是鼠标滚轮有他的默认事件,那就是页面滚动,这里要阻止它。
mousewheel(e) {
const event = window.event || e
e.preventDefault();
let newRadio = this.radio;
if(e.deltaY > 0) {
newRadio -= 0.1;
} else {
newRadio += 0.1
}
if(newRadio <= 0.1) {
return;
}
this.radio = newRadio;
this.zoom();
},
\
项目中
本文重点介绍拖拽,单纯实现很简单,但是由于vue项目,机房图有很多事件,拖拽就成了难点
1.获取Svg当前缩放比例--------documen.getElementById("SVG").currentScale
放大:
documen.getElementById("SVG").currentScale = documen.getElementById("SVG").currentScale1.5
**缩小:
**documen.getElementById("SVG").currentScale = documen.getElementById("SVG").currentScale0.5
项目难点:
嵌入vue项目,貌似selectedElement.setAttributeNS(null, "", "");不好使 使用下面方法替换
1.请尝试绑定方法:
selectedElement.addEventListener("mousemove", this.moveElement(event), false);
2.记得执行完解绑:
selectedElement.addEventListener("mouseup", (event) => {
selectedElement.removeEventListener("mousemove", this.moveElement(event), false);
}, false);
卡顿
原因:
设置了transition,发现这样设置过渡动画,一旦元素多了后会异常的卡顿,这是因为改变这些属性时会触发浏览器重排大量耗费性能,而transform既不会触发重排也不会触发重绘,是交给gpu渲染,
解决:
1 第三方库
直接使用现有开源库当然是最方便省事的啦,在github中找了几个start比较高的库:svg-pan-zoom、panzoom
这两个库都是对g元素的transform来实现缩放的,svg-pan-zoom必须传入svg元素并且会使用一个g元素包裹svg内的所有元素; 而panzoom对传入元素没有限制,但只能对传入的指定元素进行移动缩放
然而本方案有个致命缺点,这两个库都会自动删除svg的viewBox属性,意味着没有办法使用svg的viewBox坐标系了,而这两个库提供的pan和zoom方法都是传入基于Dom坐标系的clientX、clientY,这将影响到现有系统的业务功能(视图中移动到svg图中到指定的元素),所以此方案暂时抛弃
最后 在简单的svg中(没有涉及到业务功能的svg)采用这个库
其他的:
2 window.requestAnimationFrame方法
- window.requestAnimationFrame可以让DOM在每一帧中集中处理DOM操作,试试这个神器能不能提高性能?
- 分别在wheelZoom、panning事件中加上requestAnimationFrame,对svg.panzoom.js代码修改如下:
- 拖动时不会卡顿了,但在浏览大图时整体流畅度依然不高(fps低),并且拖动开始和拖动结束的时候会卡顿一下,
方案3(g transform+requestAnimationFrame+拖动结束修改viewBox)
参考方案1的原理,既然transform性能效果这么好,那么能不能既提高拖拽性能,又不删除svg的viewbox属性呢,想到如下方案:在panning时,仅对指定的g元素(可以把svg所有内容都放进去)transform实现拖动、缩放,在panEnd时将transform变化到svg的viewBox上,再配合window.requestAnimationFrame
方案4(g transform+requestAnimationFrame)
参考方案1的原理,使用一个g元素(代理元素)将svg内所有内容包裹起来,所有的拖动缩放都只影响g元素的transform,不去在panEnd时去设置viewBox了,并且不删除viewBox,可以继续使用svg坐标系
本方案与方案3大体一致,区别就是在panEnd时不再去设置svg的viewBox,因为在方案3中浏览大一点的svg图时方案3在panStart、panEnd时会卡顿,所以我以为是设置viewBox导致浏览器重排/重绘影响了性能(实际上并不是),接着往下看