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

实战可行,vue3+vite+ts实现

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

安装

npm install konva 

插件引入

import Konva from '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)
    }
    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 }) // 跟随鼠标偏移位置
          }
        }
      }
    })
  }
}

绑定设备时传入的图标,可点击弹窗详情
从零开始使用Konva,画图并绑定节点。_第1张图片

初始化

 <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>

你可能感兴趣的:(javascript,前端,开发语言)