­

三種圖表技術SVG、Canvas、WebGL 3D比較

  • 2019 年 10 月 4 日
  • 筆記

1.什麼是SVG?

描述:

  • 一種使用XML描述的2D圖形的語言
  • SVG基於XML意味着,SVG DOM中的每個元素都是可用的,可以為某個元素附加Javascript事件處理器。
  • 在 SVG 中,每個被繪製的圖形均被視為對象。如果 SVG 對象的屬性發生變化,那麼瀏覽器能夠自動重現圖形。

2.什麼是canvas?

描述:

  • 通過Javascript來繪製2D圖形。
  • 是逐像素進行渲染的。
  • 其位置發生改變,會重新進行繪製。

3.什麼是WebGL 3D?

  • 說白了就是基於Canvas的3D框架
  • 主要用來做 3D 展示、動畫、遊戲。

因為前兩項都是描述2D圖形的,而WebGL是描述3d的,所以以下針對SVG和Canvas做比較。

3.有了Canvas為什麼還要使用SVG

最重要的一點是SVG不依賴於終端設備的像素,可以隨意放大縮小但是不會失真

繼續:為什麼SVG放大不會失真而Canvas卻會變模糊呢?

因為SVG的渲染的原理是通過對圖形的數學描述來繪圖的,例如:以下哆啦A夢的頭型的思路是,我先畫一個貝塞爾函數,然後填充顏色。

而Canvas的渲染原理是通過對每個像素顏色的填充,最後組成圖形,例如:以下馬里奧的帽子我們可以看出,其實帽子的形狀是由一個個像素填充出來的。

另外Canvas渲染出來的圖叫位圖,SVG渲染出來的圖叫矢量圖

看到這裡你肯定會覺得那直接所有圖形都用SVG畫不就行了,位圖就可以直接淘汰了呀,但是SVG畫的圖也有缺點,以下針對兩者的不同做一個對比。

4.兩者的對比

理解適用場景:

從以下這張微軟開發社區公布的性能圖中也可以看出,SVG在繪圖面積較大,數據量較小的時候性能較好,渲染時間較短,而Canvas剛好相反。

5.總結

Canvas和SVG兩者的適用場景不同,開發者在使用是應根據具體的項目需求來選擇相應的渲染方式。

最後附上一個SVG編譯器幫大家更好的理解和使用SVG

<!DOCTYPE HTML>  <html>  <head>  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />      <title>SVG 編輯器</title>      <style>          #toolbox {              position: absolute;              top: 0;              bottom: 0;              left: 0;              width: 250px;              border-right: 1px solid #CCC;          }            #toolbox h2 {              margin: 0;              padding: 0;              background: #EEE;              font-size: 16px;              height: 24px;              line-height: 24px;              padding: 5px 10px;          }            #toolbox form {              padding: 10px;          }            #canvas {              position: absolute;              left: 260px;              top: 10px;              bottom: 10px;              right: 10px;              box-shadow: 2px 2px 10px rgba(0,0,0,.4);              border-radius: 5px;          }            label {              display: inline-block;              width: 80px;              text-align: right;          }      </style>  </head>  <body>      <div id="toolbox">          <h2>創建</h2>          <form id="create-shape">              <button type="button" create="rect">Rect</button>              <button type="button" create="circle">Circle</button>              <button type="button" create="ellipse">Ellipse</button>              <button type="button" create="line">Line</button>          </form>          <h2>形狀</h2>          <form id="shape-attrs">              請先創建圖形          </form>          <h2>外觀和變換</h2>          <form id="look-and-transform" disabled="disabled">              <p>                  <label style="display: inline;">填充</label>                  <input id="fill" type="color" value="#ffffff" />              </p>              <p>                  <label style="display: inline;">描邊</label>                  <input id="stroke" type="color" value="#ff0000" />                  <input id="strokeWidth" type="range" value="1" />              </p>              <p>                  <label>translateX</label>                  <input id="translateX" type="range" min="-400" max="400" value="0" />                    <label>translateY</label>                  <input id="translateY" type="range" min="-400" max="400" value="0" />                    <label>rotate</label>                  <input id="rotate" type="range" min="-180" max="180" value="0" />                    <label>scale</label>                  <input id="scale" type="range" min="-1" max="2" step="0.01" value="1" />              </p>          </form>      </div>      <div id="canvas"></div>  </body>  <script>      var SVG_NS = 'http://www.w3.org/2000/svg';        // 圖形及對應默認屬性      var shapeInfo = {          rect: 'x:10,y:10,width:200,height:100,rx:0,ry:0',          circle: 'cx:200,cy:200,r:50',          ellipse: 'cx:200,cy:200,rx:80,ry:30',          line: 'x1:10,y1:10,x2:100,y2:100'      };        // 默認公共屬性      var defaultAttrs = {          fill: '#ffffff',          stroke: '#ff0000'      };        var createForm = document.getElementById('create-shape');      var attrForm = document.getElementById('shape-attrs');      var lookForm = document.getElementById('look-and-transform');        var svg = createSVG();      var selected = null;        createForm.addEventListener('click', function(e) {          if (e.target.tagName.toLowerCase() == 'button') {              create(e.target.getAttribute('create'));          }      });        attrForm.addEventListener('input', function(e) {          if (e.target.tagName.toLowerCase() != 'input') return;          var handle = e.target;          selected.setAttribute(handle.name, handle.value);      });        lookForm.addEventListener('input', function(e) {          if (e.target.tagName.toLowerCase() != 'input') return;          if (!selected) return;          selected.setAttribute('fill', fill.value);          selected.setAttribute('stroke', stroke.value);          selected.setAttribute('stroke-width', strokeWidth.value);          selected.setAttribute('transform', encodeTranform({              tx: translateX.value,              ty: translateY.value,              scale: scale.value,              rotate: rotate.value          }));      });        function createSVG() {          var svg = document.createElementNS(SVG_NS, 'svg');          svg.setAttribute('width', '100%');          svg.setAttribute('height', '100%');          canvas.appendChild(svg);            svg.addEventListener('click', function(e) {              if (e.target.tagName.toLowerCase() in shapeInfo) {                  select(e.target);              }          });          return svg;      }        function create(name) {          var shape = document.createElementNS(SVG_NS, name);          svg.appendChild(shape);          select(shape);      }        function select(shape) {          var attrs = shapeInfo[shape.tagName].split(',');          var attr, name, value;            attrForm.innerHTML = "";            while(attrs.length) {              attr = attrs.shift().split(':');              name = attr[0];              value = shape.getAttribute(name) || attr[1];              createHandle(shape, name, value);              shape.setAttribute(name, value);          }            for (name in defaultAttrs) {              value = shape.getAttribute(name) || defaultAttrs[name];              shape.setAttribute(name, value);          }          selected = shape;            updateLookHandle();      }        function createHandle(shape, name, value) {              var label = document.createElement('label');          label.textContent = name;            var handle = document.createElement('input');          handle.setAttribute('name', name);          handle.setAttribute('type', 'range');          handle.setAttribute('value', value);          handle.setAttribute('min', 0);          handle.setAttribute('max', 800);            attrForm.appendChild(label);          attrForm.appendChild(handle);      }        function updateLookHandle() {          fill.value = selected.getAttribute('fill');          stroke.value = selected.getAttribute('stroke');          var t = decodeTransform(selected.getAttribute('transform'));          translateX.value = t ? t.tx : 0;          translateY.value = t ? t.ty : 0;          rotate.value = t ? t.rotate : 0;          scale.value = t ? t.scale : 1;      }        function decodeTransform(transString) {          var match = /translate((d+),(d+))srotate((d+))sscale((d+))/.exec(transString);          return match ? {              tx: +match[1],              ty: +match[2],              rotate: +match[3],              scale: +match[4]          } : null;      }        function encodeTranform(transObject) {          return ['translate(', transObject.tx, ',', transObject.ty, ') ',              'rotate(', transObject.rotate, ') ',              'scale(', transObject.scale, ')'].join('');      }    </script>  </html>