SVG学习(5)

SVG文本

<text><tspan>创建文本

  • x和y属性 - 定位标准
  • dx和dy属性 - 字形偏移
  • style属性 - 设置样式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">
<defs>
<pattern id="grid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<path stroke="#F0F0F0" fill="none" d="M0,0H20V20"></path>
</pattern>
</defs>
<rect width="1200" height="1000" fill="url(#grid)"></rect>
<text id="sintext" x="100" y="150"></text>
<text x="100" y="150">
<tspan fill="red" dy="-20 20">AB</tspan>
<tspan stroke="green" fill="none" dy="-40 20">CDE</tspan>
</text>
<path d="M100,0V200M0,100H200" stroke="red" fill="none"></path>
</svg>
<script>
// y = s * sin(w * x + t)
// x = [20, 20, 20...]

var NS = 'http://www.w3.org/2000/svg';
var text = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var n = text.length;
var x = [];
var y = null;
var i = n;
var s = 100;
var w = 0.02;
var t = 0;

while(i--) {
x.push(10);
var tspan = document.createElementNS(NS, '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(var i = 0; i < n; ++i){
cy = -s * Math.sin(w * i * 20 + t);
y.push(cy - ly);
ly = cy;
}
// y = x.map(function(x){
// return s * Math.sin(w * x + t);
// });
}

function render(){
sintext.setAttribute("dx", x.join(' '));
sintext.setAttribute("dy", y.join(' '));
}

function frame(){
t += 0.01;
arrange(t);
render();
requestAnimationFrame(frame);
}
frame();
</script>

<tspan>中dy是向下传递的
text和tspan中同时设置dy时,text中的dy属性会被覆盖

垂直居中问题

模拟
var box = text.getBBox(); 渲染的盒子
顶线: box.y
中线: box.y + box.height / 2
底线: box.y + box.height
文本基线: y

<textPath> 让文本在指定路径上排列

路径文本

  • 使用方法
1
2
3
4
5
6
<path id="path1" d="M 100 200 Q 200 100 300 200 T 500 200" stroke="rgb(0,255,0)" fill="none" />
<text style="font-size: 24px;">
<textPath xlink:href="#path1">
这个文字先上去,又下来了。Upside down in english!
</textPath>
</text>
  • 布局原理

渲染原理
浏览器会把字符从字体表中查到这个字符要占的宽度charWidth,然后从路径最开始的位置找到了一个点,根据这个宽度,在这个路径经过多少长度之后找到另一个点,这两个点将作为计算的基点,通过这两个基点算出来一个点是这个点到这两个基点的位置在路径上的长度都是一样的,最终算出中间的点将会作一条切线以及法线,在这条法线上会把这个文字对齐到文字中央并且把文字的基线位置对齐到切线的位置,这样就完成了第一个文字的排列。上一个文字排列的时候算出来的三个点中的最后一个点会作为下一次计算的开始的点。

  • 定位属性 x,y,dx,dy的作用

x、text-anchor和startOffset属性

- 确定排列起始位置

dx、dy属性

- 切线和法线方向的偏移
1
2
3
4
5
6
7
8
9
10
<path id="path1" d="M 100 200 Q 200 100 300 200 T 500 200" stroke="rgb(0,255,0)" fill="none" />
<text style="font-size: 24px;">
<textPath xlink:href="#path1">
<tspan>这个文字先</tspan>
<tspan fill="blue" dy="-30">上去</tspan>
<tspan dy="30">,又</tspan>
<tspan fill="red" dy="30">下来</tspan>
<tspan dy="-30">了。Upside down in english!</tspan>
</textPath>
</text>

示例:点击查看

  • 脚本控制
    setAttributeNS()方法设置xlink:href属性
    把文本节点替换为节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<form>
<label>Path:</label>
<select id="text-path-select">
<option value="none">none</option>
<option value="#path1">path1</option>
<option value="#path2">path2</option>
<option value="#path3">path3</option>
</select>
</form>
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<path id="path1" d="M 100 200 Q 200 100 300 200 T 500 200" stroke="rgb(0,255,0)" fill="none" />
<path id="path2" d="M100,300l100-50,200,100,100,-50" stroke="rgb(255,0,0)" fill="none" />
<path id="path3" d="M100,400A400,300,0,0,0,500,400" stroke="blue" fill="none" />
<text id="text" x="100" y="100" style="font-size: 20px;">
Text path scripting.<tspan id="tspan">动态使用路径文本</tspan>
</text>
</svg>
<script>
var SVG_NS = 'http://www.w3.org/2000/svg';
var XLINK_NS = 'http://www.w3.org/1999/xlink';

var select = document.getElementById('text-path-select');
var text = document.getElementById('text');
var tspan = document.getElementById('tspan');

function addTextPath() {
var textPath = document.createElementNS(SVG_NS, 'textPath');
while (text.firstChild) {
textPath.appendChild(text.firstChild);
}
text.appendChild(textPath);
}

function setTextPath(path) {
var textPath = text.firstChild;
textPath.setAttributeNS(XLINK_NS, 'xlink:href', path);

var pathElement = document.querySelector(path);
tspan.setAttribute('fill', pathElement.getAttribute('stroke'));
}

function removeTextPath() {
var textPath = text.firstChild;
while (textPath.firstChild) {
text.appendChild(textPath.firstChild);
}
text.removeChild(textPath);
tspan.removeAttribute('fill');
}

select.addEventListener('input', function() {
var value = select.value;
if (text.firstChild.tagName && text.firstChild.tagName.toLowerCase() == 'textpath') {
if (value == 'none') {
removeTextPath();
} else {
removeTextPath();
addTextPath();
setTextPath(value);
}
} else {
if (value != 'none') {
addTextPath();
setTextPath(value);
}
}
});
</script>

示例:点击查看

<a>插入超链接

  • 可以添加到任意的图形上
  • xlink:href 指定链接地址
  • xlink:title 指定链接提示
  • target 指定打开目标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="600">
<a xlink:href="http://baike.baidu.com/search/word?word=正方形" xlink:title="正方形" target="_blank">
<rect x="100" y="100" width="100" height="100" fill="rgba(255,0,0,.5)" stroke="rgb(200,0,0)" stroke-width="2"></rect>
</a>
<a xlink:href="http://baike.baidu.com/search/word?word=圆形" xlink:title="圆形" target="_blank">
<circle cx="300" cy="150" r="50" fill="rgba(0,255,0,.5)" stroke="rgb(200,0,0)" stroke-width="2"></circle>
</a>
<a xlink:href="http://baike.baidu.com/search/word?word=三角形" xlink:title="三角形" target="_blank">
<polygon points="150 250 200 350 100 350" fill="rgba(0,0,255,.5)" stroke="rgb(200,0,0)" stroke-width="2"></polygon>
</a>
<a xlink:href="http://baike.baidu.com/search/word?word=平行四边形" xlink:title="平行四边形" target="_blank">
<polygon points="300 250 350 250 300 350 250 350" fill="rgba(255,255,0,.5)" stroke="rgb(200,0,0)" stroke-width="2"></polygon>
</a>
</svg>

示例:点击查看

根据慕课网课程整理