Hit-testing SVG shapes?

2019-01-05 03:11发布

The browsers which have implemented parts of the SVG spec (Firefox etc) do hit-testing for us for free - if I attach a mousedown listener on an SVG object, I get notified whenever the shape is clicked. This is amazing, especially for complex polygon shapes.

I'm wondering if there's a way I can utilize this feature for a bit more hit testing. I want to know if a given rectangle intersects any of my SVG shapes.

For example, I add 3 complex polygons to my element. Now I want to know if rectangle (40, 40, 100, 100) intersects any of them. Does anyone have an idea how I could possibly hook into the already great hit-testing support available, instead of adding all this code myself?

Thanks

4条回答
贼婆χ
2楼-- · 2019-01-05 03:23

getIntersectionList works fine in Opera. My problem is that the functions in the SVG 1.1 Full spec regarding this requires that elements must be rendered (and possible target for pointer events) in order to be detected as hit. Unfortunately this makes these functions useless for hit testing in a game world where only part of the world is currently visible.

查看更多
叛逆
3楼-- · 2019-01-05 03:39

Chrome's version of checkIntersection (and getIntersectionList) tests the element bounding boxes, rather than the elements themselves. I was able to write my own checkIntersection which works on chrome by using a canvas with this fairly involved approach which seems to work well for small rectangles and will be slow for large ones, so it's nice for hit testing. This technique will work as a polyfill for checkIntersection in Chrome, for small rectangles and possibly other browsers that have broken implementations of checkIntersection.

  1. Create an image that uses a data URI containing your SVG's outerHTML (you may need to include style rules in it as well), like so (this image doesn't have to be in the page). You can use an onload event handler to determine when it's loaded if you need to.
  2. Create a canvas to use for your hit-test rectangle (this canvas doesn't need to be in the page)

To test if a rectangle intersects with any of your shapes, do this:

  1. Make sure the canvas is the same size as your rectangle (set its width and height)
  2. Clear the canvas using the canvas context clearRect() method
  3. Draw the SVG on the canvas at -x, -y so that the portion of the image that overlaps the canvas corresponds to the area you want to test using drawImage()
  4. Fetch the canvas's ImageData using the context's getImageData(). Every 4th element of the data array is the alpha byte and a nonzero value means part of your SVG overlaps the rectangle. If all of the 4th bytes are 0, then your SVG did not intersect the rectangle.
查看更多
我想做一个坏孩纸
4楼-- · 2019-01-05 03:41

The SVG 1.1 DOM has just the right method (unfortunately it's not yet implemented in mozilla):

var nodelist = svgroot.getIntersectionList(hitrect, null);

For a full working example see here.

查看更多
唯我独甜
5楼-- · 2019-01-05 03:46

I don't know of any way of intersecting a whole rectangle. But you can intersect a point, so you could build a more complicated check out of that:

var el= document.elementFromPoint(x, y);

will give you the highest-stacked element at a particular page-relative co-ordinate. You'll get the <svg> element if no shapes inside the SVG are hit.

This is a non-standard Mozilla extension, but it works on WebKit as well. Unfortunately, though it exists in Opera, it won't look inside <svg>, so on that browser the element will always be the SVGSVGElement.

查看更多
登录 后发表回答