在 pixi.js 中,我們可以繪制矩形、原型、多邊形等各種圖形,也可以通過 lineTo,bezierCurveTo 方法來繪制線條。
繪制矩形、多邊形等這種具有面積的圖形后,我們可以通過 addListener 方法添加鼠標事件,但是繪制直線、貝塞爾曲線等線條后,這樣做卻不行了。為什么呢?因為線條是一條路徑,我們通過路徑坐標點繪制后,雖然通過設置線條的寬度后能看到這是一條線,但是實際上它也只是一條路徑,沒有寬度沒有面積,對於一個沒有面積的對象沒法將鼠標移動到它上面。
上圖就是我們通過 lineTo 和 bezierCurveTo 繪制的線條,我們直接給它添加點擊事件是無效的,如下繪制直線的代碼:
let line = new Graphics();
line.lineStyle(8, 0xFFFFFF, 1);
line.moveTo(0, 0); // moveTo lineTo 繪制線段
line.lineTo(80, 50);
line.x = 120;
line.y = 30;
// 在這里我們添加鼠標事件無效
line.interactive = true;
line.cursor = 'pointer'
line.addListener('click', () => { console.log('line click') })
stage.addChild(line);
hitArea
那么怎樣才能給線添加事件呢?graphics 對象中有個 hitArea 屬性,給它設置一個區域,鼠標就能在這個區域內觸發時間。
有一個簡單方法可以在一定程度上添加事件,但是事件是綁定在一個矩形區域內,雖然有時會有用。
line.hitArea = line.getBounds() // 通過 getBounds 獲取這條線的邊界區域
上面通過 getBounds 添加的區域如圖所示,在整個紅框內都能觸發。
但是我們通常是只希望鼠標移動到線上才觸發,那就需要用到更高級的東西了。
graphics.geometry.points
在 graphics 對象中有個 geometry 屬性,里面還有一個 points 屬性記錄了這個圖形渲染后的頂點信息。比如這條直線就可以通過 line.geometry.points 獲取。
注意:geometry.points 需要在渲染完成后才能獲取到,所以獲取之前先用 renderer.render() 進行渲染。
頂點信息里面有什么呢?其實就是這個圖形渲染后的各個頂點坐標,通過控制台輸出可以查看:
這就是貝塞爾曲線的頂點坐標信息,簡單的用圖來描述:
直線就有4個頂點坐標信息,貝塞爾曲線就是若干個。
下面我們就需要通過這些頂點坐標繪制成一個與原線條重合的多邊形區域賦值給 hitArea 就可以完美的在線條上觸發鼠標事件了。可是怎么將這些坐標繪制成多邊形區域呢?
首先通過文檔可以查閱到通過 PIXI.Polygon 可以將坐標生成一個區域,那么我們就這么寫嗎?
const path = bezierLine.geometry.points
bezierLine.hitArea = new PIXI.Polygon(path)
通過嘗試並不正確,你們也可以試下用這些坐標繪制一個多邊形出來會是什么樣的,總之很奇怪就是了。
然后我通過觀察直線的 geometry.points 發現,坐標的位置似乎有什么不對。
points 中坐標的順序如圖所示,通過1234的順序繪制出的多邊形明顯與預期的不符。然后我將3和4的位置交換,那就是正確的了。
直線可以簡單的交換3和4的坐標位置(繪制順序也就是1243),貝塞爾曲線怎么辦呢,這到底是怎樣的規律呢,還是沒有規律?
后面又經過測試得到了正確的規律:
如圖所示,頂點順序就是線的兩邊不斷交替的結果。
那就很簡單了,只需要寫個算法將頂點順序轉換成以下線條走向的順序就行了:
區域會自動閉合形成一個覆蓋在線條上的多邊形,這樣就能完美觸發線條的事件了。
那么為什么頂點順序是一邊一次的呢?
其實很簡單,webGL 繪圖是通過三角形來繪制的,就像3D建模之類的一樣,三角形是繪圖的基本元素之一。
這樣間隔的坐標,webGL 繪制的時候就是通過123,234,345,456...這樣的坐標順序一個一個的繪制三角形,所以線的頂點坐標就是一邊一次的了。