# SVG
使用 XML 描述的矢量文件
# svg基础图形
http://www.w3.org/2000/svg 是一个命名空间URI,它代表了SVG(Scalable Vector Graphics)的标准。
在SVG文件中看到这个命名空间URI时,它通常与xmlns属性一起使用,以声明SVG元素所属的命名空间。
<svg xmlns="http://www.w3.org/2000/svg">
<rect
x="10"
y="10"
rx="5"
ry="5"
width="150"
height="100"
stroke="red"
fill="none">
</rect>
<circle
cx="250"
cy="60"
r="50"
stroke="red"
fill="none">
</circle>
<ellipse
cx="400"
cy="60"
rx="70"
ry="50"
stroke="red"
fill="none">
</ellipse>
<line
x1="10"
y1="120"
x2="160"
y2="220"
stroke="red">
</line>
<polyline
points="250 120
300 220
200 220"
stroke="red"
fill="none">
</polyline>
<polygon
points="250 120
300 220
200 220"
stroke="red"
stroke-width="5"
fill="yellow"
transform="translate(150 0)">
</polygon>
</svg>
# svg编辑器
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SVG 编辑器</title>
<style>
#toolbox {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 250px;
border-right: 1px solid #CCC;
}
#toolbox h2 {
margin: 0;
padding: 0;
background: #EEE;
font-size: 16px;
height: 24px;
line-height: 24px;
padding: 5px 10px;
}
#toolbox form {
padding: 10px;
}
#canvas {
position: absolute;
left: 260px;
top: 10px;
bottom: 10px;
right: 10px;
box-shadow: 2px 2px 10px rgba(0,0,0,.4);
border-radius: 5px;
}
label {
display: inline-block;
width: 80px;
text-align: right;
}
</style>
</head>
<body>
<div id="toolbox">
<h2>创建</h2>
<form id="create-shape">
<button type="button" create="rect">Rect</button>
<button type="button" create="circle">Circle</button>
<button type="button" create="ellipse">Ellipse</button>
<button type="button" create="line">Line</button>
</form>
<h2>形状</h2>
<form id="shape-attrs">
请先创建图形
</form>
<h2>外观和变换</h2>
<form id="look-and-transform" disabled="disabled">
<p>
<label style="display: inline;">填充</label>
<input id="fill" type="color" value="#ffffff" />
</p>
<p>
<label style="display: inline;">描边</label>
<input id="stroke" type="color" value="#ff0000" />
<input id="strokeWidth" type="range" value="1" />
</p>
<p>
<label>translateX</label>
<input id="translateX" type="range" min="-400" max="400" value="0" />
<label>translateY</label>
<input id="translateY" type="range" min="-400" max="400" value="0" />
<label>rotate</label>
<input id="rotate" type="range" min="-180" max="180" value="0" />
<label>scale</label>
<input id="scale" type="range" min="-1" max="2" step="0.01" value="1" />
</p>
</form>
</div>
<div id="canvas"></div>
</body>
<script>
var SVG_NS = 'http://www.w3.org/2000/svg';
// 图形及对应默认属性
var shapeInfo = {
rect: 'x:10,y:10,width:200,height:100,rx:0,ry:0',
circle: 'cx:200,cy:200,r:50',
ellipse: 'cx:200,cy:200,rx:80,ry:30',
line: 'x1:10,y1:10,x2:100,y2:100'
};
// 默认公共属性
var defaultAttrs = {
fill: '#ffffff',
stroke: '#ff0000'
};
var createForm = document.getElementById('create-shape');
var attrForm = document.getElementById('shape-attrs');
var lookForm = document.getElementById('look-and-transform');
var svg = createSVG();
var selected = null;
createForm.addEventListener('click', function(e) {
if (e.target.tagName.toLowerCase() == 'button') {
create(e.target.getAttribute('create'));
}
});
attrForm.addEventListener('input', function(e) {
if (e.target.tagName.toLowerCase() != 'input') return;
var handle = e.target;
selected.setAttribute(handle.name, handle.value);
});
lookForm.addEventListener('input', function(e) {
if (e.target.tagName.toLowerCase() != 'input') return;
if (!selected) return;
selected.setAttribute('fill', fill.value);
selected.setAttribute('stroke', stroke.value);
selected.setAttribute('stroke-width', strokeWidth.value);
selected.setAttribute('transform', encodeTranform({
tx: translateX.value,
ty: translateY.value,
scale: scale.value,
rotate: rotate.value
}));
});
function createSVG() {
var svg = document.createElementNS(SVG_NS, 'svg');
svg.setAttribute('width', '100%');
svg.setAttribute('height', '100%');
canvas.appendChild(svg);
svg.addEventListener('click', function(e) {
if (e.target.tagName.toLowerCase() in shapeInfo) {
select(e.target);
}
});
return svg;
}
function create(name) {
var shape = document.createElementNS(SVG_NS, name);
svg.appendChild(shape);
select(shape);
}
function select(shape) {
var attrs = shapeInfo[shape.tagName].split(',');
var attr, name, value;
attrForm.innerHTML = "";
while(attrs.length) {
attr = attrs.shift().split(':');
name = attr[0];
value = shape.getAttribute(name) || attr[1];
createHandle(shape, name, value);
shape.setAttribute(name, value);
}
for (name in defaultAttrs) {
value = shape.getAttribute(name) || defaultAttrs[name];
shape.setAttribute(name, value);
}
selected = shape;
updateLookHandle();
}
function createHandle(shape, name, value) {
var label = document.createElement('label');
label.textContent = name;
var handle = document.createElement('input');
handle.setAttribute('name', name);
handle.setAttribute('type', 'range');
handle.setAttribute('value', value);
handle.setAttribute('min', 0);
handle.setAttribute('max', 800);
attrForm.appendChild(label);
attrForm.appendChild(handle);
}
function updateLookHandle() {
fill.value = selected.getAttribute('fill');
stroke.value = selected.getAttribute('stroke');
var t = decodeTransform(selected.getAttribute('transform'));
translateX.value = t ? t.tx : 0;
translateY.value = t ? t.ty : 0;
rotate.value = t ? t.rotate : 0;
scale.value = t ? t.scale : 1;
}
function decodeTransform(transString) {
var match = /translate\((\d+),(\d+)\)\srotate\((\d+)\)\sscale\((\d+)\)/.exec(transString);
return match ? {
tx: +match[1],
ty: +match[2],
rotate: +match[3],
scale: +match[4]
} : null;
}
function encodeTranform(transObject) {
return ['translate(', transObject.tx, ',', transObject.ty, ') ',
'rotate(', transObject.rotate, ') ',
'scale(', transObject.scale, ')'].join('');
}
</script>
</html>
# viewbox调试
viewBox属性定义了SVG元素内部的坐标系统。
它由四个参数组成:min-x, min-y, width, 和 height。
<!DOCTYPE html>
<html>
<head>
<title>ViewBox 使用演示</title>
<style>
body {
background: #eee;
}
svg {
position: absolute;
border: 1px solid green;
width: 300px;
height: 200px;
left: 50%;
top: 50%;
margin-top: -100px;
margin-left: -150px;
background: white;
}
input[type=number] {
width: 50px;
}
</style>
</head>
<body>
<h1>ViewBox 演示</h1>
<form id="form">
<fieldset>
<legend>viewBox</legend>
<label>x: <input id="vx" type="number" value="0"></label>
<label>y: <input id="vy" type="number" value="0"></label>
<label>width: <input id="vw" type="number" value="300"></label>
<label>height: <input id="vh" type="number" value="200"></label>
</fieldset>
<fieldset>
<legend>preserveAspectRatio</legend>
<label>align: <select id="align">
<option value="none">none</option>
<option value="xMinYMin">xMinYMin</option>
<option value="xMidYMin">xMidYMin</option>
<option value="xMaxYMin">xMaxYMin</option>
<option value="xMinYMid">xMinYMid</option>
<option value="xMidYMid" selected>xMidYMid</option>
<option value="xMaxYMid">xMaxYMid</option>
<option value="xMinYMax">xMinYMax</option>
<option value="xMidYMax">xMidYMax</option>
<option value="xMaxYMax">xMaxYMax</option>
</select></label>
<label>meetOrSlice: <select id="meetOrSlice">
<option value="meet">meet</option>
<option value="slice">slice</option>
</select></label>
</fieldset>
</form>
<p>
<svg id="svg" xmlns="http://www.w3.org/2000/svg">
<!--Face-->
<circle cx="100" cy="100" r="90" fill="#39F" />
<!--Eyes-->
<circle cx="70" cy="80" r="20" fill="white" />
<circle cx="130" cy="80" r="20" fill="white" />
<circle cx="65" cy="75" r="10" fill="black" />
<circle cx="125" cy="75" r="10" fill="black"/>
<!--Smile-->
<path d="M 50 140 A 60 60 0 0 0 150 140"
stroke="white" stroke-width="3" fill="none" />
<rect id="viewBoxIndicator" stroke="red" stroke-width="3.5" fill="none" />
</svg>
</p>
<script>
function update() {
var viewBox = [vx.value, vy.value, vw.value, vh.value].join(' ');
var preserveAspectRatio = [align.value, meetOrSlice.value].join(' ');
svg.setAttribute('viewBox', viewBox);
svg.setAttribute('preserveAspectRatio', preserveAspectRatio);
var rect = viewBoxIndicator;
rect.setAttribute('x', vx.value);
rect.setAttribute('y', vy.value);
rect.setAttribute('width', vw.value);
rect.setAttribute('height', vh.value);
}
form.addEventListener('input', update);
update();
</script>
</body>
</html>
# svg渐变
线性渐变沿着一条直线改变颜色。它使用<linearGradient>元素来定义,该元素必须嵌套在<defs>元素内。
<linearGradient>元素通过x1、y1、x2、y2属性定义渐变线的起点和终点,从而控制渐变的方向。
渐变的颜色范围通过<stop>元素来定义,每个<stop>元素都有一个offset属性(表示颜色在渐变线上的位置)和一个stop-color属性(表示颜色)。
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<linearGradient id="grad1" 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="70" rx="85" ry="55" fill="url(#grad1)"/>
</svg>
径向渐变从一个中心点向外扩散改变颜色。它使用<radialGradient>元素来定义。
<radialGradient>元素通过cx、cy定义中心点,r定义半径,fx、fy定义焦点(可选)。
与线性渐变类似,渐变颜色通过<stop>元素定义。
# svg笔刷(Pattern)
SVG中的笔刷允许你定义复杂的图形作为填充元素。笔刷使用<pattern>元素定义,并放置在<defs>元素内。
笔刷可以包含任何SVG图形,如线条、矩形、圆形、椭圆、多边形、路径等。 笔刷通过id属性定义,然后在需要填充的元素上使用fill="url(#id)"属性应用。
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="p1" x="0" y="0" width="0.2" height="0.2" patternUnits="objectBoundingBox">
<circle cx="10" cy="10" r="5" fill="red"/>
<polygon points="30 10 60 50 0 50" fill="green"/>
</pattern>
</defs>
<rect x="100" y="100" width="800" height="500" fill="url(#p1)" stroke="blue"/>
</svg>
# SVGpath
# SVG文本
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Examples</title>
<meta name="description" content="">
<meta name="keywords" content="">
<link href="" rel="stylesheet">
</head>
<body>
<!-- pattern 笔刷 -->
<svg xmlns="http;//www.w3.org/2000/svg" width="100%" height="800px">
<defs>
<pattern id="grid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<path stroke="#F0F0F0" fill="none" d="M 0 0,H 20,V 20"></path>
</pattern>
</defs>
<rect width="1200" height="1000" fill="url(#grid)"></rect>
<text id="sintext" x='100' y='160' style="font-size:14px;font-family:'Arial';">
</text>
<path d="M 100 0,V 200,M 0 160,H 200" transform="translate(0,60)" stroke="red"></path>
</svg>
<script type="text/javascript">
var text = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var n = text.length;
var x = [];
var i = n;
var y = null;
var s = 100,w = 0.02,t = 0;
while(i--){
x.push(10);
var tspan = document.createElementNS('http://www.w3.org/2000/svg','tspan');
tspan.textContent = text[n - i - 1];
sintext.appendChild(tspan);
var h = Math.round(360 / 26 * i);
tspan.setAttribute('fill','hsl(' + h + ',100%,80%)');
}
function arrange(t){
y = [];
var ly = 0,cy;
for(i = 0;i < n; ++i){
cy = -s * Math.sin(w* i *20 +t);
y.push(cy - ly);
ly = cy;
}
//console.table(y);
}
function render(){
sintext.setAttribute('dx',x.join(' '));
sintext.setAttribute('dy',y.join(' '));
}
function frame(){
t += 0.01;
arrange(t);
render();
requestAnimationFrame(frame);
}
arrange(0);
render();
frame();
</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>动态SVG路径文本</title>
</head>
<body>
<svg width="400" height="200" viewBox="0 0 400 200">
<defs>
<path id="path1" d="M 10 50 C 40 10, 65 10, 95 50 S 150 150, 200 100" fill="none" stroke="black" stroke-width="2"/>
<path id="path2" d="M 10 150 C 40 190, 65 190, 95 150 S 150 50, 200 100" fill="none" stroke="black" stroke-width="2"/>
</defs>
<!-- 初始时不显示文本 -->
<text id="textOnPath" style="display: none;">
<textPath id="textPath" xlink:href="#" startOffset="50%">
沿着路径显示的文本
</textPath>
</text>
<!-- 显示两个路径 -->
<use xlink:href="#path1" />
<use xlink:href="#path2" />
</svg>
<!-- 控制按钮 -->
<button onclick="addTextToPath('path1', 'red')">添加到路径1(红色)</button>
<button onclick="addTextToPath('path2', 'blue')">添加到路径2(蓝色)</button>
</body>
</html>
<script>
function addTextToPath(pathId, color) {
var text = document.getElementById('textOnPath');
var textPath = document.getElementById('textPath');
// 隐藏之前的文本(如果有的话)
text.style.display = 'none';
// 设置textPath的xlink:href属性为指定的路径ID
textPath.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#' + pathId);
// 设置文本颜色
textPath.style.fill = color;
// 显示文本
text.style.display = 'block';
}
</script>
# svg动画
# svg力导向图
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<title>动画:js脚本控制 力导图 (可以修改小球数量) _2 优化</title>
<style>
svg{border:1px solid #eee;}
</style>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-400 -400 800 800" width="800" height="800">
<path id="links" fill="none" stroke="#eeeeee" />
<text id="text" fill="white"></text>
</svg>
<script>
//辅助类 Vector: 向量==矢量,有方向的标量;
(function(){
function Vector(x,y){
this.x=x||0;
this.y=y||0;
}
Vector.prototype={
constructor:Vector,
square:function(){
return this.x**2+this.y**2;
},
length:function(){
return Math.sqrt(this.square());
},
add:function(q){
return new Vector(this.x+q.x, this.y+q.y)
},
minus:function(q){
return new Vector(this.x-q.x, this.y-q.y)
},
multiply:function(scale){
return new Vector(this.x*scale, this.y*scale)
},
//标准化: 乘以一个方向向量,相当于length在某向量上的投影
normalize:function(length){
if(length==undefined){
length=1;
}
return this.multiply(length/ this.length() );
}
};
Vector.fromPoints=function(p1,p2){ //函数可以有自己的方法
return new Vector(p2.x-p1.x, p2.y-p1.y);
};
window.Vector=Vector;
})();
//end of Vector
/**********************
* 力导图: 近乎弹簧的互作,近排斥,远吸引;
**********************/
var XML_NS="http://www.w3.org/2000/svg";
var k=0.1; //弹性系数,弹簧变形的距离和力之间的系数 F=k*delta; 尝试0.05比较好
//建立点,简化为等质量的点,可以人一多个点
//todo map()函数
var points="a,b,c,d,e,f,g,h".split(",").map(function(name, index, arr){
return {
name: name,
color: 'hsl('+ (360/arr.length *index) +', 100%, 60%)',
}
});
var relation=300; //互作为0的距离;
var svg=document.querySelector('svg'), text=document.querySelector('text');
var Vector=window.Vector;
function random(min, max){
return Math.round(min+(max-min)*Math.random());
}
// todo forEach()函数
points.forEach(function(point){
//console.log(point) //{name: "a", color: "hsl(0, 100%, 60%)"}
var circle=document.createElementNS(XML_NS, 'circle'); //这个namespace写错是不能显示的!
//init position
var x=random(-300,300), y=random(-300,300); //调整到屏幕中心区域
circle.setAttribute('cx',x);
circle.setAttribute('cy',y);
circle.setAttribute('r',"10");
circle.setAttribute('fill',point.color);
//绑定
point.circle=circle;
point.s=new Vector(x,y);//初始化位移
point.v=new Vector();//初始没有速度
point.a=new Vector();//初始没有加速度
//加入到dom
svg.appendChild(circle);
});
//获取上一帧的时间
var lastFrameTime = +new Date();
//更新当前帧
var w=0
var links=document.getElementById('links');
function update(){
var frameTime= +new Date();//拿到当前帧的时间
var t=frameTime-lastFrameTime;//两帧时间差
//animation begin
//对时间差(单位是ms)进行缩放,
t/=100; //缩放到1/5s为单位; 物理学单位是m,屏幕单位是px,尝试出来的换算关系;
//更新点的位移、速度、加速度、力等
points.forEach(function(pa){
//console.log(pa)
var f=new Vector(); //定义一个空矢量,表示力
//1.计算合力
points.forEach(function(pb){
//两两互作,排除掉自己和自己
if(pa.name==pb.name) return;
//console.log(w,'两两',pa.name, pb.name)
var x=Vector.fromPoints(pa.s, pb.s);//点之间的距离
var delta=x.length()-relation;//自然长度之外的 弹性变量
//求合力 f=k*x
f=f.add(x.normalize(delta * k)); //在x方向上,力(delta*k) 的投影的长度
});
//2.计算加速度 a=F/m; 简化质量都是m=1
pa.a=f;
//3. 计算速度 vt=v0+a*t;
//如果没有能量损耗,系统将会一直运动下去。此处假设速度每次损耗2%,设置为0.98
pa.v=pa.v.add(pa.a.multiply(t)).multiply(0.98); //bug: 0.999时停止的慢,但是全部小球的质心不固定!
//4. 计算位置 st=s0+v*t
pa.s=pa.s.add( pa.v.multiply(t) );
//设置到dom上
pa.circle.setAttribute('cx',pa.s.x);
pa.circle.setAttribute('cy',pa.s.y);
});
//前面计算
//接着,根据计算结果,更新画面
render();
//for debug only;
w+=1;
if(w%100==0){
console.log(' >>> steps=',w);
}
//2000步差不多正好,可以停止了,
//也可以设置其他停止条件,比如全部小球的变动小于某个值的时候,需要记录上一个小球的位置
if(w>2000){
return ;
}
//animation end
lastFrameTime=frameTime;//当前帧的时间已经成历史
window.requestAnimationFrame(update);//更新画面
}
//渲染
function render(){
//画点和点之间的连线
var linkPath=[];
points.forEach(function(pa){
var sa=pa.s;
points.forEach(function(pb){
if(pa.name==pb.name) return;
var sb=pb.s;
linkPath=linkPath.concat([
"M", sa.x,sa.y,
'L', sb.x, sb.y,
]);
});
});
//绘制连线
links.setAttribute('d', linkPath.join(' ') );
//为小球添加文字
text.innerHTML="";
function n2str(n){
return new String( n );
}
points.forEach(function(p){
var tspan=document.createElementNS(XML_NS, 'tspan');
tspan.setAttribute('x', n2str(p.s.x-5) )
tspan.setAttribute('y', n2str(p.s.y+5) )
tspan.innerHTML=p.name;
text.appendChild(tspan);
});
svg.append(text);//重新把文字放到最后,也就是顶层
}
window.requestAnimationFrame(update);
</script>
# svg蒙版
← 创建svg