david2tdw / blog

学习记录
1 stars 1 forks source link

[canvas] Canvas折线图带tooltip提示 #181

Open david2tdw opened 4 years ago

david2tdw commented 4 years ago
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>tooltip</title>
    <style>
      canvas {
        cursor: pointer;
        border: 1px solid black;
      }
    </style>
    <script>
      // 保存每个点的数据
      function Point(x, y, radius, color, value) {
        this.x = x
        this.y = y
        this.radius = radius
        this.color = color
        this.value = value
        this.isSelected = false
      }

      var points = []
      var previousSelectedPoint

      var canvas
      var context

      window.onload = function () {
        canvas = document.getElementById('canvas')
        context = canvas.getContext('2d')
        // 清空画布
        clearCanvas()
        canvas.onmousemove = onMouseMove
      }

      // 绘制 chart
      function drawChart() {
        /*开始绘制外边框*/
        context.beginPath()
        context.strokeStyle = '#cdc9c4'
        context.lineWidth = 4
        context.moveTo(100, 400)
        context.lineTo(1020, 400)
        context.stroke()

        context.beginPath()
        context.strokeStyle = '#cdc9c4'
        context.lineWidth = 4
        context.moveTo(100, 400)
        context.lineTo(100, 0)
        context.stroke()

        /*开始绘制横向内边框*/
        context.beginPath()
        context.strokeStyle = '#e7e5e2'
        context.lineWidth = 1
        for (let i = 1; i <= 6; i++) {
          context.moveTo(100, 400 - 60 * i)
          context.lineTo(980, 400 - 60 * i)
          context.stroke()
        }
        /*开始绘制竖向内边框*/
        for (let i = 1; i <= 11; i++) {
          context.moveTo(100 + 80 * i, 400)
          context.lineTo(100 + 80 * i, 40)
          context.stroke()
        }

        /*开始绘制竖轴文字*/
        context.font = '16px SimHei'
        context.fillStyle = '#00c5de'
        //从坐标点(50,345)开始绘制文字
        for (let i = 0; i <= 6; i++) {
          context.fillText(10 * i + '万', 50, 405 - 60 * i)
        }

        /*开始绘制横轴文字*/
        for (let i = 1; i <= 12; i++) {
          context.fillText('2017-' + i, 80 * i, 440)
        }
        context.stroke()
      }

      // 将所有的点添加到points中
      function addAllPoints(context) {
        // 模拟的每月数据
        var arr = [22, 40, 50, 60, 32, 11, 0, 45, 50, 15, 20, 55]
        for (let i = 0; i < arr.length; i++) {
          let x = 100 + 80 * i
          let y = 400 - arr[i] * 6

          // let point = new Point(x, y, 9, 0, Math.PI * 2)
          let point = new Point(x, y, 9, 'pink', arr[i])
          points.push(point)
        }

        // 绘制点
        drawPoints()
      }

      function drawPoints() {
        for (let i = 0; i < points.length; i++) {
          let point = points[i]
          context.globalAlpha = 0.85
          context.beginPath()
          context.arc(point.x, point.y, point.radius, 0, Math.PI * 2)
          context.fillStyle = '#00c5de'
          context.strokeStyle = '#00c5de'

          context.lineWidth = 1

          // 绘制选中的点
          if (point.isSelected) {
            context.strokeStyle = '#dc128a'
          }

          context.fill()
          context.closePath()
          context.stroke()

          //绘制连接圆的折线
          context.beginPath()
          context.strokeStyle = '#00c5de'
          context.lineWidth = 1

          context.moveTo(point.x, point.y)
          // console.log(points[i + 1])
          if (points[i + 1]) {
            context.lineTo(points[i + 1].x, points[i + 1].y)
          }

          context.stroke()
        }
      }

      function onMouseMove(evt) {
        // 清空画布
        clearCanvas()

        // 清除之前选择的圆圈
        if (previousSelectedPoint != null) {
          previousSelectedPoint = null
        }

        // 取得画布上被单击的点
        var mousePosition = getMousePos(canvas, evt)
        for (let i = 0; i < points.length; i++) {
          let point = points[i]
          // 使用勾股定理计算这个点与圆心之间的距离
          let distanceFromCenter = Math.sqrt(
            Math.pow(point.x - mousePosition.x, 2) + Math.pow(point.y - mousePosition.y, 2)
          )
          // 判断这个点是否在圆圈中
          if (distanceFromCenter <= point.radius) {
            // 选择新圆圈
            previousSelectedPoint = point
            point.isSelected = true
            // 停止搜索
            break
          }
        }
        // 绘制点
        drawPoints()
        // 如果当前鼠标位置有圆圈,还要显示tip
        if (previousSelectedPoint != null) {
          drawToolTip('当前值为:' + previousSelectedPoint.value + '万', mousePosition.x, mousePosition.y)
        }
      }

      //获取鼠标在canvas画布上的位置(**不是浏览器窗口的鼠标位置)
      function getMousePos(canvas, evt) {
        // var rect = canvas.getBoundingClientRect()
        // return {
        //   x: evt.clientX - rect.left * (canvas.width / rect.width),
        //   y: evt.clientY - rect.top * (canvas.height / rect.height),
        // }
        var clickX = evt.pageX - canvas.offsetLeft
        var clickY = evt.pageY - canvas.offsetTop
        return {
          x: clickX,
          y: clickY,
        }
      }

      /// 清空画布
      function clearCanvas() {
        points = []
        context.clearRect(0, 0, canvas.width, canvas.height)
        drawChart()
        addAllPoints()
      }

      //绘制tooltip提示文字
      function drawToolTip(txtLoc, x, y) {
        context.save()
        var padding = 3
        var gap = 10
        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 - gap, width + padding * 2, height + padding * 2)

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

        context.restore()
      }
    </script>
  </head>
  <body>
    <div class="myCanvas-cont">
      <canvas id="canvas" width="1100" height="500">
        您的浏览器暂不支持canvas
      </canvas>
    </div>
  </body>
</html>

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

david2tdw commented 4 years ago

canvas.save() 和 canvas.restore():

当我们对画布进行旋转,缩放,平移等操作的时候其实我们是想对特定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么之后在画布上的元素都会受到影响,所以我们在操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响。

canvas.save() canvas.restore() 作用