HTML 上的圖形渲染主要有兩種方案 SVG 和 Canvas,前者更易於使用,然後者潛力更大,本文主要關注如何使用 Canvas 繪製出更多的圖形,提供更加流暢的交互。本文的內容有:javascript
咱們以簡單的一個圓爲示例,來對比 SVG 和 Canvas 的渲染:
html
<svg> <circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/> </svg> 複製代碼
<canvas></canvas> <script> var ctx=c.getContext("2d"); ctx.strokeStyle = 'black'; ctx.fillStyle = 'red' ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(100,50,40,0,2 * Math.PI); ctx.stroke(); </script> 複製代碼
PS:你能夠把 SVG 的理解成製做完一個個的圖形放到頁面上,而 Canvas 則是使用畫筆一個個的繪製圖形。
這篇文章並非對比 SVG 和 Canvas 差別的文章,二者的差異 w3cshool 的描述很是準確。html5
使用瀏覽器的方法須要從新繪製一遍圖形:react
ctx.beginPath(); ctx.arc(100,50,40,0,2 * Math.PI); const inPath = ctx.isPointInPath(100, 100); const inStroke = ctx.isPointInStroke(100, 100); 複製代碼
須要對每種圖形提供判斷是否在圖形內部和圖形邊上的方法:git
function isInCircle(point, x, y, r) { return distance(point.x, point.y, x, y) <= r; } function isInCircleStroke(point, x, y, r, lineWidth) { const d = distance(point.x, point.y, x, y); return d <= r + lineWidth / 2 && d >= r - lineWidth / 2; } const point = {x: 100, y: 100}; const inPath = isInCircle(point, 100, 50, 40); const inStroke = isInCircle(point, 100, 50, 40, 2); 複製代碼
PS:二者的性能測試對比github
瀏覽器 API | 數學計算 | |
---|---|---|
1 | 111.02999997092411 | 4.779999959282577 |
2 | 110.53000000538304 | 5.694999999832362 |
3 | 117.55500000435859 | 7.979999994859099 |
4 | 126.2599999899976 | 5.354999972041696 |
5 | 110.8949999907054 | 4.725000006146729 |
6 | 121.6549999662675 | 6.2049999833106995 |
7 | 121.18500005453825 | 4.529999976512045 |
8 | 116.78500002017245 | 8.094999997410923 |
9 | 124.06000000191852 | 8.925000031013042 |
10 | 124.42499998724088 | 4.849999968428165 |
平均值 | 118.43799999915063 | 6.113999988883734 |
更多更快的拾取方案能夠參考 2D 圖形拾取方案web
對於 SVG 的圖形來講,直接修改對應標籤的屬性便可,有瀏覽器控制刷新圖形。可是對於 Canvas 來講須要清除整個畫布,從新繪製全部的圖形,也就是說 Canvas 畫布上有 10W 個圖形,僅僅更新一個圖形時,其餘 99999 個圖形也須要從新繪製。算法
function drawAll() { // 繪製全部圖形 } function repaint() { ctx.clearRect(0, 0, width, height); drawAll(); } 複製代碼
從上面的渲染機制咱們能夠天然的推導出 Canvas 的圖形渲染的性能瓶頸主要在三方面:canvas
咱們以繪製 1W 個圓做爲示例,來看一下單次繪製的成本:
前面咱們測試過圖形拾取和數學拾取的差別,1W 個圓的拾取須要 11ms左右,若是再加上圖形刷新的響應,能夠預期幀率會很是低。
咱們以鼠標在畫布上移動,移入一個圓這個圓變顏色,咱們來看一下畫布總體刷新時的效果:
若是咱們對圓進行動畫看一下幀率:
當一次渲染的圖形過多時,將一次渲染分紅屢次渲染,每次渲染時間增長几毫秒的間隔,這時候就不會卡頓:
鼠標在畫布上移動時,不斷的致使重繪,咱們只要可以保證 60 幀的重繪頻率便可,因此重繪的間隔不能小於 16ms,咱們能夠將持續渲染的同步機制,改爲每 16 ms 渲染的異步延遲渲染機制,這樣能夠大大下降重繪的頻率。
咱們能夠實現圖形的局部刷新,在局部刷新時僅清空圖形所在的包圍盒,全部與這個包圍盒相交的圖形所有刷新,這時候咱們來看上面的兩個示例:當鼠標在畫布上移動時,這就流暢多了,基本上沒有延遲
拾取的優化咱們在上面已經進行了簡單的說明,數學拾取的性能遠遠超過使用瀏覽器的方法來拾取,更多的拾取方案參考:2D 圖形拾取方案
因爲 webGL 的渲染是在 GPU 中進行,能夠顯著的提高渲染效率,能夠看下面的示例:
這些優化大部分已經在 2D 繪圖引擎 G上實現 ,2D 圖形的渲染優化主要在異步渲染、拾取加速和局部渲染三個方面,可是每一個方面都很是複雜,均可以獨立成章,本文僅僅是從思路上進行講解,更多更細的分析都會在後面提供獨立的章節進行講解,敬請期待!
AntV 官網:antv.vision/
2D 繪圖引擎 G:github.com/antvis/g