javascript 在画布图中显示工具提示
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17064913/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Display tooltip in canvas graph
提问by Lucy
I am using html5 canvas element to draw a graph with dots denoting various points in here.
我正在使用 html5 canvas 元素绘制一个图形,其中的点表示此处的各个点。
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?
我应该在此代码中添加哪些内容,以便能够将用户输入显示为工具提示?
回答by markE
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)
- 数据点的 x/y 坐标,
- 数据点的半径,
- 要从中获取提示的文本框的 ID。
- 您还需要 rXr 它总是 == 半径平方(在命中测试期间需要)
Here is the code for creating tooltip info to be stored in dots[]
这是用于创建要存储在 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:
然后设置一个 mousemove 处理程序来查看 dots 数组。如果用户在任何 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/
这是代码和小提琴: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>
回答by Neomedes
I tried markE's solution and it worked flawlessly, EXCEPT that when you scroll down just a little bit (e.g. when you have your canvas a little down the site).
我尝试了 markE 的解决方案,它完美地工作,除了当你向下滚动一点时(例如,当你的画布在网站下方时)。
Then the positions where your mouseover is recognized will shift to the bottom the same length and it could happen that they end up outside of 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:
当您使用 mouseEvent.pageX 和 mouseEvent.pageY 而不是 .clientX 和 .clientY 时,应该没问题。有关更多上下文,这是我的代码:
// 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";
}
});
回答by Bili
Maybe you could play with the "title" attribute of graph, and adapt its contents depending on the mouse position. Try adding this handler to your fiddle 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
在这里看到:更新的小提琴
Edit: in the code above, I choose to display the tooltip if the mouse is in a square of 10x10 around the point. Of course, this can be adapted. Moreover, there is probably more tests to do, especially before calling the value on getElementById, which could potentially return null.
编辑:在上面的代码中,如果鼠标位于点周围 10x10 的正方形中,我选择显示工具提示。当然,这可以适应。此外,可能还有更多测试要做,尤其是在调用 getElementById 上的值之前,这可能会返回 null。
回答by Saturnix
Short answer: as you've done it now, you can't.
简短的回答:正如你现在所做的那样,你不能。
Long answer: you can, but you need to get the exact mouse position every 30milliseconds or so. For each millisecond, you must check if the mouse is hovering over the dot, re-draw the screen and show the tooltip if he's doing it. Doing so by yourself can be tedious, this is why I use gee.js.
长答案:可以,但您需要每 30 毫秒左右获取一次准确的鼠标位置。对于每一毫秒,您必须检查鼠标是否悬停在点上,重新绘制屏幕并显示工具提示(如果他正在这样做)。自己这样做可能很乏味,这就是我使用gee.js 的原因。
Check out this example: http://jsfiddle.net/Saturnix/Aexw4/
看看这个例子:http: //jsfiddle.net/Saturnix/Aexw4/
This is the expression which controls the mouse hovering:
这是控制鼠标悬停的表达式:
g.mouseX < x + r && g.mouseX > x -r && g.mouseY > y -r && g.mouseY < y+r
回答by honkskillet
CSS ONLY method here. No javascript, JQUERY, or special library required. Lightweight, sexy.
CSS ONLY 方法在这里。不需要 javascript、JQUERY 或特殊库。轻盈,性感。
HTML
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
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/