Canvas、SVG实现鼠标滑过某个区域高亮显示的方案说明

1、需求背景:

用户提供了某个厂区的底图(就一张静态图片),在底图中,划分了10个不规则区域,给了10个区域的高亮、开灯效果图片(切好了图),鼠标滑过每个区域的时候,要高亮显示,开灯的时候,显示开灯效果;看到这个需求的时候,挺懵的,有点不知从何下手,但是分析后,有2种可以实现的方案,具体方案如下:

2、方案说明

方案1:传统的div去定位,绝对定位,用css实现hover效果,click的时候设置active效果。

这种方案会存在问题。首先,划分的区域是不规则的,如果强行用div去定位,可能会导致区域重叠的问题;其次鼠标划过,鼠标高亮效果要完全和底图贴合,这一点其实不容易实现。而且浏览器全屏后,可能会导致位置出现偏差的问题,基于以上的考虑,我放弃了此方案。

方案2:用Canvas和SVG的方式实现。不规则的区域,手动取点,绘制路径,鼠标划过或鼠标点击的时候,判断鼠标坐标点是否在Path路径内,如果在某个区域内,就显示对应效果。但是要考虑一个问题,因为Path的点,我们是在某个分辨率基准下取点的,这就导致在不同屏幕分辨率下,会导致,无法取到点的问题,所以要重新计算鼠标坐标点。这种方案,全屏后,也不会说出现位置偏差,后面采用了此种方案,具体实现过程如下:

1、找一个容器,放背景图

<template>
  <div ref="container" class="canvas-container">
    <canvas
      ref="canvas"
      width="980px"
      height="675px"
      @mousemove.stop="handleMouseEnter"
      @click.stop="handleCanvasClick"
      @mouseout.stop="renderCanvas"
    ></canvas>
  </div>
</template>

2、 绘制canvas区域,并且在每个区域写上文字标识,绘制10个区域

    drawImage(cb) {
      const that = this
      const img = new Image()
      img.src = backImg
      img.onload = function() {
        // 在Canvas中绘制图像
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
        for (let i = 0; i < that.svgShapeList.length; i++) {
          const shape = that.svgShapeList[i]
          ctx.fillStyle = '#ffff33' // 设置填充颜色
          ctx.font = 'bold 14px Arial'
          ctx.textBaseline = 'middle'     
          ctx.fillText(shape.name, shape.textX, shape.textY) // 绘制文本
        }
        if (cb) {
          cb()
        }
      }
    }
 /**
     * @description 鼠标移出,移除效果,如果存在开灯效果继续显示开灯效果
     */
    renderCanvas() {
      this.drawImage(() => {
        for (let i = 0; i < this.svgShapeList.length; i++) {
          // 保存绘图状态
          ctx.save()
          const shape = this.svgShapeList[i]
          if (shape.switchState) {
            this.fetchFile(shape)
          }
          // 恢复到之前保存的绘图状态
          ctx.restore()
        }
      })
    },
    /**
     * @description 获取外部文件
     */
    fetchFile(shape) {
      // ctx.fillStyle = 'rgb(255, 255, 0,0.2)'
      // ctx.fill(shape.path)
      var image = new Image()
      image.src = baseImg
      image.onload = async () => {
        var pattern = await ctx.createPattern(image, 'repeat')
        ctx.fillStyle = pattern
        ctx.fill(shape.path)
      }
    },

问题点:通过mousemove实现图片渲染的时候,由于会不断的onload图片,导致页面闪烁的问题

解决方法:把pattern 缓存起来,不要一致onload,可以在给ctx赋值后,同时给pattern赋值,那么fetchFile函数中,只需要做以下的操作即可:

/**
     * @description 获取外部文件
     */
    fetchFile(shape) {
        ctx.fillStyle = pattern
        ctx.fill(shape.path)
    },

 完美!!

3、接下来就是处理鼠标滑过、鼠标点击的问题了

判断坐标点位置的核心代码

const isInArea = ctx.isPointInPath(shape.path, mouseX, mouseY)

4、重新计算坐标点位:

  setRatio() {
      const initialWidth = 1920  
      const initialHeight = 937
      const targetWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
      const targetHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
      this.ratioX = initialWidth / targetWidth
      this.ratioY = initialHeight / targetHeight
    },

说明1920,937为绘制路径时,屏幕可视区域的基准值,这个根据自己的情况来定

然后再鼠标划过,鼠标点击的时候,要注意用当前坐标点乘以一个系数ratioX,ratioY

基本上就这样子了

反正就是要注意导入图片时,不要用路径,否则会出现闪烁的问题,因为一直从服务器请求资源,建议用base64。

完整代码就不粘了,有疑问或者有更好的方案,欢迎留言!

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值