在 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...这样的坐标顺序一个一个的绘制三角形,所以线的顶点坐标就是一边一次的了。