原創: 蜀中亮子 玄說前端 4天前
起因
首先上圖:
今天,我們前端群問了一個這樣的問題,然後就開始了激烈的讨論。
那麼下面咱們一起來看看這個問題,這個問題問了兩個小問題:
1.如何在 canvas 上繪制多邊形?
2.滑鼠怎麼選中繪制的某一個圖形?
那麼咱們就來分為兩個問題解答。
繪制多邊形
要繪制一個多邊形,多邊形圖形的基本元素是路徑。路徑是通過不同顔色和寬度的線段或曲線相連形成的不同形狀的點的集合。一個路徑,甚至一個子路徑,都是閉合的。使用路徑繪制圖形需要一些額外的步驟。
- 首先,你需要建立路徑起始點
- 然後你使用畫圖指令去畫出路徑。
- 之後你把路徑封閉
- 一旦路徑生成,你就能通過描邊或填充路徑區域來渲染圖形。以上這些步驟會用到一些 API:
beginPath()
建立一條路徑,生成之後,圖形繪制指令被指向到路徑上生成路徑。
closePath()
閉合路徑之後圖形繪制指令又重新指向到上下文中。
stroke()
通過線條來繪制圖形輪廓。
fill()
通過填充路徑的内容區域生成實心的圖形。
詳解繪制過程
這裡詳細解答一下繪制的過程:
第一步,生成路徑,調用 beginPath,本質上路徑是有很多子路徑所構成的,這些子路徑全部在一個清單裡面,所有的子路徑(線、弧)構成圖形。而每次調用這個方法之後,清單都會被重置,然後就可以繪制新的圖形。(你需要在設定路徑之後指定你的起始位置);
第二步,調用指定函數繪制路徑;
第三步,閉合路勁 closePath(不是必須的);
筆式繪圖儀模型
繪制一個三角形例子:
var
在這個過程中,有一個比較有用的函數,moveTo,這個函數實際上畫不出來任何東西,它是屬于上面描述的路勁清單的一部分。
看下這個函數的作用:
moveTo()
将筆觸移動到指定的坐标 x 以及 y 上。
當 canvas 初始化或者 beginPath()調用後,你通常會使用 moveTo()函數設定起點。我們也能夠使用 moveTo()繪制一些不連續的路徑。
這個時候你可以想象一下在紙上畫東西,筆尖從一個點到另一個點的移動過程。這個過程的模式叫做
筆式繪圖儀模式。是以 canvas 2d 繪圖的模式也就是這種模式。
現在繪制多邊形就沒有什麼問題了。
canvas 上找出指定的圖形
首先,完成描述一下這個問題:按下滑鼠,如何判斷出選中了某一個圖形?
比如下圖:
滑鼠點選了這個不規則多邊形的内部,怎麼判斷?
第一反應就是 isPointInPath,或者是疊代所有圖形,拿滑鼠的點去與圖形的點碰撞檢測,這個方法可以用,但是适用場景比較少,還有就是性能開銷比較大,如果圖形太多,每一個都需要經過計算,那麼這個互動會變得非常的不友好。
有沒有其他方案了,在遊戲界有一個普遍使用的方案——包圍盒,什麼是包圍盒呢?我們以上面的圖形舉例,外面畫的紅線框就是這個多邊形的包圍盒。
很形象的一個例,就是公司發的月餅盒子,就是裡面圓圓的月餅 的包圍盒。
包圍盒的方案有個缺點,選取的範圍比較粗。比如上圖的紅框,框選了不是多邊形部分的内容。如果你想用包圍盒的方案來做,那就要分的足夠細,比如下圖:
分出來了多個包圍盒,這種情況在圖形特别複雜的時候,包圍盒這個方案就有點粗糙了。
還有下圖這種,實心和空心圓,用包圍盒也就非常的不友好。
那怎麼辦?
方案
如果想要快速選中某一個圖形,我們能不能對我們的每一個圖形有一個對應的 hash,而在滑鼠點選的時候,又能夠取到這個 hash。用 hash 的值,去找這個圖形,這個過程的時間複雜度是 O(1)。
比如在畫布的這些圖形:
在另一張一模一樣的畫布上,畫了這些圖形
上層畫布(顯示出來的)是正常的圖形,但是每個圖形配置設定一個 rgb 色值。
下層畫布(隐藏)用這個 rgb 色值做填充或者 stroke。
當滑鼠點選的時候,在隐藏畫布相同的位置,取一個像素點。
而這個像素點的rgb值就是我們要找的 hash。
至此,兩個問題已經解答了,這幾天中秋節,祝大家中秋節快樂,阖家幸福,人長久,共婵娟。 最後附上想進前端群的可以加微信,備注:玄說前端。 END獲得更多資訊關注公衆号
玄說前端