基于Vue3+LeaderLine实现画线测距及线条自由调整

news2024/11/19 20:18:53

先看下效果:我们画线后可以根据比例关系自动计算距离,并且线条不对可以自由调整
在这里插入图片描述

<template>
  <div id="image-detail">
    <el-image :src="myImageUrl" style="height: auto; width: 800px;" fit="scale-down" ref="canvasRef" @mousedown="startDrawing" @mousemove="drawLine" @mouseup="stopDrawing">
      <template #error>
        <div>
          <el-icon></el-icon>
        </div>
      </template>
    </el-image>
  </div>
</template>

<script>
import LeaderLine from 'leader-line'
import $ from 'jquery'

export default {
  ...
  methods: {
    startDrawing(event) {
      console.log("当前坐标点:" + event.offsetX + " " + event.offsetY)
      if (this.checkIfAiPoint(event)) return
    },
    drawLine(event) {
      const self = this
      if (self.ai.isAiMoving) {
        self.aiMoving(event)
        return false
      }
    },
    stopDrawing(event) {
      if (this.checkIfAiPoint(event)) return
    },
    // AI结果画图
    markPointPlus(offsetX, offsetY, tag, nameIndex) {
      const self = this
      if (document.getElementById("image-detail") === null) {
        return
      }
      
      // 点击多个点,生成多个值
      let div = document.createElement("div");
      div.className = "marker";

      div.id = "marker" + nameIndex + 'ai' + tag;
      div.style.width = 0
      div.style.height = 0
      // 红点颜色
      div.style.backgroundColor = "red";
      div.style.zIndex = 999;
      div.style.left = offsetX + "px";
      let imageHeight = $('#image-detail').height()
      div.style.top = (offsetY - imageHeight) + "px";
      div.style.position = "relative";
      // 边框半径百分比
      div.style.borderRadius = "50%";
      // 该位置监听的偏移量有问题,需计算
      // div.onclick = function (event) {
      //   // 计算出相对图片的偏移量
      //   console.log($('#image-detail .el-image__inner').left)
      //   console.log($('#image-detail .el-image__inner').top)
      //   const offsetX = event.clientX - $('#image-detail .el-image__inner').left
      //   const offsetY = event.clientY - $('#image-detail .el-image__inner').top
      //   if (self.ai.isAiMoving) {
      //     // 停止移动
      //     self.stopAiMoving(offsetX, offsetY, nameIndex, tag)
      //   } else {
      //     // 开始移动
      //     self.startAiMoving(offsetX, offsetY, nameIndex, tag)
      //   }
      // },
      document.getElementById("image-detail").appendChild(div);
      self.aiLineStartMiddleInfo[nameIndex + tag] = {'offsetX': offsetX, 'offsetY': offsetY}
    },
    startAiMoving(offsetX, offsetY, nameIndex, tag) {
      const self = this
      // 说明鼠标刚刚从原来位置up,不需要开始
      if (offsetX === self.lastAiX && offsetY === self.lastAiY) {
        return false
      }
      self.ai.isAiMoving = true
      self.ai.nameIndex = nameIndex
      self.ai.tag = tag
      self.lastAiX = offsetX
      self.lastAiY = offsetY
    },
    aiMoving(event) {
      const self = this
      const nameIndex = self.ai.nameIndex
      let imageHeight = $('#image-detail').height()
      if (!this.ai.isAiMoving) return;
      // const len = Object.keys(self.aiDrawInfo).length
      // 由于之前在开始结束位置新增了点的宽高均为4导致
      // 解决开始端移动点上移问题,原因未知, 由多点规律推断而来
      // if (self.ai.tag === 'start') {
      //   $('#marker' + nameIndex + 'ai' + self.ai.tag).css({
      //     'left': event.offsetX,
      //     'top': (event.offsetY + 4 * (len - nameIndex) * 2 - imageHeight)
      //   })
      // } else {
      //   $('#marker' + nameIndex + 'ai' + self.ai.tag).css({
      //     'left': event.offsetX,
      //     'top': (event.offsetY + 4 * ((len - nameIndex) * 2 - 2) - imageHeight)
      //   })
      // }
      $('#marker' + nameIndex + 'ai' + self.ai.tag).css({
        'left': event.offsetX,
        'top': (event.offsetY - imageHeight)
      })
      const lineRef = self.aiDrawInfo['marker' + nameIndex + 'ai']
      lineRef.position()
    },
    stopAiMoving(currOffsetX, currOffsetY, nameIndex, tag) {
      const self = this
      nameIndex = Number(nameIndex)
      // 说明鼠标刚刚从原来位置up,不需要结束
      if (currOffsetX === self.lastAiX && currOffsetY === self.lastAiY) {
        return false
      }
      self.ai.isAiMoving = false
      self.ai.nameIndex = 0
      self.ai.tag = ''
      let offsetX
      let offsetY
      if (tag === 'start') {
        offsetX = self.aiLineStartMiddleInfo[nameIndex + 'middle']['offsetX']
        offsetY = self.aiLineStartMiddleInfo[nameIndex + 'middle']['offsetY']
      } else {
        offsetX = self.aiLineStartMiddleInfo[nameIndex + 'start']['offsetX']
        offsetY = self.aiLineStartMiddleInfo[nameIndex + 'start']['offsetY']
      }
      const currDistance = Math.sqrt((currOffsetX - offsetX) ** 2 + (currOffsetY - offsetY) ** 2)
      let currRealDistance = (self.rulerRealDistance * 100 / self.rulerDistance * currDistance / 100).toFixed(1)
      // 判断是否为标尺移动
      if (nameIndex === 0) {
        self.rulerDistance = currDistance
        // 重新计算其他所有的画线距离
        this.recalculateAiByRuler()
      } else {
        const lineRef = self.aiDrawInfo['marker' + nameIndex + 'ai']
        lineRef.setOptions({'endLabel': currRealDistance + 'mm'})
      }++-
      // 同时更新坐标信息
      self.aiLineStartMiddleInfo[nameIndex + tag]['offsetX'] = currOffsetX
      self.aiLineStartMiddleInfo[nameIndex + tag]['offsetY'] = currOffsetY
      self.lastAiX = currOffsetX
      self.lastAiY = currOffsetY
    },
    // 重新计算其他所有的画线距离
    recalculateAiByRuler() {
      const self = this
      for (let nameIndex = 0; nameIndex < Object.keys(this.aiLineStartMiddleInfo).length / 2; nameIndex++) {
        // 标尺信息不重新计算
        if (nameIndex === 0 || nameIndex === 5) {
          continue
        }
        const middleOffsetX = this.aiLineStartMiddleInfo[nameIndex+'middle']['offsetX']
        const middleOffsetY = this.aiLineStartMiddleInfo[nameIndex+'middle']['offsetY']
        const startOffsetX = this.aiLineStartMiddleInfo[nameIndex+'start']['offsetX']
        const startOffsetY = this.aiLineStartMiddleInfo[nameIndex+'start']['offsetY']
        const currDistance = Math.sqrt(( middleOffsetX - startOffsetX) ** 2 + (middleOffsetY - startOffsetY) ** 2)
        let currRealDistance = ( this.rulerRealDistance * 100 / this.rulerDistance * currDistance / 100).toFixed(1)
        const lineRef = self.aiDrawInfo['marker' + nameIndex + 'ai']
        lineRef.setOptions({'endLabel': currRealDistance + 'mm'})
      }
    },
    drawAiLine(color, x1, y1, x2, y2, index, tag, rulerRealDistance) {
      const self = this
      const nameIndex = self.aiIndex
      // 由于图片自适应,等比例缩放
      let imageHeight = $('#image-detail').height()
      let imageWidth = $('#image-detail .el-image__inner').width()
      const widthRatio = self.other.aiWidth / imageWidth
      const heightRatio = widthRatio
      x1 /= widthRatio
      y1 /= heightRatio
      x2 /= widthRatio
      y2 /= heightRatio
      this.markPointPlus(x1, y1, 'start', nameIndex)
      this.markPointPlus(x2, y2, 'middle', nameIndex)
      const dom31 = document.getElementById('marker' + nameIndex + 'ai' + "start")
      const dom32 = document.getElementById('marker' + nameIndex + 'ai' + "middle")
      let lineRef3 = new LeaderLine(dom31, dom32, {'color': color, 'path': 'straight'});
      lineRef3.show()
      self.aiDrawInfo['marker' + nameIndex + 'ai'] = lineRef3
      const currDistance = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
      const currRealDistance = (rulerRealDistance * 100 / self.rulerDistance * currDistance / 100).toFixed(1)
      lineRef3.setOptions({'endLabel': currRealDistance + 'mm'})
      self.$emit('updateResult', {'index': index, 'tag': tag, 'value': currRealDistance})
    },
    drawByAi() {
      ...
    }
  }
}
</script>

<style lang="scss" scoped>
.line-block {
  height: 90vh;

  .one {
    display: flex;
    justify-content: space-around;
    // height: 100px;
  }

  .two {
    margin-top: 100px;
    display: flex;
    justify-content: space-around;
  }

  .iframe-block {
    margin-top: 50px;
  }

  .target {
    display: inline-block;
    background-color: #9ee7ea;
    padding: 12px;
  }

  .block-one {
    border: 1px solid blue;
    padding: 10px;
  }

  .block-two {
    border: 1px solid blue;
    padding: 10px;
  }

  .block-three {
    border: 1px solid blue;
    padding: 10px;
  }

  .block-four {
    border: 1px solid blue;
    padding: 10px;
  }
}
</style>

欢迎关注公众号 算法小生

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

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

相关文章

Django实现接口自动化平台(十)自定义action names【持续更新中】

相关文章&#xff1a; Django实现接口自动化平台&#xff08;九&#xff09;环境envs序列化器及视图【持续更新中】_做测试的喵酱的博客-CSDN博客 深入理解DRF中的Mixin类_做测试的喵酱的博客-CSDN博客 python中Mixin类的使用_做测试的喵酱的博客-CSDN博客 本章是项目的一…

高数笔记5(第一章函数 极限 连续-第三节-函数的连续性)

目录 第三节 函数的连续性&#xff08;1&#xff09;函数的连续性例1&#xff08;补充定义&#xff0c;函数连续&#xff09;例4&#xff08;无穷小*有界量&#xff09;例6 &#xff08;补充定义&#xff0c;三角函数的代换的妙用&#xff09; &#xff08;2&#xff09;连续函…

7.带你入门matlab偏斜度和峰度(matlab程序)

峰度&#xff08;Kurtosis&#xff09;与偏态&#xff08;Skewness&#xff09;就是量测数据正态分布特性的两个指标。 峰度&#xff08;Kurtosis&#xff09; 峰度衡量数据分布的平坦度&#xff08;flatness&#xff09;&#xff0c;即数据取值分布形态陡缓程度的统计量。它…

C#,数值计算——柯西分布(Cauchy distribution)的计算方法与源程序

柯西分布&#xff08;Cauchy distribution&#xff09;简介 The Cauchy distribution, also called the Lorentzian distribution or Lorentz distribution, is a continuous distribution describing resonance behavior. It also describes the distribution of horizontal …

ASEMI快恢复二极管MUR2080CTR资料,MUR2080CTR参数

编辑-Z MUR2080CTR是一种高压、超快恢复整流二极管。它通常用于各种电子应用&#xff0c;如电源、逆变器和电机控制电路。该二极管设计用于处理高压和高频开关&#xff0c;适用于需要快速高效整流的应用。 MUR2080CTR二极管的一个关键特性是其超快的恢复时间。这意味着它可以非…

从零开始 Spring Cloud 2:Eureka 注册中心

从零开始 Spring Cloud 2&#xff1a;Eureka 注册中心 图源&#xff1a;laiketui.com Eureka 常被用作 Spring Cloud 的注册中心&#xff0c;用于注册微服务的接口提供方。 在上一篇文章中&#xff0c;我们实现了两个子模块互相调用接口&#xff0c;但存在一个缺陷&#xff0…

旅游管理系统的设计与实现(论文+源码)_kaic

摘 要 旅游业走过了改革开放&#xff0c;到现在依旧蓬勃发展。但是放眼国际社会&#xff0c;我们在旅游业发展的深度和广度上所做的努力还远远不够。在中国&#xff0c;旅游业也将成为经济崛起中的重要一环。目前&#xff0c;我们生活在一个信息时代里。无论是工作&#xff0c;…

哈希表的原理

哈希概念 线性表、树结构的查找方式都是以关键字的比较为基础&#xff0c;查找效率比较低&#xff0c;顺序表的时间复杂度是O&#xff08;n&#xff09;&#xff0c;平衡树中为树的高度&#xff0c;即O&#xff08;logn&#xff09;&#xff0c;搜素的效率取决于搜索过程的元素…

归并排序的递归和非递归

基本思想 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分治法&#xff08;Divide andConquer&#xff09;的一个非常典型的应用。将已有序的子序列合并&#xff0c;得到完全有序的序列&#xff1b;即先使每个子序列有序&a…

OpenCV项目开发实战--详细介绍如何进行边缘轮廓检测 (Python/C++)-附源码

使用轮廓检测​​,我们可以检测对象的边界,并轻松在图像中定位它们。它通常是许多有趣应用的第一步,例如图像前景提取、简单图像分割、检测和识别。 因此,让我们使用 OpenCV 来了解轮廓和轮廓检测,并亲眼看看如何使用它们来构建各种应用程序。 轮廓在计算机视觉中的应用

latex2【图片、公式、矩阵】

图片 语法&#xff1a; \includegraphics{排队论模型.png} 看起来很别扭是吧&#xff0c;需要进行“修饰”&#xff1a; 当然&#xff0c;这样也很丑&#xff0c;一般写论文可以用以下的格式&#xff1a; \begin{figure}[H] \caption{问题一模型示意图} \label{paiduimx} …

【企业架构工具】2023 年 18 大企业架构工具

这些流行和新兴的 EA 工具为企业提供了支持企业架构和数字化转型所需的一切。 企业架构系统并不总是必不可少的。据推测&#xff0c;在 1940 年代&#xff0c;国际商业机器公司的一位领导人小托马斯沃森 (Thomas Watson Jr.) 曾说过&#xff1a;“我认为大约有 5 台计算机的全球…

基于SpringBoot+vue的校园疫情防控系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

Linux内核网络-拥塞控制系列(一)

谈起网络拥塞控制&#xff0c;大家可能很熟悉八股文中的"加法增大“、”乘法减小“、”慢开始“、“拥塞避免”、“快重传”、“快恢复”等概念。没错&#xff0c;这是一种经典网络拥塞控制算法的基础理论&#xff0c;但在实际的实现时不同的拥塞控制算法&#xff0c;有很…

【Android Camera开发】深入理解相机ISP(图像信号处理)必看文章

​原文&#xff1a;https://blog.51cto.com/u_16081664/6224003 作者&#xff1a;mb64411cc0e9333 凡是和图像领域工作的人&#xff0c;都会经常听到ISP&#xff08;Image Signal Process&#xff0c;图像信号处理&#xff09;&#xff0c;知道ISP对图像质量非常重要。比如华为…

电能管理系统在路店上的应用 安科瑞 许敏

摘要&#xff1a;随着企业改革的不断深入&#xff0c;对现代化用电管理的水平要求越来越高&#xff0c;准确、快速、经济的获得用电回路的各类数据进行用电分析、负荷管理、表计运行状况监测、电费自动结算的基础。同时也是提高企业经济效益的有效手段。近年来技术人员对监控系…

嵌入式程序开发者的数量剧增

随着物联网、智能设备和嵌入式系统的快速发展&#xff0c;嵌入式程序开发领域的需求不断增长&#xff0c;因此嵌入式程序开发者的数量也在剧增。这种趋势在过去几年中已经变得非常明显。 以下是导致嵌入式程序开发者数量剧增的一些主要原因&#xff1a; 我这里刚好有嵌入式、单…

Mac平台下如何制作pkg安装包以及rpath设置

打包工具介绍 Mac平台规范包可以使用Packages工具。下载地址 打包前准备工作 创建一个目录 macProject macProject目录中是以下目录结构 myProject.app└── Contents├── Info.plist├── MacOS├── res├── libmymath.dylib├── Frameworks└── Resources├…

Hive多行转多列,多列转多行

hive中的行列转换包含单行、多行、单列、多列&#xff0c;所以一共有四种组和转换结果。 一、多行转多列 原始数据表 目标结果表 分析&#xff1a;目标表中的a和b是用分组形成&#xff0c;所以groupby字段选用原始表中col1&#xff0c;c、d、e是原始表中的行值&#xff0c;…

数据结构(王道)——线性表的存储结构之循环表

一、循环单链表 定义&#xff1a; 循环单链表代码实现 创建并初始化、判断循环单链表是否为空、判断结点p是否为循环单链表的表尾结点的代码操作。 二、循环双链表 定义&#xff1a; 循环双链表代码实现 创建并初始化、判断循环双链表是否为空、判断结点p是否为循环双链表的…