Detect mouseover of certain points within an HTML canvas?

前端 未结 7 1155
予麋鹿
予麋鹿 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: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

提交回复
热议问题