Detect mouseover of certain points within an HTML canvas?

前端 未结 7 1131
予麋鹿
予麋鹿 2020-12-02 14:45

I\'ve built an analytical data visualization engine for Canvas and have been requested to add tooltip-like hover over data elements to display detailed metrics for the data

相关标签:
7条回答
  • 2020-12-02 14:55

    I needed to do detect mouse clicks for a grid of squares (like cells of an excel spreadsheet). To speed it up, I divided the grid into regions recursively halving until a small number of cells remained, for example for a 100x100 grid, the first 4 regions could be the 50x50 grids comprising the four quadrants. Then these could be divided into another 4 each (hence giving 16 regions of 25x25 each). This requires a small number of comparisons and finally the 25x25 grid could be tested for each cell (625 comparisons in this example).

    0 讨论(0)
  • 2020-12-02 14:56

    Shadow Canvas

    The best method I have seen elsewhere for mouseover detection is to repeat the part of your drawing that you want to detect onto a hidden, cleared canvas. Then store the ImageData object. You can then check the ImageData array for the pixel of interest and return true if the alpha value is greater than 0.

    // slow part
    ctx.clearRect(0,0,canvas.width,canvas.height);
    ctx.fillRect(100,100,canvas.width-100,canvas.height-100);
    var pixels = ctx.getImageData(0,0,canvas.width,canvas.height).data;
    
    // fast part
    var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
    if (pixels[idx]) { // alpha > 0
      ...
    }
    

    Advantages

    • You can detect anything you want since you're just repeating the context methods. This works with PNG alpha, crazy compound shapes, text, etc.
    • If your image is fairly static, then you only need to do this one time per area of interest.
    • The "mask" is slow, but looking up the pixel is dirt cheap. So the "fast part" is great for mouseover detection.

    Disadvantages

    • This is a memory hog. Each mask is W*H*4 values. If you have a small canvas area or few areas to mask, it's not that bad. Use chrome's task manager to monitor memory usage.
    • There is currently a known issue with getImageData in Chrome and Firefox. The results are not garbage collected right away if you nullify the variable, so if you do this too frequently, you will see memory rise rapidly. It does eventually get garbage collected and it shouldn't crash the browser, but it can be taxing on machines with small amounts of RAM.

    A Hack to Save Memory

    Rather than storing the whole ImageData array, we can just remember which pixels have alpha values. It saves a great deal of memory, but adds a loop to the mask process.

    var mask = {};
    var len = pixels.length;
    for (var i=3;i<len;i+=4) if ( pixels[i] ) mask[i] = 1;
    
    // this works the same way as the other method
    var idx = 4 * (mouse_x + mouse_y * canvas.width) + 3;
    if (mask[idx]) {
      ...
    }
    
    0 讨论(0)
  • 2020-12-02 14:59

    This could be done using the method ctx.isPointInPath, but it is not implemented in ExCanvas for IE. But another solution would be to use HTML maps, like I did for this little library : http://phenxdesign.net/projects/phenx-web/graphics/example.htm you can get inspiration from it, but it is still a little buggy.

    0 讨论(0)
  • 2020-12-02 15:06

    There is a book by Eric Rowell named "HTML5 CANVAS COOKBOOK". In that book there is a chapter named "Interacting with the Canvas: Attaching Event Listeners to Shapes and Regions". mousedown, mouseup, mouseover, mouseout, mousemove, touchstart, touchend and touchmove events can be implemented. I highly suggest you read that.

    0 讨论(0)
  • 2020-12-02 15:07

    I would suggest overlaying an image map with proper coordinates set on the areas to match your canvas-drawn items. This way, you get tooltips AND a whole lot of other DOM/Browser functionality for free.

    0 讨论(0)
  • 2020-12-02 15:14

    You could handle the mousemove event and get the x,y coordinates from the event. Then you'll probably have to iterate over all your paths to test if the point is over the path. I had a similar problem that might have some code you could use.

    Looping over things in this way can be slow, especially on IE. One way you could potentially speed it up - and this is a hack, but it would be quite effective - would be to change the color that each path is drawn with so that it is not noticeable by humans but so that each path is drawn in a different color. Have a table to look up colors to paths and just look up the color of the pixel under the mouse.

    0 讨论(0)
提交回复
热议问题