[JavaScript] HTML svg 動態畫圖

習作要用 HTML 的 <canvas> 或 <svg> 畫統計圖形,就玩了一下這些標籤,這篇只討論 <svg> ,關於動態生成圖形,例如上圖是按了 <button> 後的結果,本來全是空的如下:

生成的部份我用「原本有無標籤」分成兩類

已有形狀tag

如果 <svg>裡已經有要畫的標籤 tag,例如<line>、<rect>、... ,只要動態賦予屬性即可,例如 HTML 是:

<body>
  <svg id="idSvg" width="300" height="300">
    <line id="line" x1="0" y1="0" x2="0" y2="0"></line>
    <circle id="circ" cx="0" cy="0" r="0"></circle>
  </svg>
  <button onclick="draw()">畫</button>
  <script>
  </script>
</body>

即使直線與圖形的標籤已存在,只要直線的點重合、圓形半徑為零,等等的情形下也是不會畫出來的,而寫在 <button> 中的事件要做的是抓取 #line 與 #circ 的 DOM,改變直線的坐標、改變圓心坐標和半徑

<script>
function draw() {
  let line = document.getElementById('line');
  line.setAttribute('x1', 220);
  line.setAttribute('y1', 40);
  line.setAttribute('x2', 80);
  line.setAttribute('y2', 260);

  let circ = document.getElementById('circ');
  circ.setAttribute('cx', 150);
  circ.setAttribute('cy', 150);
  circ.setAttribute('r', 80);
}
</script>

用這樣的技巧加上 new Date() 和 window.setInterval() 可以簡單做出一個指針時鐘。這個做法適合畫布上要畫的圖形不多,而且個數已確定不太變動的時候。

形狀tag不存在

這樣的 HTML 比較乾淨,<svg>中沒有其他標籤:

<body>
  <svg id="idSvg" width="300" height="300"></svg>
  <button onclick="draw()">畫</button>
  <script>
  </script>
</body>

接下來要做的是用 JavaScript 生成 DOM 元素讓 <svg> 去 appendChild(),成為像是 <svg> <line> </line> <circle> </circle> </svg>。

不過單純的 document.createElement('line') 是沒有用的,要使用 document.createElementNS('http://www.w3.org/2000/svg','line') ,好像是因為 document.createElement() 是產生 HTML 元素 <line> 而不是 <svg> 的 <line>(老實說我看不懂差別,但沒有指定 namespace 真的無法創建。資源參考自 Document.createElementNS: What's the difference and why we need it

function draw() {
  let domSvg = document.getElementById('idSvg');
  let xmls = 'http://www.w3.org/2000/svg';

  let line = document.createElementNS(xmls,'line');
  line.setAttribute('x1', 220);
  line.setAttribute('y1', 40);
  line.setAttribute('x2', 80);
  line.setAttribute('y2', 260);
  domSvg.appendChild(line);

  let circ = document.createElementNS(xmls,'circle');
  circ.setAttribute('cx', 150);
  circ.setAttribute('cy', 150);
  circ.setAttribute('r', 80);
  domSvg.appendChild(circ);
}

這個做法適合大量的圖形,可能用陣列與迴圈產生圖形的時候,或是要繪製的圖形數量不固定,要依情形判斷的時候。

留言