Search code examples
javascripthtml5-canvastooltipmousehover

Display tooltip in canvas graph


I am using html5 canvas element to draw a graph with dots denoting various points in here.

I want to display different tool-tip on different points on mouse hover.the text to be displayed as tool-tip will be provided by the user.

I tried but couldn't figure out how to add tool-tip to various points in the graph.The code I'm using for displaying dots is..

// Draw the dots
c.fillStyle = '#333';

for (var i = 0; i < data.values.length; i++) {
  c.beginPath();
  c.arc(getXPixel(data.values[i].X), getYPixel(data.values[i].Y), 4, 0, Math.PI * 2, true);
  c.fill();
}

What addition should I make in this code so that i am able to display user input as tool-tip?


Solution

  • You can display tooltips when your user moves over your chart's data-dot

    This tooltip is simply a second canvas which draws the text from the linked textbox and is positions itself above the data-dot.

    First you create an array to hold the tooltip info for each of your data-dots.

        var dots = [];
    

    For each tooltip, you will need:

    • The x/y coordinate of the data-dot,
    • The radius of the data-dot,
    • The id of the textbox you want to get the tip from.
    • You also need rXr which always == radius squared (needed during hit testing)

    Here is the code for creating tooltip info to be stored in dots[]

        // define tooltips for each data point
    
        for(var i = 0; i < data.values.length; i ++) {
            dots.push({
                x: getXPixel(data.values[i].X),
                y: getYPixel(data.values[i].Y),
                r: 4,
                rXr: 16,
                tip: "#text"+(i+1)
            });
        }
    

    Then you set up a mousemove handler that looks through the dots array. The tooltip is displayed if the user moves inside any data=dot:

        // request mousemove events
    
        $("#graph").mousemove(function(e){handleMouseMove(e);});
    
        // show tooltip when mouse hovers over dot
        function handleMouseMove(e){
          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);
    
          // Put your mousemove stuff here
          var hit = false;
          for (var i = 0; i < dots.length; i++) {
              var dot = dots[i];
              var dx = mouseX - dot.x;
              var dy = mouseY - dot.y;
              if (dx * dx + dy * dy < dot.rXr) {
                  tipCanvas.style.left = (dot.x) + "px";
                  tipCanvas.style.top = (dot.y - 40) + "px";
                  tipCtx.clearRect(0, 0, tipCanvas.width, tipCanvas.height);
                  tipCtx.fillText($(dot.tip).val(), 5, 15);
                  hit = true;
              }
          }
          if (!hit) { tipCanvas.style.left = "-200px"; }
        }
    

    [ Edited to fit into your code ]

    Here is code and a Fiddle: http://jsfiddle.net/m1erickson/yLBjM/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    <style>
        body{ background-color: ivory; margin-top:35px; }
        #wrapper{position:relative; width:300px; height:150px;}
        canvas{border:1px solid red;}
        #tip{background-color:white; border:1px solid blue; position:absolute; left:-200px; top:100px;}
    </style>
    
    <script>
    $(function(){
    
        var graph = document.getElementById("graph");
        var ctx = graph.getContext("2d");
        var tipCanvas = document.getElementById("tip");
        var tipCtx = tipCanvas.getContext("2d");
    
        var canvasOffset = $("#graph").offset();
        var offsetX = canvasOffset.left;
        var offsetY = canvasOffset.top;
    
        var graph;
        var xPadding = 30;
        var yPadding = 30;
    
        // Notice I changed The X values
        var data = { values:[
            { X: 0, Y: 12 },
            { X: 2, Y: 28 },
            { X: 3, Y: 18 },
            { X: 4, Y: 34 },
            { X: 5, Y: 40 },
            { X: 6, Y: 80 },
            { X: 7, Y: 80 }
        ]};
    
        // define tooltips for each data point
        var dots = [];
        for(var i = 0; i < data.values.length; i ++) {
            dots.push({
                x: getXPixel(data.values[i].X),
                y: getYPixel(data.values[i].Y),
                r: 4,
                rXr: 16,
                color: "red",
                tip: "#text"+(i+1)
            });
        }
    
        // request mousemove events
        $("#graph").mousemove(function(e){handleMouseMove(e);});
    
        // show tooltip when mouse hovers over dot
        function handleMouseMove(e){
          mouseX=parseInt(e.clientX-offsetX);
          mouseY=parseInt(e.clientY-offsetY);
    
          // Put your mousemove stuff here
          var hit = false;
          for (var i = 0; i < dots.length; i++) {
              var dot = dots[i];
              var dx = mouseX - dot.x;
              var dy = mouseY - dot.y;
              if (dx * dx + dy * dy < dot.rXr) {
                  tipCanvas.style.left = (dot.x) + "px";
                  tipCanvas.style.top = (dot.y - 40) + "px";
                  tipCtx.clearRect(0, 0, tipCanvas.width, tipCanvas.height);
                  tipCtx.fillText($(dot.tip).val(), 5, 15);
                  hit = true;
              }
          }
          if (!hit) { tipCanvas.style.left = "-200px"; }
        }
    

    // unchanged code follows

        // Returns the max Y value in our data list
        function getMaxY() {
            var max = 0;
    
            for(var i = 0; i < data.values.length; i ++) {
                if(data.values[i].Y > max) {
                    max = data.values[i].Y;
                }
            }
    
            max += 10 - max % 10;
            return max;
        }
    
        // Returns the max X value in our data list
        function getMaxX() {
            var max = 0;
    
            for(var i = 0; i < data.values.length; i ++) {
                if(data.values[i].X > max) {
                    max = data.values[i].X;
                }
            }
    
            // omited
          //max += 10 - max % 10;
            return max;
        }
    
        // Return the x pixel for a graph point
        function getXPixel(val) {
            // uses the getMaxX() function
            return ((graph.width - xPadding) / (getMaxX() + 1)) * val + (xPadding * 1.5);
            // was
          //return ((graph.width - xPadding) / getMaxX()) * val + (xPadding * 1.5);
        }
    
        // Return the y pixel for a graph point
        function getYPixel(val) {
            return graph.height - (((graph.height - yPadding) / getMaxY()) * val) - yPadding;
        }
    
            graph = document.getElementById("graph");
            var c = graph.getContext('2d');            
    
            c.lineWidth = 2;
            c.strokeStyle = '#333';
            c.font = 'italic 8pt sans-serif';
            c.textAlign = "center";
    
            // Draw the axises
            c.beginPath();
            c.moveTo(xPadding, 0);
            c.lineTo(xPadding, graph.height - yPadding);
            c.lineTo(graph.width, graph.height - yPadding);
            c.stroke();
    
            // Draw the X value texts
            var myMaxX = getMaxX();
            for(var i = 0; i <= myMaxX; i ++) {
                // uses data.values[i].X
                c.fillText(i, getXPixel(i), graph.height - yPadding + 20);
            }
            /* was
            for(var i = 0; i < data.values.length; i ++) {
                // uses data.values[i].X
                c.fillText(data.values[i].X, getXPixel(data.values[i].X), graph.height - yPadding + 20);
            }
            */
    
            // Draw the Y value texts
            c.textAlign = "right"
            c.textBaseline = "middle";
    
            for(var i = 0; i < getMaxY(); i += 10) {
                c.fillText(i, xPadding - 10, getYPixel(i));
            }
    
            c.strokeStyle = '#f00';
    
            // Draw the line graph
            c.beginPath();
            c.moveTo(getXPixel(data.values[0].X), getYPixel(data.values[0].Y));
            for(var i = 1; i < data.values.length; i ++) {
                c.lineTo(getXPixel(data.values[i].X), getYPixel(data.values[i].Y));
            }
            c.stroke();
    
            // Draw the dots
            c.fillStyle = '#333';
    
            for(var i = 0; i < data.values.length; i ++) {  
                c.beginPath();
                c.arc(getXPixel(data.values[i].X), getYPixel(data.values[i].Y), 4, 0, Math.PI * 2, true);
                c.fill();
            }
    
    
    }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <div id="wrapper">
            <canvas id="graph" width=300 height=150></canvas>
            <canvas id="tip" width=100 height=25></canvas>
        </div>
        <br><br>
        <input type="text" id="text1" value="text 1"/><br><br>
        <input type="text" id="text2" value="text 2"/><br><br>
        <input type="text" id="text3" value="text 3"/><br><br>
        <input type="text" id="text4" value="text 4"/><br><br>
        <input type="text" id="text5" value="text 5"/><br><br>
        <input type="text" id="text6" value="text 6"/><br><br>
        <input type="text" id="text7" value="text 7"/><br><br>
    </body>
    </html>