返回 导航

HTML5 / CSS3

hangge.com

HTML5 - 让Canvas内部元素实现鼠标移入、移出效果(Tooltip提示效果)

作者:hangge | 2017-12-05 08:10
    Canvas 是一种非保留性的绘图界面,即不会记录过去执行的绘图操作,而是保持最终结果(构成图像的彩色像素)。
    如果想让 Canvas 变得具有交互性,比如用户可以选择、拖动画布上的图形。那么我们必须记录绘制的每一个对象,才能在将来灵活的修改并重绘它们,实现交互。
    在之前的文章中(点击查看)我演示了如何实现通过鼠标点击来选择图形,以及图形的拖动。本文接着介绍如何实现当鼠标移入到一个图形元素上,显示相应 tooltip 提示信息的功能。

1,效果图

(1)下面样例中点击“添加圆圈”按钮可以在画布上增加位置、大小、颜色都是随机的圆圈。
(2)点击“清空画布”按钮可以清除画布上所有圆圈。
(3)鼠标移动到任意圆圈上,该圆圈会出现黑色边框,同时在鼠标位置处会显示该圆圈的 tip 提示文本信息(跟随鼠标)。
(4)鼠标移出圆圈,该圆圈的黑色边框消失,同时 tip 提示框也会自动隐藏。

2,代码说明

(1)为了能够将圆圈对象保存起来,我们定义了一个叫 Circle() 的函数类创建自定义对象。同时要让这个对象能够保持数据,要使用关键字 this 来创建属性。

(2)drawCircles() 函数用来根据当前圆圈的集合来填充画布。drawToolTip() 函数用来绘制 toolTip 提示框。每次程序刷新画布时,会先使用 clearRect() 方法清除画布上的所有内容。但不用当心这样会造成画布闪烁,即画布上的圆圈一下子全部消失,然后一下子又重新出现。
因为 Canvas 针对这个问题进行了优化,会在所有绘图逻辑执行完毕后才清除或绘制所有内容,保证最终结果的流畅。

(3)我们监听 canvas 的鼠标移动事件(mousemove),当鼠标每次移动时都与所有的圆形进行碰撞检测。即计算鼠标当前位置是否落在某个形状里。对于圆圈而言,只要计算当前点与圆心的直线距离即可。
  • 如果找到符合条件的圆圈,则将其设置为选中,重绘时对该圆圈描黑边。同时再绘制一个提示框(包含提示框背景,以及上面的提示文字)。
  • 如果没找到符合条件的圆圈,则将之前选中的圆圈置为未选中(如果有的话),那么重绘所有圆圈均无黑边。且提示框也不再绘制。

3,样例代码

(高亮处表示与 tooltip 相关的代码)
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>hangge.com</title>

 <style>
     canvas {
       cursor: pointer;
       border: 1px solid black;
     }
 </style>
 <script>
    // 这个方法用来储存每个圆圈对象
    function Circle(x, y, radius, color) {
      this.x = x;
      this.y = y;
      this.radius = radius;
      this.color = color;
      this.isSelected = false;
    }

    // 保存画布上所有的圆圈
    var circles = [];

    var canvas;
    var context;

    window.onload = function() {
      canvas = document.getElementById("canvas");
      context = canvas.getContext("2d");

      canvas.onmousemove = onMouseMove;
    };

    //“添加圆圈”按钮点击
    function addRandomCircle() {
      // 为圆圈计算一个随机大小和位置
      var radius = randomFromTo(10, 60);
      var x = randomFromTo(0, canvas.width);
      var y = randomFromTo(0, canvas.height);

      // 为圆圈计算一个随机颜色
      var colors = ["green", "blue", "red", "yellow", "magenta", "orange", "brown",
                    "purple", "pink"];
      var color = colors[randomFromTo(0, 8)];

      // 创建一个新圆圈
      var circle = new Circle(x, y, radius, color);

      // 把它保存在数组中
      circles.push(circle);

      // 重新绘制画布
      drawCircles();
    }

    //“清空画布”按钮点击
    function clearCanvas() {
      // 去除所有圆圈
      circles = [];

      // 重新绘制画布.
      drawCircles();
    }

    //绘制圆圈
    function drawCircles() {
      // 清除画布,准备绘制
      context.clearRect(0, 0, canvas.width, canvas.height);

      // 遍历所有圆圈
      for(var i=0; i<circles.length; i++) {
        var circle = circles[i];

        // 绘制圆圈
        context.globalAlpha = 0.85;
        context.beginPath();
        context.arc(circle.x, circle.y, circle.radius, 0, Math.PI*2);
        context.fillStyle = circle.color;
        context.strokeStyle = "black";

        if (circle.isSelected) {
          context.lineWidth = 5;
        }
        else {
          context.lineWidth = 1;
        }
        context.fill();
        context.stroke();
      }
    }

    //绘制tooltip提示文字
    function drawToolTip(txtLoc, x, y) {
        context.save();
        var padding = 3;
        var font = "16px arial";
        context.font = font;
        context.textBaseline = 'bottom';
        context.fillStyle = 'yellow';

        //绘制ToolTip背景
        var width = context.measureText(txtLoc).width;
        var height = parseInt(font, 10);
        context.fillRect(x, y-height, width+padding*2, height+padding*2);

        //绘制ToolTip文字
        context.fillStyle = '#000';
        context.fillText(txtLoc, x+padding, y+padding);

        context.restore();
    }

    //当前选中的圆圈
    var previousSelectedCircle;

    //鼠标移动事件
    function onMouseMove(e) {
      // 清除之前选择的圆圈
      if (previousSelectedCircle != null) {
        previousSelectedCircle.isSelected = false;
        previousSelectedCircle = null;
      }

      // 取得画布上被单击的点
      var clickX = e.pageX - canvas.offsetLeft;
      var clickY = e.pageY - canvas.offsetTop;

      // 查找被单击的圆圈
      for(var i=circles.length-1; i>=0; i--) {
        var circle = circles[i];
        //使用勾股定理计算这个点与圆心之间的距离
        var distanceFromCenter = Math.sqrt(Math.pow(circle.x - clickX, 2)
            + Math.pow(circle.y - clickY, 2))
        // 判断这个点是否在圆圈中
        if (distanceFromCenter <= circle.radius) {
          previousSelectedCircle = circle;

          //选择新圆圈
          circle.isSelected = true;

          //停止搜索
          break;
        }
      }

      //更新显示,重绘圆圈
      drawCircles();
      //如果当前鼠标位置有圆圈,还要显示tip
      if(previousSelectedCircle != null){
        drawToolTip("颜色:" + previousSelectedCircle.color, clickX, clickY);
      }
    }

    //在某个范围内生成随机数
    function randomFromTo(from, to) {
      return Math.floor(Math.random() * (to - from + 1) + from);
    }
 </script>
</head>

<body>

  <canvas id="canvas" width="360" height="300">
  </canvas>

  <div>
    <button onclick="addRandomCircle()">添加圆圈</button>
    <button onclick="clearCanvas()">清空画布</button>
  </div>

</body>
</html>
评论

全部评论(0)

回到顶部