以前自己開發繪圖函式庫時,字都是自己畫,一開始是抓倚天中文的字形檔,後來用自己的方式產生自己的字形檔,並加上 alpha 來做反鋸齒,當時還為了開發泰國版本,自行設計泰文輸入法,雖然可以使用 truetype 字形,但是當時硬體規格太差,為了速度,還是要自己寫效率比較高,現在看到 SVG 文字相關的定義,真的覺得當初自己所想到的功能太陽春,SVG 的規劃要完整多了。
text 的參數如下:
x,y 座標
dx,dy 偏移
rotate 旋轉
lengthAdjust spacing (預設) 字與字之間以空白做間隔
spacingAndGlyphs 字與字之間以變形填滿
textLength 字串長度
text-anchor start , middle , end 文字定位
以下參數也可以做為 CSS 的參數
dominant-baseline auto (預設)
text-bottom
alphabetic
ideographic
middle
central
mathematical
hanging
text-top
writing-mode horizontal-tb
vertical-rl
vertical-lr
先來一個簡單範例
<svg width="320" height="200" viewBox="0 0 420 200" style="background-color:#ffa">
<text x="10" y="160" stroke-width="10" stroke="#f00" fill="#0ff" style="font-size:180px;font-weight:900">1a</text>
<text x="210" y="160" stroke-linejoin="round" stroke-width="10" stroke="#f00" fill="#0ff" style="font-size:180px;font-weight:900">1a</text>
</svg>
可以發現,SVG 的字完全就是用畫的,像多邊形一樣,所以右邊的文字有加上 stroke-linejoin="round"
在角落的地方就成了圓角
文字處理另一個麻煩的就是基準線,這裡使用的是 dominant-baseline ,可以當作參數,也可以當作 CSS 使用
CSS 的文字有個很特別的設計,利用類似陣列的方式為每一個字設定屬性
<svg width="300" height="100" style="background-color:#ffa">
<text x="10,20,40,80,150" y="60,70,50" fill="#000" style="font-size:32px;">abcde</text>
</svg>
也可以用 dx 和 dy 做偏移
<svg width="300" height="100" style="background-color:#ffa">
<text x="10" y="60" dx="0,10,20,30,40" dy="0,10,20,-40" fill="#000" style="font-size:32px;">abcde</text>
</svg>
可以用 rotate 旋轉, 而旋轉預設的中心點在左下角
<svg width="300" height="100" style="background-color:#ffa">
<text x="10" y="60" rotate="0,30,60,90,-40,0,0,90,-90" fill="#000" style="font-size:32px;">abcde ddd</text>
</svg>
textLength 和 lengthAdjust 搭配,設定字串長度後,看要用空白還是變形的方式填空間
<svg width="320" height="200" viewBox="0 0 420 200" style="background-color:#ffa">
<text x="10" y="40" textLength="300" lengthAdjust="spacing" fill="#000" style="font-size:32px;">abcde</text>
<text x="10" y="80" textLength="300" lengthAdjust="spacingAndGlyphs" fill="#000" style="font-size:32px;">abcde</text>
<text x="10" y="120" textLength="60" lengthAdjust="spacingAndGlyphs" fill="#000" style="font-size:32px;">abcde</text>
<text x="360" y="20" textLength="140" lengthAdjust="spacingAndGlyphs" fill="#000" style="font-size:32px;writing-mode:vertical-lr">直寫測試123</text>
</svg>
tspan
當一段文字想要有不同設定時,可以用 tspan ,就如同 css 的 span 一樣,搭配 dominant-baseline 就可以做到上標字和下標字的效果,
*經實測結果 Firefox 會不正常顯示,以下正確結果應該顯示為 X₂+Y²=Z⁽²ⁿ⁺¹⁾
<style>
.a112707_central
{
dominant-baseline:central;
}
.a112707_up
{
font-size:0.6em;
dominant-baseline:auto;
}
.a112707_down
{
font-size:0.6em;
dominant-baseline:hanging;
}
</style>
<svg width="320" height="200" style="background-color:#ffa">
<text x="10" y="40" fill="#000" style="font-size:32px;">abc<tspan fill="#f00">123</tspan>def<tspan style="font-size:48px">def</tspan>ghi</text>
<text x="10" y="100" fill="#000" style="font-size:32px;" class="a112707_central">
X
<tspan class="a112707_down">2</tspan>
+Y
<tspan class="a112707_up">2</tspan>
=Z
<tspan class="a112707_up">(2n+1)</tspan>
</text>
</svg>
如果要完全相容的上標和下標字,似忽只有利用 dy 來實作,但是這個缺點就是,沒辦法單純用 css 來完成,而且 <tspan>
的定義很奇怪,一但 dy 改變了,就是改變了,並不會在 </tspan>
時變回來,所以在使用上標字(dy 減去某個值) ,必須在下一個顯示的字把 dy 的值加回來。
<style>
.a112708_normal
{
font-size:1em;
}
.a112708_up
{
font-size:0.6em;
}
.a112708_down
{
font-size:0.6em;
}
</style>
<svg width="320" height="100" style="background-color:#ffa">
<text x="10" y="60" fill="#000" style="font-size:32px;">
<tspan class="a112708_normal">X</tspan>
<tspan class="a112708_down">2</tspan>
<tspan class="a112708_normal">+Y</tspan>
<tspan class="a112708_up" dy="-1em">2</tspan>
<tspan class="a112708_normal" dy="0.6em">=Z</tspan>
<tspan class="a112708_up" dy="-1em">(2n+1)</tspan>
</text>
</svg>
為了處理上標和下標問題,只好利用 javascript 來協助,加上 svgsup 和 svgsub 兩個 class ,由 javascript 來處理 dy 的問題,程式碼很簡單,效果也不錯
<style>
.svgsup,.svgsub
{
font-size:0.6em;
}
</style>
<svg width="320" height="100" style="background-color:#ffa">
<text x="10" y="60" fill="#000" style="font-size:32px;">
<tspan>X</tspan>
<tspan class="svgsub">2</tspan>
<tspan>+Y</tspan>
<tspan class="svgsup">2</tspan>
<tspan>=Z</tspan>
<tspan class="svgsup">(2n+1)</tspan>
</text>
</svg>
<script>
$(function()
{
$(".svgsup").attr("dy","-1em");
$(".svgsup + tspan").attr("dy","0.6em");
})
</script>
textPath
SVG 有個有趣的東西叫 textPath ,文字可以隨著路徑跑,這個功能當初我在設計網頁上的向量地圖時曾經用來顯示路名
當初在寫導航程式顯示路名時,字隨路徑走是用比較偷懶的方式,計算出每個字的座標,但是字不會旋轉,隨著現在硬體規格越來越強,加上 SVG 有這麼方便的指令,要開發 2D 地圖真的簡單多了。
<svg width="320" height="200" viewBox="0 0 400 200" style="background-color:#ffa">
<defs>
<path id="a112709_p1" d="M20,100 Q100,0 200,100 Q300,200 380,100"></path>
</defs>
<use xlink:href="#a112709_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;">
<textPath href="#a112709_p1">文字可以隨著路徑跑
</textPath>
</text>
</svg>
<br><br>
<svg width="320" height="200" viewBox="0 0 400 200" style="background-color:#ffa">
<use xlink:href="#a112709_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;">
<textPath href="#a112709_p1" startOffset="100">startOffset="100"
</textPath>
</text>
</svg>
<br><br>
<svg width="320" height="200" viewBox="0 0 400 200" style="background-color:#ffa">
<use xlink:href="#a112709_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;">
<textPath href="#a112709_p1">可以使用<tspan fill="#f0f" dy="-20" style="font-size:32px">tspan</tspan><tspan dy="20" fill="#00f">來做效果</tspan>
</textPath>
</text>
</svg>
<br><br><font color="#f00"><b>這個設定在 iPhone 及 safari 沒用</b></font><br>
<svg width="320" height="200" viewBox="0 0 400 200" style="background-color:#ffa">
<use xlink:href="#a112709_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;" dominant-baseline="central">
<textPath href="#a112709_p1">dominant-baseline="central"
</textPath>
</text>
</svg>
<br><br>
<svg width="320" height="200" viewBox="0 0 400 200" style="background-color:#ffa">
<use xlink:href="#a112709_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;writing-mode:vertical-lr;">
<textPath href="#a112709_p1">直式書寫 writing-mode:vertical-lr;
</textPath>
</text>
</svg>
這個設定在 iPhone 及 safari 沒用
文字可以隨著路徑跑,那麼是不是可以取得路徑長度後,計算每個字的間距,讓字平均分布呢 ?
方法如下 :
let pp = document.getElementById("[path 的 ID]");
let len = pp.getTotalLength(); //取得路徑總長度
let xy = pp.getPointAtLength(500); //取得距離 500 的點位 , return {x:n,y:m}
let box = pp.getBBox(); //取得路徑範圍 , return {x:n,y:n,width:n,height:n}
取得長度後就可以計算平均分布的位置
<svg width="300" height="300" style="background-color:#ffa">
<defs>
<path id="a112710_p1" d="M30,150 a1,1,0,0,1,240,0 a1,1,0,0,1,-240,0"></path>
</defs>
<use xlink:href="#a112710_p1" fill="none" stroke="#fa0" stroke-width="2"></use>
<text fill="#000" style="font-size:24px;">
<textPath href="#a112710_p1">
<tspan id="a112710_txt">文字可以隨著路徑跑喲</tspan>
</textPath>
</text>
</svg>
<script>
$(function()
{
let pp = document.getElementById("a112710_p1");
let len = pp.getTotalLength();
let txt=$("#a112710_txt");
let txtlen=txt.html().length;
let i;
let hh="";
let j=len/txtlen;
for (i=0;i<txtlen;i++)
{
hh+=j*i+" ";
}
txt.attr("x",hh);
})
</script>