一、文本標簽<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>