Search code examples
javascriptdata-visualizationtooltipp5.js

How can I create a tooltip on mouse over in p5JS?


I have started playing with P5JS, but I wanted to create a tooltip on mouse over for each of the data points when the pointer is over it.

How can I do this? I have seen some examples using the map function, but unsure how this would work here.

Appreciate any help! I am not a JS dev just yet!


Solution

  • Generally speaking displaying tooltips that are specific to graphics displayed on a canvas involves "hit testing," which is to say: checking if the mouse is hovering over the specific graphics, and then some mechanism for displaying the tooltip.

    Since your graphics are all circles, hit testing is quite simple. For each circle that you draw, check the distance to the mouse position. If the distance is less than the radius of the circle than the mouse is hovering over that circle:

    let mouseDist = dist(posX, posY, mouseX, mouseY);
    if (mouseDist < earthquakeMag * 5) {
      // display tooltip, or set a flag to display the
      // tooltip after the rest of the graphics have been
      // displayed
    }
    

    As for displaying the tooltip you have two options: 1) you can rely on the native behavior of browser elements by setting the title attribute of the canvas element, or 2) you can display your own tooltip. The advantage of the former is that it is very trivial, but the advantage of the latter is that you have more control over where/when/how the tooltip is rendered.

    Option 1:

    let c;
    
    function setup() {
      c = createCanvas(500,500);
      // ...
    }
    
    function draw() {
      // ...
      for (var i = 0; i < this.data.getRowCount(); i++) {
        // ...
    
        let mouseDist = dist(posX, posY, mouseX, mouseY);
        if (mouseDist < earthquakeMag * 5) {
          c.elt.title = 'hit!';
        }
      }
    }
    

    Option 2:

      let tooltipText;
    
      for (var i = 0; i < this.data.getRowCount(); i++) {
        // ...
        
        let mouseDist = dist(posX, posY, mouseX, mouseY);
        if (mouseDist < earthquakeMag * 5) {
          // If we displayed the tooltip at this point then
          // some of the circles would overlap it.
          tooltipText = date_[i];
        }
    
        // ...
      }
      
      if (tooltipText) {
        // measure the width of the tooltip
        let w = textWidth(tooltipText);
    
        // save the current fill/stroke/textAlign state
        push();
    
        // draw a lightgray rectangle with a dimgray border
        fill('lightgray');
        stroke('dimgray');
        strokeWeight(1);
        // draw this rectangle slightly below and to the
        // right of the mouse
        rect(mouseX + 10, mouseY + 10, w + 20, 24, 4);
        textAlign(LEFT, TOP);
        noStroke();
        fill('black');
        text(tooltipText, mouseX + 20, mouseY + 16);
    
        // restore the previous fill/stroke/textAlign state
        pop();
      }