# 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蒙版

最后更新: 6/12/2024, 8:49:29 AM