vue2制作长方形容器,正方形网格散点图,并且等比缩放拖动

需求:有个长方形的容器,但是需要正方形的网格线,网格线是等比缩放的并且可以无线拖动的,并且添加自适应缩放和动态切换,工具是plotly.js,已完成功能如下

1.正方形网格

2.散点分组

3.自定义悬浮框的数据

4.根据窗口大小自适应缩放

5.解决数据过大或者过小网格线较少问题

6.解决项目引入plotly.js失败问题

1.效果

录像有点看不清,项目运行的话是正方形的

2.下载插件

2.1下载plotly.js

npm install plotly.js-dist-min

github的插件地址 ,里面有引入步骤

GitHub - plotly/plotly.js: Open-source JavaScript charting library behind Plotly and Dash

plotly.js官网如下(全英文),里面有案例可以看

Plotly javascript graphing library in JavaScript

2.2下载linq.js

npm install linq

2.3 引入(解决引入报错)

通常都是第一种import的方式引入

// ES6 module
import Plotly from 'plotly.js-dist-min'

// CommonJS
var Plotly = require('plotly.js-dist-min')

注意!!!如果引入后运行代码提示BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default. 可以看看这篇文章,完美解决Vue-解决BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default._breaking change: webpack < 5 used to include polyf-CSDN博客

3.代码详解

3.1 添加容器

   <div id="gd" ref="chart" style="width: 100%; height: 90vh"></div>

 3.2 模拟散点数据

{
  "points": [
    {
      "id": 1553,
      "project_id": 11,
      "name": "1504",
      "x": -2.456,
      "y": 25440.3437,
      "z": -2.5487
    },
    {
      "id": 1554,
      "project_id": 11,
      "name": "1503",
      "point_type": 2,
      "x": -1.7427000000000001,
      "y": 25440.4148,
      "z": -2.5736
    },
    {
      "id": 1555,
      "project_id": 11,
      "name": "1502",
      "point_type": 1,
      "x": 0.5841,
      "y": 25440.6208,
      "z": -0.7072
    },
  ]
}

3.3 画散点图

需要准备容器的id,data和layout还有配置项,有俩种写法,不做数据切换的时候可以用Plotly.newPlot,做数据切换的时候使用Plotly.react ,其他功能看看这个Function reference in JavaScript

    Plotly.react(
        "gd",
        this.data,
        this.layout,
        this.options
      );

 3.4 配置data

配置项地址: 

Scatter traces in JavaScript

 通过linq根据数据源的point_type进行分组,一共分为4组散点数据图例分别为点组1234,

  1. x,y:点在图形的位置,格式为[1,2,3,4]
  2. text:点名称
  3. type: "scatter" 散点图
  4. marker:给散点根据组设置不同的样式,这个点目前没看到有自定义样式的选项,但是可供选择的样式有很多,可以根据官网进行配置

  5. name:图例的名称

  6. hovertemplate:鼠标滑到点上显示的悬浮框样式

  7. customdata:自定义数据,作用就是把z的数据添加到悬浮框中

      const groupedData = Enumerable.from(data)
        .groupBy((point) => point.point_type)
        .select((group) => {
          const xValues = group.select((point) => point.x).toArray();
          const yValues = group.select((point) => point.y).toArray();
          const zValues = group.select((point) => point.z).toArray();
          const textValues = group.select((point) => point.name).toArray();

          return {
            x: xValues,
            y: yValues,
            text: textValues,
            type: "scatter",
            mode: "markers",
            marker: {
              size: 12,
              sizemode: "diameter",
              symbol: (() => {
                switch (group.key()) {
                  case 1:
                    return "232";
                  case 2:
                    return "222";
                  case 3:
                    return "diamond";
                  default:
                    return "triangle-up";
                }
              })(),
            },
            name: (() => {
              switch (group.key()) {
                case 1:
                  return "点组1";
                case 2:
                  return "点组2";
                case 3:
                  return "点组3";
                default:
                  return "点组4";
              }
            })(),
           hovertemplate: `点名:%{text} <br> X: %{x}<br>Y: %{y}<br>Z:%{customdata}<br><extra></extra>`,
            customdata: zValues, //自定义数据
          };
        })
        .toArray();

3.5 配置layout(重点)

  正方形网格代码,必须设置

         scaleanchor: "y", // 将X轴的缩放锚定到Y轴

         scaleratio: 1, // 设置X轴和Y轴的比例为1:1

并且后续需要设置xaxis.dtick和yaxis.dtick的刻度间隔设置为一致,不然不是正方形

        xaxis: {
          dtick: null, // 设置X轴刻度间隔(不需要设置范围,只需要对齐yaxis的间隔设置一致就可以使图形是正方形并且等比缩放了)
          scaleanchor: "y", // 将X轴的缩放锚定到Y轴
          scaleratio: 1, // 设置X轴和Y轴的比例为1:1
          autorange: true,
          // range: [1, 5], // 初始X轴范围
        },

代码我写了备注,有些细节注意!!如果要做数据切换必须设置   uirevision: "true",  autorange: true

dragmode:“pan”,,配置项还有:"zoom" | "pan" | "select" | "lasso" | "drawclosedpath" | "drawopenpath" | "drawline" | "drawrect" | "drawcircle" | "orbit" | "turntable" | false

showlegend:显示图例

legend:调整图例的位置,不然默认是在右侧

      layout: {
        margin: { t: 0 }, //canvas对顶部的距离
        xaxis: {
          dtick: null, // 设置X轴刻度间隔(不需要设置范围,只需要对齐yaxis的间隔设置一致就可以使图形是正方形并且等比缩放了)
          scaleanchor: "y", // 将X轴的缩放锚定到Y轴
          scaleratio: 1, // 设置X轴和Y轴的比例为1:1
          autorange: true,
          // range: [1, 5], // 初始X轴范围
        },
        yaxis: {
          // dtick: 1000, // 设置Y轴刻度间隔
          dtick: null, // 设置X轴刻度间隔
          autorange: true,
          // range: [1, 100], // 初始Y轴范围
        },
        // dragmode: "zoom",
        dragmode: "pan", // 启用平移功能
        showlegend: true,
        uirevision: "true",
        legend: {
          x: 0.5, // 图例的x坐标
          y: 1, // 图例的y坐标
          xanchor: "center", // 图例水平居中
          yanchor: "bottom", // 图例底部对齐
          orientation: "h", // 图例横向排列
        },
      },

3.6 设置 

这段代码意思就是为了保证无论数据过大还是过小的情况下都能显示多个网格线,不会导致数据只会出现一条网格线,并且数据更新的时候都得重新设置

  this.layout.xaxis.autorange = true;

      this.layout.yaxis.autorange = true;

      this.layout.uirevision = maxY; uirevision必须和上一条数据不一致不然可能会导致数据更新失败

   calculateDtick(maxY, maxX, minY, minX) {
      // 计算绝对值
      const absX = Math.abs(maxX);
      const absY = Math.abs(maxY);

      // 取较大的绝对值
      const maxAbs = Math.max(absX, absY);
      const minAbs = Math.min(minY, minY);

      // 最大值减最小值除以 5(也就是默认分为5等分)
      let cz = Math.abs(maxAbs - minAbs);
      let result = cz / 5;

      // 将结果转换为整数,并且确保结果是 10 的倍数
      let roundedResult = Math.round(result / 10) * 10;
      console.log(roundedResult, "rounded result");
      // 确保 dtick 不为 0
      if (roundedResult < 1) {
        roundedResult = 1;
      }
      this.layout.xaxis.dtick = roundedResult;
      this.layout.yaxis.dtick = roundedResult;
      console.log(
        this.data,
        this.layout,
        this.options,
        this.layout.xaxis.dtick,
        "data数据"
      );
      this.layout.xaxis.autorange = true;
      this.layout.yaxis.autorange = true;
      this.layout.uirevision = maxY;
      // newPlot
      Plotly.react(
        "gd",
        this.data,
        this.layout,
        this.options
        // /* JSON object */ {
        //   data: ,
        //   layout: ,
        //   options: ,
        // }
      );
    },

4.完整代码

<template>
  <div style="width: 100%; height: 100%">
    <button @click="updateChart()">修改代码</button>
    <button @click="updateChartOne()">修改代码22</button>
    <div id="gd" ref="chart" style="width: 100%; height: 90vh"></div>
  </div>
</template>

<script>
import Plotly from "plotly.js-dist-min";
import dataJson from "@/utils/data.json";
import Enumerable from "linq";
export default {
  data() {
    return {
      data: [],
      layout: {
        margin: { t: 0 }, //canvas对顶部的距离
        xaxis: {
          dtick: null, // 设置X轴刻度间隔(不需要设置范围,只需要对齐yaxis的间隔设置一致就可以使图形是正方形并且等比缩放了)
          scaleanchor: "y", // 将X轴的缩放锚定到Y轴
          scaleratio: 1, // 设置X轴和Y轴的比例为1:1
          autorange: true,
          // range: [1, 5], // 初始X轴范围
        },
        yaxis: {
          // dtick: 1000, // 设置Y轴刻度间隔
          dtick: null, // 设置X轴刻度间隔
          autorange: true,
          // range: [1, 100], // 初始Y轴范围
        },
        // dragmode: "zoom",
        dragmode: "pan", // 启用平移功能
        showlegend: true,
        uirevision: "true",
        legend: {
          x: 0.5, // 图例的x坐标
          y: 1, // 图例的y坐标
          xanchor: "center", // 图例水平居中
          yanchor: "bottom", // 图例底部对齐
          orientation: "h", // 图例横向排列
        },
      },
      options: {
        scrollZoom: true, //启用缩放功能
        displayModeBar: false, //不显示操作栏
        responsive: true, //制作响应式图表
        // tickmode: auto,
        // nticks: 10000,
      },
    };
  },
  mounted() {
    this.getData(dataJson.points, false);
  },
  methods: {
    updateChart() {
      this.getData(
        [
          {
            id: 2711,
            project_id: 11,
            name: "aa222",
            x: 10,
            y: 22,
            z: 332,
          },
        ],
      );
    },
    // 2126.474 1205.8596 1930.9592 850.2585
    updateChartOne() {
      this.getData(
        [
          {
            id: 2711,
            project_id: 11,
            name: "aa222",
            point_type: 2,
            x: 2126.474,
            y: 1205.8596,
            z: 332,
          },
          {
            id: 2712,
            project_id: 11,
            name: "aa333",
            point_type: 1,
            x: 850.2585,
            y: 1930.9592,
            z: 1110,
          },
          {
            id: 2712,
            project_id: 11,
            name: "aa333",
            point_type: 1,
            x: 1000,
            y: 1000,
            z: 1110,
          },
        ],
      );
    },
    // 模拟数据
    getData(data) {
      // this.layout.xaxis.range = [];
      // this.layout.yaxis.range = [];
      const groupedData = Enumerable.from(data)
        .groupBy((point) => point.point_type)
        .select((group) => {
          const xValues = group.select((point) => point.x).toArray();
          const yValues = group.select((point) => point.y).toArray();
          const zValues = group.select((point) => point.z).toArray();
          const textValues = group.select((point) => point.name).toArray();
          // 假设我们有一个函数可以根据 point_type 计算 z 值
          return {
            x: xValues,
            y: yValues,
            text: textValues,
            type: "scatter",
            mode: "markers",
            marker: {
              size: 12,
              sizemode: "diameter",
              symbol: (() => {
                switch (group.key()) {
                  case 1:
                    return "232";
                  case 2:
                    return "222";
                  case 3:
                    return "diamond";
                  default:
                    return "triangle-up";
                }
              })(),
            },
            name: (() => {
              switch (group.key()) {
                case 1:
                  return "点组1";
                case 2:
                  return "点组2";
                case 3:
                  return "点组3";
                default:
                  return "点组4";
              }
            })(),
            hovertemplate: `点名:%{text} <br> X: %{x}<br>Y: %{y}<br>Z:%{customdata}<br><extra></extra>`,
            customdata: zValues, //自定义数据
          };
        })
        .toArray();
      const maxY = Enumerable.from(data).max("$.y");
      const maxX = Enumerable.from(data).max("$.x");
      const minY = Enumerable.from(data).min("$.y");
      const minX = Enumerable.from(data).min("$.x");
      var len_str = (Math.ceil(maxY - minX) + "").length;
      let numStr = Math.pow(10, len_str - 1); // 初始化公共间最大间隔,
      console.log("最大 y 值:", maxY, maxX, minY, minX, numStr);
      // this.layout.xaxis.range = [1, maxX];
      // this.layout.yaxis.range = [1, maxY];

      this.data = groupedData;
      console.log(groupedData);

      this.calculateDtick(maxY, maxX, minY, minX);
    },
    calculateDtick(maxY, maxX, minY, minX) {
      // 计算绝对值
      const absX = Math.abs(maxX);
      const absY = Math.abs(maxY);

      // 取较大的绝对值
      const maxAbs = Math.max(absX, absY);
      const minAbs = Math.min(minY, minY);

      // 最大值减最小值除以 5(也就是默认分为5等分)
      let cz = Math.abs(maxAbs - minAbs);
      let result = cz / 5;

      // 将结果转换为整数,并且确保结果是 10 的倍数
      let roundedResult = Math.round(result / 10) * 10;
      console.log(roundedResult, "rounded result");
      // 确保 dtick 不为 0
      if (roundedResult < 1) {
        roundedResult = 1;
      }
      this.layout.xaxis.dtick = roundedResult;
      this.layout.yaxis.dtick = roundedResult;
      console.log(
        this.data,
        this.layout,
        this.options,
        this.layout.xaxis.dtick,
        "data数据"
      );
      this.layout.xaxis.autorange = true;
      this.layout.yaxis.autorange = true;
      this.layout.uirevision = maxY;
      // newPlot
      Plotly.react(
        "gd",
        this.data,
        this.layout,
        this.options
        // /* JSON object */ {
        //   data: ,
        //   layout: ,
        //   options: ,
        // }
      );
    },
  },
};
</script>

<style lang="scss" scoped></style>

文章到此结束,希望对你有所帮助~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Lora理解QLoRA

Parameter-Efficient Fine-Tuning (PEFT) &#xff1a;节约开销的做法&#xff0c;fine-tune少量参数&#xff0c;而不是整个模型&#xff1b; Low-Rank Adaptation (LoRA) &#xff1a;是PEFT的一种&#xff1b;冻结原参数矩阵&#xff0c;只更新2个小参数矩阵。 原文经过对比…

Elasticsearch:Jira 连接器教程第二部分 - 6 个优化技巧

作者&#xff1a;来自 Elastic Gustavo Llermaly 将 Jira 连接到 Elasticsearch 后&#xff0c;我们现在将回顾最佳实践以升级此部署。 在本系列的第一部分中&#xff0c;我们配置了 Jira 连接器并将对象索引到 Elasticsearch 中。在第二部分中&#xff0c;我们将回顾一些最佳实…

联发科MTK6762/MT6762安卓核心板_4G智能模块应用

MT6762安卓核心板是一款工业级高性能、可运行 android9.0 操作系统的 4G智能模块。MT6762平台打造具备 AI 体验、先进双摄像头拍摄效果且具备丰富连接功能的智能手机主板。 MT6762安卓核心板 是一款髙性能低功耗的 4G 全网通安卓智能模块。此模块支持 2G/3G/4G 移动&#xff0c…

vue3+ts+uniapp 微信小程序(第一篇)—— 微信小程序定位授权,位置信息权限授权

文章目录 简介一、先看效果1.1 授权定位前&#xff0c;先弹出隐私协议弹框1.2 上述弹框点击同意&#xff0c;得到如下弹框1.3 点击三个点&#xff0c;然后点设置 1.4 在1.2步骤下&#xff0c;无论同意或者拒绝 二、manifest.json 文件配置三、微信公众平台配置3.1 登录进入微信…

网安快速入门之Windows命令

在Windows中 我们今天介绍几个命令&#xff1a; help copy dir cd type del ipconfig net netstat tasklist sc1. help 显示命令的帮助信息。或者显示Windows内置命令。 常用参数&#xff1a; <命令>&#xff1a;查看指定命令的帮助。 示例&#xff1a;help copy 显…

Red Hat8:搭建FTP服务器

目录 一、匿名FTP访问 1、新建挂载文件 2、挂载 3、关闭防火墙 4、搭建yum源 5、安装VSFTPD 6、 打开配置文件 7、设置配置文件如下几个参数 8、重启vsftpd服务 9、进入图形化界面配置网络 10、查看IP地址 11、安装ftp服务 12、遇到拒绝连接 13、测试 二、本地…

Git 安装 操作 命令 远程仓库 多人协作

Git作用 Git诞生史 很多人都知道&#xff0c;Linus在1991年创建了开源的Linux&#xff0c;从此&#xff0c;Linux系统不断发展&#xff0c;已经成为最大的服务器系统软件了。Linus虽然创建了Linux&#xff0c;但Linux的壮大是靠全世界热心的志愿者参与的&#xff0c;这么多人在…

Vue快速开发之环境搭建nodejs与运行第一个Vue项目

步骤一:下载node.js 注意: 建议下载v12.16.0版本以上的,因为版本低无法创建Vue的脚手架。文件选择:下载方式1: 从官网下载:http://nodejs.cn/download/ 下载方式2: 从我发现的一个网站下载,选择自己想要的版本 https://nodejs.org/download/release/ 比如我下载的时v2…

AUTOSAR通信篇 - PDU和收发数据

点击订阅专栏不迷路 文章目录 一、概述二、OSI模型与AUTOSAR层级关系三、I-PDU、N-PDU、L-PDU及其关系3.1. L-PDU3.2. N-PDU3.3. I-PDU 四、数据流4.1. 普通数据流4.2. 诊断数据流4.3. 动态PDU数据流4.4. 安全通信数据流4.5. XCP数据流 返回总目录 一、概述 在学习Autosar通信…

.Net8 Avalonia跨平台UI框架——<vlc:VideoView>控件播放海康监控、摄像机视频(Windows / Linux)

一、UI效果 二、新建用户控件&#xff1a;VideoViewControl.axaml 需引用&#xff1a;VideoLAN.LibVLC.Windows包 Linux平台需安装&#xff1a;VLC 和 LibVLC &#xff08;sudo apt-get update、sudo apt-get install vlc libvlccore-dev libvlc-dev&#xff09; .axaml 代码 注…

哈尔滨有双线服务器租用吗?

哈尔滨有双线服务器租用吗&#xff1f;双线服务器是一种针对哈尔滨特有的网络环境优化的服务器解决方案&#xff0c;它能够同时支持中国电信和中国联通或移动其中两家主要ISP&#xff08;互联网服务提供商&#xff09;的连接。 由于中国南方地区多采用电信网络&#xff0c;而北…

58,【8】BUUCTF [PwnThyBytes 2019]Baby_SQL1

进入靶场 和2次注入的页面很像 不过养成查看源代码的好习惯 先访问source.zip 下载后解压&#xff0c;发现两个文件 第一个文件夹打开又有4个PHP文件 那还是先看index.php文件好了 有PHP和HTML两部分&#xff0c;下面是PHP部分代码&#xff08;HTML太长了&#xff0c;先放一…

在服务器上增加新网段IP的路由配置

在服务器上增加新网段IP的路由配置 前提条件步骤一:检查当前路由表步骤二:添加新路由步骤三:验证新路由步骤四:持久化路由配置脚本示例结论在网络管理中,路由配置是一项基本且重要的任务。它决定了数据包在网络中的传输路径。本文将详细介绍如何在服务器上增加新的路由配置…

图像处理|闭运算

闭运算&#xff08;Closing&#xff09;是形态学操作中的另一种基本操作&#xff0c;它与开运算&#xff08;Opening&#xff09;类似&#xff0c;但执行的步骤相反。闭运算结合了膨胀和腐蚀操作&#xff0c;顺序为 先膨胀后腐蚀。这种操作通常用于填补图像中的小空洞&#xff…

【Pytorch实用教程】TCN(Temporal Convolutional Network,时序卷积网络)简介

文章目录 TCN的基本特点TCN的优点TCN的应用场景典型的TCN架构总结TCN(Temporal Convolutional Network,时序卷积网络)是一种用于处理序列数据的深度学习模型,尤其适用于时间序列预测、语音识别、自然语言处理等任务。它利用卷积神经网络(CNN)来处理时序数据,相比于传统的…

Python调用go语言编译的库

要在 Python 中调用用 Go 语言编写的库&#xff0c;可以使用 Go 语言的 cgo 特性将 Go 代码编译成共享库&#xff08;如 .so 文件&#xff09;&#xff0c;然后在 Python 中通过 ctypes 或 cffi 模块加载和调用这个共享库。 新建main.go文件&#xff0c;使用go语言编写如下代码…

服务器一次性部署One API + ChatGPT-Next-Web

服务器一次性部署One API ChatGPT-Next-Web One API ChatGPT-Next-Web 介绍One APIChatGPT-Next-Web docker-compose 部署One API ChatGPT-Next-WebOpen API docker-compose 配置ChatGPT-Next-Web docker-compose 配置docker-compose 启动容器 后续配置 同步发布在个人笔记服…

深度学习项目--基于LSTM的火灾预测研究(pytorch实现)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 前言 LSTM模型一直是一个很经典的模型&#xff0c;这个模型当然也很复杂&#xff0c;一般需要先学习RNN、GRU模型之后再学&#xff0c;GRU、LSTM的模型讲解将…

PCL K4PCS算法实现点云粗配准【2025最新版】

目录 一、算法原理1、算法概述2、算法流程3、参考文献二、 代码实现1、原始版本2、2024新版三、 结果展示本文由CSDN点云侠原创,原文链接,首发于:2020年4月27日。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的抄袭狗。 博客长期更新,本文最近一次更新时间为…

.Net Core微服务入门全纪录(二)——Consul-服务注册与发现(上)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…