从零开始使用Konva,画图并绑定节点。

实战可行,vue3+vite+ts实现
在这里插入图片描述
在这里插入图片描述

实现电子地图,左侧列表可拖拽绑定
地图可绑定点设备坐标

安装

npm install konva 

插件引入

import Konva from 'konva'
import { getImgUrl } from '@/utils'
export class konvaManager {
  public stage: any
  public layer: any
  private readonly width: number = window.innerWidth - 400
  init(canvas: any) {
    const stage = new Konva.Stage({
      container: canvas,
      height: 700,
      width: this.width
    })
    const layer = new Konva.Layer()
    stage.add(layer)
    this.stage = stage
    this.layer = layer
    // this.sacleFun()
  }
  // 当成一个节点,绑定背景图
  initBgImg(url: string, y: number = 150) {
    const bgImg = new Image()
    bgImg.src = getImgUrl(url)
    bgImg.onload = () => {
      const kImage = new Konva.Image({
        image: bgImg,
        x: 50,
        y: y,
        width: bgImg.width,
        height: bgImg.height
      })
      this.layer.add(kImage)
    }
  }
  // 如果允许点拖拽编辑绑定,drag传true
  // img为项目需要,不同设备对应不同图标
  initDevice(device: any, img: string = '', drag: boolean = false) {
    const pointImg = new Image()
    pointImg.src = getImgUrl('info/map/point.png') // 图片地址
    pointImg.onload = () => {
      const kImage = new Konva.Image({
        draggable: drag,
        image: pointImg,
        x: device.x,
        y: device.y,
        width: pointImg.width,
        height: pointImg.height
      })
      kImage.on('dblclick ', (e) => {
        console.log('============,dblclick ', e, this.stage)
        console.log('===双击保存,dblclick ', e.target.attrs.x, e.target.attrs.y)
      })
      this.layer.add(kImage)
    }
    // 底部显示设备名称
    const nameText = new Konva.Text({
      x: x - device.name.length * 4,
      y: y + 35,
      text: device.name,
      fontSize: 14,
      fontFamily: 'Calibri',
      fill: '#FFF',
      visible: visible
    })
    this.layer.add(nameText)
    if (img) {
      const pointImg = new Image()
      pointImg.src = getImgUrl(img)
      pointImg.onload = () => {
        const kImage = new Konva.Image({
          draggable: drag,
          image: pointImg,
          x: device.x - 10,
          y: device.y - 50,
          width: pointImg.width,
          height: pointImg.height
        })
        kImage.on('click ', (e) => {
          //可添加弹窗等点击事件响应
          device.fn(device.id)
        })
        kImage.on('mouseenter', () => {
          this.stage.container().style.cursor = 'pointer'
        })
        kImage.on('mouseleave', () => {
          this.stage.container().style.cursor = 'default'
        })
        this.layer.add(kImage)
      }
    }
  }
  // 放大缩小时点不会偏移
  sacleFun() {
    this.stage.on('wheel', (e: any) => {
      const max = 4 // 放大最大的比例
      const min = 0.5 // 缩小最小的比例
      const step = 0.03 // 每次缩放的比例

      const x = e.evt.offsetX
      const y = e.evt.offsetY

      const offsetX =
        ((x - this.layer.offsetX()) * this.layer.scaleX()) / (this.layer.scaleX() - step) -
        (x - this.layer.offsetX())
      const offsetY =
        ((y - this.layer.offsetY()) * this.layer.scaleY()) / (this.layer.scaleY() - step) -
        (y - this.layer.offsetY())

      if (e.evt.wheelDelta) {
        if (e.evt.wheelDelta > 0) {
          // 放大
          if (this.layer.scaleX() < max && this.layer.scaleY() < max) {
            this.layer.scaleX(this.layer.scaleX() + step)
            this.layer.scaleY(this.layer.scaleY() + step)
            this.layer.move({ x: -offsetX, y: -offsetY }) // 跟随鼠标偏移位置
          }
        } else {
          // 缩小
          if (this.layer.scaleX() > min && this.layer.scaleY() > min) {
            this.layer.scaleX(this.layer.scaleX() - step)
            this.layer.scaleY(this.layer.scaleY() - step)
            this.layer.move({ x: offsetX, y: offsetY }) // 跟随鼠标偏移位置
          }
        }
      }
    })
  }
}

绑定设备时传入的图标,可点击弹窗详情
在这里插入图片描述

初始化

 <div class="cavans" id="entranceCavans"></div>
 <div class="absolute right-20px top-60px">
   <draggable @drag="dragEvent" />
 </div>
import { konvaManager } from './init'
const konvaCanvas = new konvaManager()
import draggable from '@/views/info/konva/draggable.vue'
/** 初始化 */
onMounted(() => {
  konvaCanvas.init('entranceCavans')
  konvaCanvas.initBgImg('info/map1.png')
	// videoClick 为设备点绑定的点击事件
  setTimeout(() => {
    konvaCanvas.initDevice({ id: '1', x: 600, y: 360, fn: videoClick }, 'info/map/access.png')
    konvaCanvas.initDevice({ id: '2', x: 250, y: 400, fn: videoClick }, 'info/map/access.png')
    konvaCanvas.initDevice({ id: '3', x: 1108, y: 443, fn: videoClick }, 'info/map/access.png')
  })
})
// 拖拽结束时点的坐标,不知道是不是绑定底图设置的宽高原因,导致拖拽后实际点的坐标放在konva上会有偏移
// 给减去一定值才会是鼠标抬起后再画布上的位置
const dragEvent = (evt) => {
  konvaCanvas.initDevice(
    { id: '3', x: evt.originalEvent.x - 310, y: evt.originalEvent.y - 180 },
    '',
    true
  )
}

绑定 拖拽节点

需要引入vuedraggable
npm i -S vuedraggable

<!-- 拖拽列表 -->
<template>
  <div class="border border-[#1F62B7] border-solid">
    <draggable
      v-model="myArray"
      @start="onStart"
      @end="onEnd"
      :sort="false"
      item-key="id"
      draggable=".item"
      handle=".mover"
    >
      <template #item="{ element }">
        <div class="item flex items-center p-2" :class="{ 'cursor-not-allowed': element.x }">
          <img
            src="@/assets/imgs/info/map/point.png"
            class="w-15px"
            :class="{ mover: !element.x }"
          />
          <span class="ml-10px text-14px text-[#fff]">{{ element.name }}</span>
        </div>
      </template>
    </draggable>
  </div>
</template>
<script setup lang="ts">
import draggable from 'vuedraggable'
// 如果有坐标,列表不允许拖拽
const myArray = ref([
  { name: '设备1', id: '1', x: 0 },
  { name: '设备2', id: '2', x: 100 },
  { name: '设备3', id: '3', x: 0 }
])
//开始拖拽事件
const onStart = () => {
  console.log('开始拖拽事件')
}
//拖拽结束事件
const onEnd = (evt) => {
  console.log('拖拽结束事件', evt)
  emit('drag', evt)
}
const emit = defineEmits<{
  (e: 'drag', obj: object)
}>()
</script>

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值