SVG之文本
一、文本标签<text>
SVG支持直接对文本进行操作,如果我们需要在SVG中使用文本,那么我们需要使用到<text>标签。直接看一个简单的demo。
-
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>textDemo</title> 6 </head> 7 <body> 8 <svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="1000"> 9 <defs> 10 <pattern id="grid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse"> 11 <path d="M0,0H20V20" style="stroke: #0006;fill: none"></path> 12 </pattern> 13 </defs> 14 <!--网格背景--> 15 <rect fill="url(#grid)" width="1400" height="1000"></rect> 16 <!--文字--> 17 <text x="100" y="100" fill="green" style="font-size: 50px;font-family: 'Arial';">你好,Hello SVG</text> 18 <path d="M100,0V200M0,100H500" stroke="red" /> 19 </svg> 20 </body> 21 </html>
这个demo中我们使用笔刷绘制了20*20大小的格子背景,方便观察坐标。我们发现属性:fill填充文字颜色;X,Y的值是文本的起始坐标(左下角坐标)。然而,汉字明显突出了界限,而英文字母则很好地位于Y值之上。
除了X,Y属性,text还有两个重要的属性:
- dx属性:横向位移字符。
- dy属性:纵向位移字符。
我们加入dx,dy属性来直接感受一下字符之间的变化(从第一个字符起生效):
-
<text x="100" y="100" fill="green" dx="10 20" dy="10 -10 10 -10 10 -10 10 -10 10 -10 10 -10" style="font-size: 50px;font-family: 'Arial';">你好,Hello SVG</text>
一共有12个字符(包含空格),所以dx,dy应当有分别是12个数值,如果不足,默认为0。
二、<tspan>标签的使用
1、小Demo
<span>标签我们应该都很熟悉,对于内联元素我们一般使用<span>来处理。同样的,在SVG的<text>标签中,我们可以使用<tspan>标签进行内联元素处理。
<tspan>的使用能更好地处理文本,特别是部分文本。比如上文的“你好,Hello SVG”,如果我们希望中文和英文的颜色不一样,那么我们可以使用<tspan>将文本“分割”来处理:
-
<text x="100" y="100"> <tspan dx="10 10" dy="-10 20" fill="black" stroke="blue" style="font-size: 40px;">你好,</tspan> <tspan dx="0 0 0 0 0 0 10 -15 -15" dy="10 -10 10 -10 10 -10 10 -10 10" fill="none" stroke="green" style="font-size: 50px;font-family: 'Arial';">Hello SVG</tspan> </text>
我们发现,dx和dy在<tspan>中由于字符串长度变短会变得更好调整。
2、使用dx和dy属性:完成sin(x)文字动画
利用dx和dy属性可以调整文本字符的横向和纵向位移距离,我们现在利用这个属性将26个字母按照y = A·sin(ax+b)这个函数进行排列。
-
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>textDemo</title> 6 </head> 7 <body> 8 <svg id="svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="1000"> 9 <defs> 10 <pattern id="grid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse"> 11 <path d="M0,0H20V20" style="stroke: #0006;fill: none"></path> 12 </pattern> 13 </defs> 14 <!--网格背景--> 15 <rect fill="url(#grid)" width="1400" height="1000"></rect> 16 <!--文字--> 17 <text id="sinText" x="100" y="200" style="font-size: 18px;font-family: 'Arial';">ABCDEFGHIJKLMNOPQRSTUVWXYZ</text> 18 <path d="M100,0V200M0,100H200" transform="translate(20,100)" stroke="red" /> 19 </svg> 20 <script> 21 //x = [20,20,20,...] 22 //y = s*sin(w*x+t); 23 var n = 26; 24 var x = []; 25 var y = null; 26 var i = n; 27 var s = 100; 28 var w = 0.02; 29 var t = 0; 30 31 //横向间隔20 32 while(i--) x.push(20); 33 34 //纵向按照sin()函数变化 35 function arrange(t){ 36 y = []; 37 var ly = 0,cy; 38 for(i=0;i<n;++i){ 39 cy = -s* Math.sin(w * i * 20 +t); 40 y.push(cy - ly); 41 ly = cy; 42 } 43 } 44 //将数组转换成字符串并设置为dx,dy值 45 function render(){ 46 sinText.setAttribute('dx',x.join(' ')); 47 sinText.setAttribute('dy',y.join(' ')); 48 } 49 50 //执行 51 arrange(t); 52 render(); 53 54 </script> 55 </body> 56 </html>
- 现在我们可以动态改变设置t的值,使得这些文本动起来,我们只需要将arrange(t); render();两个方法替换为以下动态方法即可:
//动态改变t的值 function frame() { t += 0.02; arrange(t); render(); window.requestAnimationFrame(frame);//动画效果:递归调用frame方法 } frame();
我们还可以稍加修饰,为每个字母即每个<tspan>增加fill属性,依次来渲染字母的颜色,我们只需要修改while()循环部分:
-
while(i--){ x.push(20); 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%,50%)'); }
三、文字的水平/垂直居中
- 水平居中使用text-anchor属性,值包括:start、middle、end
- 垂直居中使用dominant-baseline属性
下面是一个演示器:
-
1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>SVG文本对齐</title> 5 </head> 6 <body> 7 <br> 8 <label>水平居中属性:text-anchor=</label> 9 <select id="ta"> 10 <option value="start">start</option> 11 <option value="middle">middle</option> 12 <option value="end">end</option> 13 </select> 14 <span>------</span> 15 <label>垂直居中属性:dominant-baseline=</label> 16 <select id="select"></select> 17 <br> 18 <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300"> 19 <path stroke="green" d="M0,100 h400 M140,0 v200" /> 20 <text id="text" x="140" y="100" fill="red" font-size="50">SVG</text> 21 <rect id="rect" stroke="blue" fill="none"></rect> 22 </svg> 23 24 <script> 25 var values = "auto | use-script | no-change | reset-size | ideographic | alphabetic | hanging | mathematical | central | middle | text-after-edge | text-before-edge | text-top | text-bottom".split(' | '); 26 27 values.forEach(function(value) { 28 var opt = document.createElement('option'); 29 opt.value = opt.textContent = value; 30 select.appendChild(opt); 31 }); 32 33 select.addEventListener('input', function() { 34 text.setAttribute('dominant-baseline', select.value); 35 var box = text.getBBox(); 36 rect.setAttribute('x', box.x); 37 rect.setAttribute('y', box.y); 38 rect.setAttribute('width', box.width); 39 rect.setAttribute('height', box.height); 40 }); 41 42 ta.addEventListener('input', function() { 43 text.setAttribute('text-anchor', ta.value); 44 }); 45 </script> 46 </body> 47 </html>
四、控制文本路径的标签<textpath>
1、小栗子
上面我们使用dx和dy属性对26个字母实现了文本按照sin()函数排列,但这并不是真正地按照路径排列。如果想要文本按照某个路径排列,我们需要使用<textPath>标签。直接看一个简单的栗子:
-
1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>路径文本</title> 5 </head> 6 7 <body> 8 <svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"> 9 <path id="path1" d="M 100 200 Q 200 100 300 200 T 500 200" stroke="green" stroke-width="2" fill="none" /> 10 <g fill="red"> 11 <circle cx="100" cy="200" r="4" /> 12 <circle cx="300" cy="200" r="4" /> 13 <circle cx="500" cy="200" r="4" /> 14 </g> 15 <text style="font-size: 22px;"> 16 <textpath xlink:href="#path1">投我以木瓜,报之以琼琚,匪报也,永以为好也。</textpath> 17 </text> 18 </svg> 19 </body> 20 </html>
其中,我们先使用<path>定义了一条路径,然后使用<textpath>来处理文本,并设置 xlink:href 参数来控制文本路径。
2、一个演示器
当我们使用<textpath>标签控制文本路径后,<text>的属性:x,y,dx,dy,会产生不一样的效果,下面是一个演示器,我们可以更直观的感受这种变化:
-
1 <html> 2 <head> 3 <meta charset="utf-8"> 4 <title>路径文本</title> 5 </head> 6 7 <body> 8 <br> 9 <form id="ctrl"> 10 <label>x:</label> 11 <input id="x" ctrl="x" type="range" value="0" min="-200" max="200" /> 12 <label id="x_value" style="color: red">0</label> 13 14 <label>text-anchor:</label> 15 <select ctrl="text-anchor"> 16 <option value="start" selected>start</option> 17 <option value="middle">middle</option> 18 <option value="end">end</option> 19 </select> 20 <br><br> 21 <label>y:</label> 22 <input id="y" ctrl="y" type="range" value="0" min="-200" max="200" /> 23 <label id="y_value" style="color: red">0</label> 24 25 26 <label>startOffset:</label> 27 <input id="startOffset" ctrl="startOffset" type="range" value="0" min="-100" max="100" /> 28 <label id="so_value" style="color: red">0</label> 29 30 <br><br> 31 <button type="reset" style="margin-left: 200px;">重置</button> 32 </form> 33 <svg xmlns="http://www.w3.org/2000/svg" width="800" height="600"> 34 <path id="path1" d="M 100 200 Q 200 100 300 200 T 500 200" stroke="green" fill="none" /> 35 <g fill="red"> 36 <circle cx="100" cy="200" r="4" /> 37 <circle cx="300" cy="200" r="4" /> 38 <circle cx="500" cy="200" r="4" /> 39 </g> 40 <text style="font-size: 20px;"> 41 <textpath xlink:href="#path1">投我以木瓜,报之以琼琚,匪报也,永以为好也。</textpath> 42 </text> 43 </svg> 44 </body> 45 <script> 46 // jshint browser: true 47 var ctrl= document.getElementById('ctrl'); 48 var text = document.querySelector('text'); 49 var textPath = text.firstElementChild; 50 51 function update(target) { 52 var attr = target.getAttribute('ctrl'); 53 if(!attr) return; 54 if(attr == 'startOffset') { 55 textPath.setAttribute(attr, target.value + '%'); 56 } else { 57 text.setAttribute(attr, target.value); 58 } 59 } 60 61 function info(){ 62 var x_value = document.getElementById('x_value'); 63 var y_value = document.getElementById('y_value'); 64 var so_value = document.getElementById('so_value'); 65 x_value.innerText = x.value; 66 y_value.innerText = y.value; 67 so_value.innerText = startOffset.value; 68 } 69 70 ctrl.addEventListener('input', function(e){ 71 update(e.target); 72 info(); 73 }) 74 75 ctrl.addEventListener('reset', function(){ 76 setTimeout(function(){ 77 var list = document.querySelectorAll('#ctrl *[ctrl]'); 78 [].slice.call(list).forEach(update); 79 info(); 80 }) 81 }) 82 </script> 83 </html>
这里面startOffset的值决定了其实文本偏移位置,和之前的text-anchor类似。上面没有将dx,dy属性添加进去,但是也会影响文本路径。
五、超链接<a>标签的使用
<a>标签我们都很熟悉,嗯?在SVG中同样提供了同名的<a>标签,可以作为任意图形的超链接。
- xlink:href属性:规定连接URL。
- xlink:title属性:显示连接提示信息。
- target属性:指定在何处打开目标。可选值如下:
下面是一个简单的实例:
-
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>SVG超连接</title> 6 </head> 7 <body> 8 <svg xmlns="http://www.w3.org/2000/svg"> 9 <a xlink:href="http://www.cnblogs.com/fzz9" xlink:title="博客园_fzz" target="_blank"> 10 <rect height="30" width="100" y="0" x="0" rx="15"></rect> 11 <text fill="white" text-anchor="middle" y="21" x="45"> 12 <tspan id="feng" style="font-size: 30px;font-family:STXinwei;">风</tspan> 13 <tspan>之之</tspan> 14 </text> 15 </a> 16 </svg> 17 <script type="text/javascript"> 18 var h = 0; 19 20 //动态设置字体颜色 21 function setColor(h){ 22 var feng = document.getElementById('feng'); 23 feng.setAttribute('stroke','hsl('+ h +',100%,50%)'); 24 } 25 function frame(){ 26 setColor(h); 27 h += 0.8; 28 if(h>=360) h = 0; 29 window.requestAnimationFrame(frame);//动画效果:递归调用frame方法 30 } 31 frame(); 32 </script> 33 </body> 34 </html>