html

 <div class="bigMap-box">
   <el-dialog :visible.sync="bigMapDialog" :modal="false" :fullscreen="true" >
     <div class="head-box">
       <div class="head-text">
         世界地图
       </div>
       <!-- 缩放按钮 -->
       <div class="scalingmode">
           <i class="el-icon-plus" id="plus" @click="plusClick(1)"></i>
           <i class="el-icon-minus" id="minus" @click="plusClick(0)"></i>
       </div>
       <!-- 位置详情 -->
       <!-- <div class="place-box" :style="{top:placeTop + 'px',left:placeLift + 'px'}"></div> -->
       <img src="../assets/cha.png" class=" close" alt="" @click="closebigMap">
       <img ref="bitmap" src="../assets/a1f0197aee81a9afaa3d0dacf2d8e96.jpg"  style="display: none;">
       <div class="canvas-box" ref="canvasBox">
         <canvas ref="myCanvas" width="2264" height="1474" @mousewheel="onmousewheel" @click="findRectClick"
         @mousedown="onmousedown" @mouseup="onmouseup" @mousemove="onmousemove"></canvas>
       </div>
     </div>
    </el-dialog>
  </div>

js

data () {
    return {
      bigMapDialog: false,
      cvsCtx: null,   // dom节点
      canvas: null,  //画布标签
      img: null, //地图图片标签
      stautsConfig: {
        IDLE: 0, // 鼠标无操作状态
        MOVE_START:1, //拖拽中
        MOVING:4 //拖拽结束
      },
      canvasInfo : {
        status: 0,          //拖拽状态
        offsetMouseEvtPos:{ x: null, y: null }, // 解决抖动问题  记录鼠标的位置
        offset: { x: 0 ,y: 0}, // 画布偏移默认为0
        scale: 1,  // 缩放比例
        scaleStep: 0.5, //滚轮放缩每滚一下增0.5或者将0.5
        maxScale: 2, //最大放大倍数
        minScale: 1, //最小缩小倍数
      },
      firstTime: '', // 按下时间
      lastTime: '',   // 抬起时间
      stopClick: false //是否触发画布点击事件
    }
  },
  created () {
    // 监听键盘事件
    window.addEventListener('keydown', e => {
      if (e.key === 'm') {
        this.openbigMap()
      }
    })
  },
  methods: {
    /** 打开打地图事件 */
    openbigMap () {
      this.bigMapDialog = !this.bigMapDialog
      if(this.bigMapDialog){
        // 在dialog框生成后再去初始化canvas
        this.$nextTick(() => {
          this.getCanvas()
        })
      }
    },
    /** 关闭大地图事件 */
    closebigMap () {
      this.bigMapDialog = false
    },
    /** 绘制canvas */
    getCanvas () {
      this.canvas = this.$refs.myCanvas
      this.cvsCtx = this.canvas.getContext('2d')
      this.bitmap = this.$refs.bitmap
      // 等待图片渲染完成后绘制
      this.bitmap.onload = () => {
        // 首次渲染默认显示中间区域 计算中间区域的偏移量
        this.canvasInfo.offset.x = (this.$refs.canvasBox.offsetWidth -this.canvas.width * this.canvasInfo.scale) / 2
        this.canvasInfo.offset.y = (this.$refs.canvasBox.offsetHeight -this.canvas.height  * this.canvasInfo.scale) /2
        this.drawImage()  // 绘制图像
      }
    },
    /** 图片转换canvas渲染 */
    drawImage () {
      // 重置前一个变换矩阵然后构建新的矩阵 a水平缩放绘图。b水平倾斜绘图。
      // c垂直倾斜绘图 d	垂直缩放绘图。e	水平移动绘图。f	垂直移动绘图。
      this.cvsCtx.setTransform(this.canvasInfo.scale, 0, 0,this.canvasInfo.scale, this.canvasInfo.offset.x, this.canvasInfo.offset.y )
      // clearRect() 方法清空给定矩形内的指定像素。
      this.cvsCtx.clearRect(0, 0, this.canvas.width * 2, this.canvas.height * 2)
      this.cvsCtx.drawImage(this.bitmap,0, 0, this.canvas.width, this.canvas.width)

    },
    /** 画面位置转换为canvas位置坐标 */
    getCanvasPosition(e,offset = {x: 0, y: 0},scale = 1){
      // (鼠标点击的坐标-画布的偏移量) /缩放系数 = 画布的坐标
      return {
          x: (e.offsetX - offset.x) / scale,
          y: (e.offsetY - offset.y)/ scale
      }
    },
    /** 记录画面鼠标的位置 */
    getMousePosition(e){
      return {
          x: e.offsetX,
          y :e.offsetY
      }
    },
    /** 鼠标滚轮事件 */
    onmousewheel (e)  {
      e.preventDefault() //阻止默认滚轮事件
      const canvasPosition = this.getCanvasPosition(e , this.canvasInfo.offset)
      // 解构赋值
      const { scaleStep } = this.canvasInfo
      // 计算xy轴缩放时产生的偏移量 原点放大类似与盒子居中要减去自身的一半 原点缩小相反
      const deltaX = canvasPosition.x / this.canvasInfo.scale * scaleStep
      const deltaY = canvasPosition.y / this.canvasInfo.scale * scaleStep
      if (e.deltaY > 0 && this.canvasInfo.scale < this.canvasInfo.maxScale) {
        this.canvasInfo.offset.x -= deltaX
        this.canvasInfo.offset.y -= deltaY
        this.canvasInfo.scale += scaleStep
      } else if (e.deltaY < 0 && this.canvasInfo.scale > this.canvasInfo.minScale) {
        this.canvasInfo.offset.x += deltaX
        this.canvasInfo.offset.y += deltaY
        this.canvasInfo.scale -= scaleStep
      }
      this.preventNull()
      this.drawImage ()
    },
    /** 鼠标按下 */
    onmousedown (e) {
      this.firstTime=new Date().getTime() //  记录按下时间
      this.canvasInfo.status = this.stautsConfig.MOVE_START // 记录鼠标进入1状态
      this.canvasInfo.offsetMouseEvtPos = this.getMousePosition(e) // 记录鼠标的坐标点
    },
    /** 鼠标弹起 */
    onmouseup () {
      this.canvas.style.cursor = 'default'
      this.lastTime = new Date().getTime()
      // 根据鼠标按下抬起的时间间隔判断是否触发鼠标点击事件小于200触发点击事件
      if(this.lastTime - this.firstTime<200){
        this.stopClick = true
      }else {
        this.stopClick = false
      }
      this.canvasInfo.status = this.stautsConfig.IDLE;
    },
    /** 鼠标移动 */
    onmousemove (e) {
      // const canvasPosition = this.getCanvasPosition(e , this.canvasInfo.offset, this.canvasInfo.scale)
      // 判断鼠标状态是否按下
      if(this.canvasInfo.status === this.stautsConfig.MOVE_START ) {
          this.canvas.style.cursor = 'move'
          this.canvasInfo.status = this.stautsConfig.MOVING //切换状态2 记录上一个偏移位置
          this.canvasInfo.offsetMouseEvtPos = this.getMousePosition(e) // 记录鼠标的坐标点 记录上一个偏移位置
      }else if (this.canvasInfo.status === this.stautsConfig.MOVING ) {
        //   计算偏移量 += 新的鼠标位置 - 上一个鼠标位置。 鼠标移动了多少相当于画布偏移了多少
        const mousePosition = this.getMousePosition(e)
        this.canvasInfo.offset.x += mousePosition.x - this.canvasInfo.offsetMouseEvtPos.x
        this.canvasInfo.offset.y += mousePosition.y - this.canvasInfo.offsetMouseEvtPos.y
        // preventNull函数要放到drawImage渲染函数上面否则会出现画面抖动
        this.preventNull()
        this.drawImage ()
        this.canvasInfo.offsetMouseEvtPos = mousePosition // 重置记录鼠标位置
      }
    },
    /** 防止容器留白 判断四个边界*/
    preventNull () {
      // 计算四个边界边界 阻止留白 限制最小偏移量为0 最大偏移量为 外部盒子的宽度-(画布宽度*缩放系数)用于缩放拖动
      var adaptSizeX = this.$refs.canvasBox.offsetWidth -(this.canvas.width * this.canvasInfo.scale)
      var adaptSizeY = this.$refs.canvasBox.offsetHeight -(this.canvas.height  * this.canvasInfo.scale)
      if(this.canvasInfo.offset.x>0){
        this.canvasInfo.offset.x = 0
      }else if (this.canvasInfo.offset.x < adaptSizeX){
        this.canvasInfo.offset.x = adaptSizeX
      }
      if(this.canvasInfo.offset.y>0){
        this.canvasInfo.offset.y = 0
      }else if(this.canvasInfo.offset.y < adaptSizeY){
        this.canvasInfo.offset.y = adaptSizeY
      }
    },
    /** 鼠标点击 */
    findRectClick (e) {
      if(this.stopClick){
        console.log(222)
      }
    },
     /** 缩放按钮点击事件 */
    plusClick (num) {
      console.log(num);
      if (num == 1 && this.canvasInfo.scale < this.canvasInfo.maxScale) {
        this.canvasInfo.scale += this.canvasInfo.scaleStep
      } else if (num == 0 && this.canvasInfo.scale > this.canvasInfo.minScale) {
        this.canvasInfo.scale -= this.canvasInfo.scaleStep
      }
      this.preventNull()
      this.drawImage ()
    },
  }

css

<style lang="less" scope="scope">
.head-box{
  position: relative;
  .head-text{
    position: absolute;
    background-color: #fff;
    top: 0;
    left: 0;
    width: 100%;
    height: 60px;
    text-align: center;
    font-size: 30px;
    font-weight: 500;
    line-height: 58px;
    color: #333333;
  }
  .close{
    position: absolute;
    top: 100px;
    right: 140px;
    width: 50px;
    height: 50px;
    cursor: pointer;
  }
  .scalingmode{
    position: absolute;
    top: 100px;
    left: 140px;
    width: 40px;
    height: 130px;
    border: 1px solid red;
    text-align: center;
    .el-icon-plus{
      color: red;
      font-size: 28px;
      margin-top: 10px;
      cursor: pointer;
    }
    .el-icon-minus{
      color: red;
      font-size: 28px;
      margin-top: 50px;
      cursor: pointer;
    }
  }
  .place-box{
    position: absolute;
    width: 200px;
    height: 100px;
    background-color: aqua;
    z-index: 1;
  }
  .canvas-box{
    width: 98vw;
    height: 98vh;
    margin: 0 auto;
    overflow: hidden;
  }
}
.el-dialog__header{
  display: none;
}
.el-dialog__body{
  padding: 0;
  overflow: hidden;
}
</style>

追加  由于手机触摸事件 没有offset属性 追加转换代码

// 把pagex转化为offsetx  触摸事件无offset属性
    _touchStart (e) {
      let imgTop = this.$refs.canvasBox.getBoundingClientRect().top
      let imgLeft = this.$refs.canvasBox.getBoundingClientRect().left
      let offsetX = Math.round(e.changedTouches[0].pageX - imgLeft)
      let offsetY = Math.round(e.changedTouches[0].pageY - imgTop)
      let el = {
        offsetX,
        offsetY
      }
      return el
    }

 

参考大佬作品,canvas可视化操作-拖拽、放缩、移动_哔哩哔哩_bilibili

Logo

前往低代码交流专区

更多推荐