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

news2024/11/28 12:47:48

实战可行,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)
    }
    // 如果show为true,可添加弹窗等点击事件响应
    if (device.show) {
      this.openDialog(device.fn, device)
    }
    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)
      }
    }
  }
  openDialog(fun, device) {
    const obj = {
      ...device,
      location: {
        left: device!.x - 20 + 'px',
        top: device!.y - device.property.length * 30 + 'px'
      }
    }
    fun(obj)
  }
  // 放大缩小时点不会偏移
  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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1356829.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频怎么配上音乐?视频软件轻松配乐

视频怎么配上音乐&#xff1f;视频配乐已经成为了一种重要的表达方式。它能够为视频增添情感&#xff0c;营造氛围&#xff0c;让观众更加深入地理解视频的内容。那么&#xff0c;哪些软件可以给视频配上音乐呢&#xff1f;本文将为你介绍几款优秀软件。 一、清爽视频编辑 清爽…

福利来袭,.NET Core开发5大案例,30w字PDF文档大放送!!!

千里之行&#xff0c;始于足下&#xff0c;若想提高软件编程能力&#xff0c;最最重要的是实践&#xff0c;所谓纸上得来终觉浅&#xff0c;绝知此事要躬行。根据相关【艾宾浩斯遗忘曲线】研究表明&#xff0c;如果不动手实践&#xff0c;记住的东西会很快忘记。 为了便于大家查…

虚幻UE 增强输入-第三人称模板增强输入分析与扩展

本篇是增强输入模块&#xff0c;作为UE5.0新增加的模块。 其展现出来的功能异常地强大&#xff01; 让我们先来学习学习一下第三人称模板里面的增强输入吧&#xff01; 文章目录 前言一、增强输入四大概念二、使用步骤1、打开增强输入模块2、添加IA输入动作2、添加IMC输入映射内…

SAFe大规模敏捷企业级实训

课程简介 SAFe – Scaled Agile Framework是目前全球运用最广泛的大规模敏捷框架&#xff0c;也是成长最快、最被认可、最有价值的规模化敏捷框架&#xff0c;目前全球SAFe认证专业人士已达80万人&#xff0c;福布斯100强的70%都在实施SAFe。本课程是一个2天的 SAFe权威培训课…

线程的深入学习(二)

前言 上一篇讲了线程池的相关知识&#xff0c;这篇文章主要讲解一个 1.并发工具类如CountDownLatch、CyclicBarrier等。 2.线程安全和并发集合&#xff1a; 3.学习如何使用Java提供的线程安全的集合类&#xff0c;如ConcurrentHashMap、CopyOnWriteArrayList等。 并发工具类 …

java练习题之String方法运用

应用知识点&#xff1a;​​​​​​String类 1&#xff1a;(String 类)仔细阅读以下代码段&#xff1a; String s "hello"; String t"hello"; char[] c {h,e,l,l,o}; 2&#xff1a;下列选项输出结果为false 的语句是() System.out.println( s.euqals( t…

线段树基础(下)

线段树二分 对序列进行二分的操作&#xff0c;可能使用线段树二分进行优化。 一些序列上最左/最右位置问题可以二分解决&#xff0c;同时需要使用线段树进行查询。时间复杂度通常是 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)&#xff0c;可以尝试使用线段树二分的技巧将其优…

机器学习(四) -- 模型评估(2)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 机器学习&#xff08;四&#xff09; -- 模型评估…

B端产品经理学习-对用户进行需求挖掘

目录&#xff1a; 用户需求挖掘的方法 举例&#xff1a;汽车销售系统的用户访谈-前期准备 用户调研提纲 预约用户做访谈 用户访谈注意点 我们对于干系人做完调研之后需要对用户进行调研&#xff1b;在C端产品常见的用户调研方式外&#xff0c;对B端产品仍然适用的 用户需…

6.1810: Operating System Engineering 2023 <Lab6: Multithreading>

一、本节任务 二、要点 2.1 锁&#xff08;Locking&#xff09; 在多 CPU 或者单 CPU 多线程并发的场景中&#xff0c;对临界资源&#xff08;或者说共享资源&#xff09;的访问如果不加以限制&#xff0c;可能会引发一些严重的问题&#xff0c;比如当两个线程同时对一个共享…

Python初探:从零开始的编程奇妙之旅

一、Python是什么 Python是一门多用途的高级编程语言&#xff0c;以其简洁、易读的语法而脱颖而出。在深度学习领域&#xff0c;Python扮演着至关重要的角色。其丰富的科学计算库&#xff08;如NumPy、Pandas、Matplotlib&#xff09;和强大的深度学习框架&#xff08;如Tenso…

jmeter参数化的三种方式

1.用户定义变量 使用变量&#xff1a; ${变量名} 这个变量是全局变量&#xff0c;也就是在下面子节点中都可以使用&#xff1b; 使用场景&#xff1a;两个账号分别有不同的权限&#xff0c;A经办&#xff0c;B审核。等。。。 2.CSV数据文件设置 3.函数

案例071:基于微信小程序的汽车预约维修系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

jupyter更改默认路径到其它的目录或者到其它的盘 比如D盘

1.打开终端 输入jupyter notebook --generate-config 如下 2.在C:\Users\mb5958\.jupyter路径下 3.用记事本打开它&#xff0c;搜索directory 4.在你想要的路径下新建一个文件夹&#xff0c;如‘D:\jupyterFile’&#xff0c;然后将路径名放在c.NotebookApp.notebook_dir"…

卷麻了,00后测试用例写的比我还好,简直无地自容...........

经常看到无论是刚入职场的新人&#xff0c;还是工作了一段时间的老人&#xff0c;都会对编写测试用例感到困扰&#xff1f;例如&#xff1a; 如何编写测试用例&#xff1f; 作为一个测试新人&#xff0c;刚开始接触测试&#xff0c;对于怎么写测试用例很是头疼&#xff0c;无法…

【PCB专题】Allegro封装更新焊盘

在PCB封装的绘制中&#xff0c;有时会出现需要更新焊盘的情况。比如在制作封装的过程中发现焊盘做的不对而使用PAD_Designer重新更新了焊盘。 那在PCB中如何更新已经修改过的焊盘呢&#xff1f; 打开封装&#xff0c;选择Tools->Padstack->Refresh... 选择Refresh all …

让 sdk 包静默升级的 SAO 操作,你见过几种?

拓展阅读 让 sdk 包静默升级的 SAO 操作&#xff0c;你见过几种&#xff1f; 业务背景 有时候为业务方提供了基础的 sdk 包&#xff0c;为了保证稳定性&#xff0c;一般都是 release 包。 但是每一次升级都非常痛苦&#xff0c;也不可能写一个一步到位的 jar 包&#xff0c…

javascript 常见工具函数(三)

21.克隆数组的几种方法&#xff1a; &#xff08;1&#xff09;slice方法&#xff1a; let arr [1,2,3,4] let arr1 arr.slice() //或者是 let arr1 arr.slice(0) arr[0] 6 console.log(arr) // [6, 2, 3, 4] console.log(arr1) // [1, 2, 3, 4] &#xff08;2&…

Android Jetpack学习系列——Navigation

写在前面 Google在2018年就推出了Jetpack组件库&#xff0c;但是直到今天我才给重视起来&#xff0c;这真的不得不说是一件让人遗憾的事。过去几年的空闲时间里&#xff0c;我一直在尝试做一套自己的组件库&#xff0c;帮助自己快速开发&#xff0c;虽然也听说过Jetpack&#…

Stable Diffusion模型概述

Stable Diffusion 1. Stable Diffusion能做什么&#xff1f;2. 扩散模型2.1 正向扩散2.2 反向扩散 3. 训练如何进行3.1 反向扩散3.2 Stable Diffusion模型3.3 潜在扩散模型3.4 变分自动编码器3.5 图像分辨率3.6 图像放大 4. 为什么潜在空间是可能的&#xff1f;4.1 在潜在空间中…