Display tooltip in canvas

I use the html5 canvas element to draw a graph with dots denoting different points here .

I want to display different hints at different points on hover. The text that will be displayed as a hint will be provided by the user.

I tried, but could not figure out, how to add a hint to various points on the chart. The code I use to display the 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 additions should I make in this code so that I can display user input as a tooltip?

+11
javascript html5-canvas tooltip mousehover
source share
5 answers

You can display tooltips when a user moves around your point-to-point chart

This tooltip is just the second canvas that draws text from the linked text box and takes up positions above the data point.

First, you create an array to store tooltip information for each of your data points.

  var dots = []; 

For each hint you will need:

  • X / y coordinate of the data point,
  • Radius of the data point,
  • The identifier of the text field from which you want to receive feedback.
  • You also need rXr, which is always equal to the square of the radius (necessary during testing when testing)

Here is the code for creating tooltip information that will 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 create a mousemove handler that looks at an array of points. A 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 be inserted into your code]

Here is the code and script: 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"; } } 

// immutable 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> 
+20
source share

I tried the markE solution and it worked flawlessly, EXCLUDING this when you scroll a bit (for example, when you have a canvas a bit down the site).

Then the positions at which your mouse pointer is recognized will be shifted to the bottom of the same length, and it may happen that they fall outside the canvas and will not be recognized at all ...

When you use mouseEvent.pageX and mouseEvent.pageY instead of .clientX and .clientY, you should be fine. For more context, here is my code:

 // Filling the dots var dots = []; // [...] dots.push({ x: xCoord, y: yCoord, v: value, r: 5, tooltipRadius2: 7*7 // a little increased radius for making it easier to hit }); // [...] var tooltipCanvas = $('#tooltipCanvas')[0]; var tooltipCtx = tooltipCanvas.getContext('2d'); var canvasOffset = canvas.offset(); canvas.mousemove(function (e) { // getting the mouse position relative to the page - not the client var mouseX = parseInt(e.pageX - canvasOffset.left); var mouseY = parseInt(e.pageY - canvasOffset.top); 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.tooltipRadius2) { // show tooltip to the right and below the cursor // and moving with it tooltipCanvas.style.left = (e.pageX + 10) + "px"; tooltipCanvas.style.top = (e.pageY + 10) + "px"; tooltipCtx.clearRect(0, 0, tooltipCanvas.width, tooltipCanvas.height); tooltipCtx.textAlign = "center"; tooltipCtx.fillText(dot.v, 20, 15); hit = true; // when a dot is found, don't keep on searching break; } } if (!hit) { tooltipCanvas.style.left = "-200px"; } }); 
+1
source share

The short answer is: how you did it now, you cannot.

Long answer: you can, but you need to get the exact mouse position every 30 milliseconds or so. For every millisecond, you must check to see if the mouse hovers over the dot, re-draw the screen and show a tooltip if it does. Doing this can be tedious, so I use gee.js.

Check out this example: http://jsfiddle.net/Saturnix/Aexw4/

This is the expression that controls mouse freezing:

 g.mouseX < x + r && g.mouseX > x -r && g.mouseY > y -r && g.mouseY < y+r 
0
source share

Maybe you could play around with the "title" attribute of the chart and adapt its contents depending on the position of the mouse. Try adding this handler to your script code:

  graph.addEventListener("mousemove", (function(evt) { var rect = evt.target.getBoundingClientRect(); var x = evt.clientX - rect.left; var y = evt.clientY - rect.top; var xd, yd; graph.title = ""; for(var i = 0; i < data.values.length; i ++) { xd = getXPixel(data.values[i].X); yd = getYPixel(data.values[i].Y); if ((x > xd-5) && (x < xd+5) &&(y > yd-5) && (y < yd+5) ) { graph.title = document.getElementById("text"+(i+1)).value; break; } } }), false); 

See here: Updated fiddle

0
source share

The CSS method ONLY. No javascript, JQUERY or special library needed. Lightweight, sexy.

HTML

 <!DOCTYPE html> <body> <br /> <br /> <br /> <br /> <span class="popup" popupText="This is some popup text">Locate </span> <img src="http://upload.wikimedia.org/wikipedia/en/thumb/f/f4/The_Scream.jpg/220px-The_Scream.jpg" /> <!--I used an image here but it could be anything, including a canvas--> </body> </html> 

CSS

 .popup{ position:absolute; /*allows span to be on top of image*/ border: solid; /*a border, just for demonstration purposes*/ border-color: #333 ; border-width: 1px; width:220px; /*set the height, width equal to the size of your ing/canvas*/ height:280px; } /*this bit draws the bottom arrow of the popup, it can excluded entire if you don't want it*/ .popup:hover:before{ border: solid; border-color: #333 transparent; border-width: 6px 6px 0 6px; bottom: 300px; content: ""; left: 40px; position: absolute; z-index: 99; } /*this bit draw the main portion of the popup, including the text*/ .popup:hover:after{ background: #333; background: rgba(0,0,0,.8); border-radius: 5px; bottom: 306px; color: #fff; content: attr(popupText); /*this is where the text gets introduced.*/ left: 20px; padding: 5px 15px; position: absolute; z-index: 98; width: 150px; } 

Here is the fiddle. http://jsfiddle.net/honkskillet/RKnEu/

-3
source share

All Articles