21

我正在使用 html5 画布元素来绘制一个图形,其中的点表示此处的各个点。

我想在鼠标悬停的不同点上显示不同的工具提示。要显示为工具提示的文本将由用户提供。

我试过但不知道如何将工具提示添加到图中的各个点。我用于显示点的代码是..

// 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();
}

我应该在此代码中添加什么内容,以便能够将用户输入显示为工具提示?

4

6 回答 6

28

当您的用户在图表的数据点上移动时,您可以显示工具提示

此工具提示只是第二个画布,它从链接的文本框中绘制文本并将其自身定位在数据点上方。

首先,您创建一个数组来保存每个数据点的工具提示信息。

    var dots = [];

对于每个工具提示,您将需要:

  • 数据点的 x/y 坐标,
  • 数据点的半径,
  • 您要从中获取提示的文本框的 ID。
  • 您还需要 rXr 始终 == 半径平方(在命中测试期间需要)

这是用于创建要存储在点 [] 中的工具提示信息的代码

    // 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)
        });
    }

然后你设置了一个 mousemove 处理程序来查看点数组。如果用户在任何 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"; }
    }

[编辑以适合您的代码]

这是代码和小提琴: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"; }
    }

// 不变的代码如下

    // 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>
于 2013-06-12T16:48:22.773 回答
3

我尝试了markE 的解决方案,它完美地工作,除了当你向下滚动一点时(例如,当你的画布在站点下方一点时)。

然后,您的鼠标悬停被识别的位置将移动到底部相同的长度,并且可能会发生它们最终在画布之外并且根本不会被识别......

当您使用 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";
    }
});
于 2014-03-20T13:30:37.147 回答
2

也许您可以使用图形的“标题”属性,并根据鼠标位置调整其内容。尝试将此处理程序添加到您的小提琴代码中:

    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);

见这里:更新的小提琴

编辑:在上面的代码中,如果鼠标在该点周围 10x10 的正方形中,我选择显示工具提示。当然,这是可以调整的。此外,可能还有更多测试要做,尤其是在调用 getElementById 上的值之前,这可能会返回 null。

于 2018-12-19T09:50:21.127 回答
1

非常感谢上面的答案,因为它们帮助我制定了自己的解决方案,所以我想为那些试图做同样事情的人做出贡献。

HTML

<!-- https://jsfiddle.net/7kj2g5ur/ -->
<!-- https://stackoverflow.com/questions/17064913/display-tooltip-in-canvas-graph -->
<!-- https://stackoverflow.com/questions/1038727/how-to-get-browser-width-using-javascript-code -->
<!-- http://phrogz.net/tmp/canvas_zoom_to_cursor.html -->
<!-- https://www.w3schools.com/jsref/jsref_regexp_newline.asp -->
<!-- https://stackoverflow.com/questions/17064913/display-tooltip-in-canvas-graph -->
<!-- https://stackoverflow.com/questions/1134586/how-can-you-find-the-height-of-text-on-an-html-canvas -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>V5</title>
    <link href="style.css" rel="stylesheet">
</head>
<body>
    <canvas id="canvas" width="1000" height="500"></canvas>
    <script src="functions.js"></script>
    <script src="script.js"></script>
</body>
</html>

CSS

body { 
    background:#eee;
    /* text-align:center; */
    margin: 0;
    padding: 0;
}

#canvas { 
    display:block; 
    background:#fff;
    border:1px solid #ccc;
}

JS

/**
 * Mensura a largura e altura da fonte
 * @param {*} text 
 * @param {*} font 
 * @returns 
 */
function measureText(text, font) {
    const span = document.createElement('span');
    span.appendChild(document.createTextNode(text));
    Object.assign(span.style, {
        font: font,
        margin: '0',
        padding: '0',
        border: '0',
        whiteSpace: 'nowrap'
    });
    document.body.appendChild(span);
    const {width, height} = span.getBoundingClientRect();
    span.remove();
    return {width, height};
}

/**
 * Desenha o ponto na tela
 * @param {*} context 
 * @param {*} x 
 * @param {*} y 
 * @param {*} circleRadius 
 * @param {*} fillStyle 
 * @param {*} labels 
 */
function drawPoint(context,x,y,circleRadius,fillStyle,labels=null) {

    context.beginPath();

    context.fillStyle=fillStyle;

    var point = new Path2D();

    point.arc(x,y, circleRadius, 0, 2 * Math.PI);

    context.fill(point);
    
    if(labels)
    {
        drawTooltips(context,x,y,labels);
    }
    context.closePath();
}

/**
 * Desenha o tooltip na tela
 * @param {*} context 
 * @param {*} x 
 * @param {*} y 
 * @param {*} label 
 * @param {*} alignY 
 */
function drawTooltip(context,x,y,label,alignY=10) {

    const { width, height } = measureText(label, '8px Arial, Helvetica, sans-serif');

    const reactWidth = width + 10;
    const reactHeight = height + 10;
    const reactX = x+12;
    const reactY = y-alignY;
    const labelX = reactX+((reactWidth-width)/2);
    const labelY = reactY+12;

    context.beginPath();

    context.fillStyle = "black";

    context.fillRect(reactX,reactY,reactWidth,reactHeight);
    
    context.font = '8px Arial, Helvetica, sans-serif';

    context.strokeStyle = 'white';

    context.strokeText(label,labelX,labelY);

    context.closePath();
}

/**
 * Desenha todos os tooltips na tela
 * @param {*} context 
 * @param {*} x 
 * @param {*} y 
 * @param {*} labels 
 */
function drawTooltips(context,x,y,labels) {
    for (const key in labels) {
        if (Object.hasOwnProperty.call(labels, key)) {
            const label = labels[key];
            drawTooltip(context,x,y+(key*20),label,labels.length*10);
        }
    }
}

const canvas = document.getElementById('canvas');

const context = canvas.getContext('2d');

drawPoint(context,50,50,5,'black',[
    'LOREM 1',
    'Lorem ipsum dolor sit amet',
    'View more'
]);

drawPoint(context,150,100,5,'black',[
    'LOREM 2',
    'Lorem ipsum dolor sit amet',
    'View more'
]);
于 2021-10-18T14:07:13.817 回答
0

简短的回答:正如你现在所做的那样,你不能。

长答案:你可以,但你需要每 30 毫秒左右获得一次精确的鼠标位置。对于每一毫秒,您必须检查鼠标是否悬停在点上,重新绘制屏幕并显示工具提示,如果他正在这样做。自己这样做可能很乏味,这就是我使用gee.js的原因。

看看这个例子:http: //jsfiddle.net/Saturnix/Aexw4/

这是控制鼠标悬停的表达式:

g.mouseX < x + r && g.mouseX > x -r && g.mouseY > y -r && g.mouseY < y+r
于 2013-06-12T13:24:48.467 回答
-4

此处仅使用 CSS 方法。不需要 javascript、JQUERY 或特殊库。轻盈,性感。

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;
}

这是小提琴。 http://jsfiddle.net/honkskillet/RKnEu/

于 2013-11-08T01:39:35.830 回答