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

news2025/1/15 5:13:45

需求:有个长方形的容器,但是需要正方形的网格线,网格线是等比缩放的并且可以无线拖动的,并且添加自适应缩放和动态切换,工具是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>

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

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

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

相关文章

0基础跟德姆(dom)一起学AI 自然语言处理13-注意力机制介绍2

1 注意力机制规则 它需要三个指定的输入Q(query), K(key), V(value), 然后通过计算公式得到注意力的结果, 这个结果代表query在key和value作用下的注意力表示. 当输入的QKV时, 称作自注意力计算规则&#xff1b;当Q、K、V不相等时称为一般注意力计算规则 例子&#xff1a;seq2…

慧集通(DataLinkX)iPaaS集成平台-系统管理之UI库管理、流程模板

UI库管理 UI库管理分为平台级和自建两种&#xff0c;其中平台级就是慧集通平台自己内置的一些ui库所有客户均可调用&#xff0c;自建则是平台支持使用者自己根据规则自己新增对应的UI库。具体界面如下&#xff1a; 自建UI库新增界面&#xff1a; 注&#xff1a;平台级UI库不支…

通过一个算法的设计来了解栈的一些应用

目录 1.前言 2.步骤 3.代码实现 4.测试 5.运行结果 6.一些思考 7.一些应用示例 1.前言 掌握堆栈的基本原理 掌握堆栈的存储结构 掌握堆栈的进栈、出栈&#xff1b; 判断栈空的实现方法 掌握应用堆栈实现括号匹配的原理和实现方法&#xff1b; 熟悉python语言编程 熟练…

USB 驱动开发 --- Gadget 驱动框架梳理(一)

本文由 Linux 内核文档翻译与总结而来&#xff0c;个人学习笔记仅供参考。 Gadget 框架 在 USB 协议交互过程中&#xff0c;角色定义&#xff1a; the device driver is the master (or “client driver”) Linux 内核中称为 HCD(Host Controller Driver)&#xff0c;负责与 …

字符串算法篇——字里乾坤,算法织梦,解构字符串的艺术(下)

文章目录 前言第一章&#xff1a;最长公共前缀1.1 题目链接&#xff1a;https://leetcode.cn/problems/longest-common-prefix/description/1.2 题目分析&#xff1a;1.3 思路讲解&#xff1a;1.4 代码实现&#xff1a; 第二章&#xff1a;最长回文子串2.1 题目链接&#xff1a…

计算机网络 笔记 数据链路层3(局域网,广域网,网桥,交换机)

局域网: LAN:在某一区域内由多台计算机互联成的计算机组&#xff0c;使用广播信道 特点&#xff1a; 覆盖范围有限&#xff1a;通常局限在几千米范围内&#xff0c;比如一栋办公楼、一个校园或一个工厂等相对较小的地理区域。 数据传输速率高&#xff1a;一般能达到 10Mbps…

istio-proxy oom问题排查步骤

1. 查看cluster数量 cluster数量太多会导致istio-proxy占用比较大的内存&#xff0c;此时需检查是否dr资源的host设置有配置为* 2. 查看链路数据采样率 若采样率设置过高&#xff0c;在压测时需要很大的内存来维护链路数据。可以调低采样率或增大istio-proxy内存。 检查iop中…

fast-crud select下拉框 实现多选功能及下拉框数据动态获取(通过接口获取)

教程 fast-crud select示例配置需求:需求比较复杂 1. 下拉框选项需要通过后端接口获取 2. 实现多选功能 由于这个前端框架使用逻辑比较复杂我也是第一次使用,所以只记录核心问题 环境:vue3,typescript,fast-crud ,elementPlus 效果 代码 // crud.tsx文件(/.ts也行 js应…

Apache JMeter 压力测试使用说明

文章目录 一、 安装步骤步骤一 下载相关的包步骤二 安装 Jmeter步骤三 设置 Jmeter 工具语言类型为中文 二、使用工具2.1 创建测试任务步骤一 创建线程组步骤二 创建 HTTP 请求 2.2 配置 HTTP 默认参数添加 HTTP消息头管理器HTTP请求默认值 2.3 添加 查看结果监听器2.4 查看结果…

计算机网络 (40)域名系统DNS

前言 计算机网络域名系统DNS&#xff08;Domain Name System&#xff09;是互联网的基础技术之一&#xff0c;它负责将人类可读的域名转换为计算机用来通信的数字IP地址。 一、基本概念 DNS的主要目的是将域名解析或翻译为IP地址&#xff0c;使得用户可以通过简单易记的域名来访…

本地服务器Docker搭建个人云音乐平台Splayer并实现远程访问告别烦人广告

前言 大家好&#xff01;今天我要给大家分享的是如何在Ubuntu上用Docker快速搭建高颜值无广告的某抑云音乐播放器Splayer的详细流程&#xff0c;并且结合cpolar内网穿透工具实现远程访问。如果你是音乐爱好者&#xff0c;经常需要在外办公或旅行&#xff0c;这个教程绝对能让你…

黑马linux入门笔记(01)初始Linux Linux基础命令 用户和权限 实用操作

B站 黑马程序员 的视频 BV1n84y1i7td 黑马程序员新版Linux零基础快速入门到精通&#xff0c;全涵盖linux系统知识、常用软件环境部署、Shell脚本、云平台实践、大数据集群项目实战等 增强自控力 冥想慢呼吸绿色锻炼充分休息减少决策次数优先做重要的事情(早晨)融入强自控群控…

小程序组件 —— 31 事件系统 - 事件绑定和事件对象

小程序中绑定事件和网页开发中绑定事件几乎一致&#xff0c;只不过在小程序不能通过 on 的方式绑定事件&#xff0c;也没有 click 等事件&#xff0c;小程序中绑定事件使用 bind 方法&#xff0c;click 事件也需要使用 tap 事件来进行代替&#xff0c;绑定事件的方式有两种&…

UE5 使用内置组件进行网格切割

UE引擎非常强大&#xff0c;直接内置了网格切割功能并封装为蓝图节点&#xff0c;这项功能在UE4中就存在&#xff0c;并且无需使用Chaos等模块。那么就来学习下如何使用内置组件实现网格切割。 1.配置测试用StaticMesh 对于被切割的模型&#xff0c;需要配置一些参数。以UE5…

ue5 1.平A,两段连击蒙太奇。鼠标点一下,就放2段动画。2,动画混合即融合,边跑边挥剑,3,动画通知,动画到某一帧,把控制权交给蓝图。就执行蓝图节点

新建文件夹 创建一个蒙太奇MA_Melee 找到c_slow 调节一下速度 把D_slow拖上去 中间加一个片段 哎呀呀&#xff0c;写错了&#xff0c;我想写2 把这个标记拖过来&#xff0c;点击默认default 弄第二个片段 就会自己变成这个样子 把2这个标记拖到中间 鼠标左键&a…

《机器学习》之K-means聚类

目录 一、简介 二、K-means聚类实现步骤 1、初始化数据点、确定K值 2、通过距离分配数据点 3、更新簇中心 4、 迭代更新 三、聚类效果评价方式 1、轮廓系数的定义 2、整体轮廓系数 3、使用场景 4、优点 5、缺点 6、代码实现方法 四、K-means聚类代码实现 1、API接…

Wireshark抓包教程(2024最新版个人笔记)

改内容是个人的学习笔记 Wireshark抓包教程&#xff08;2024最新版&#xff09;_哔哩哔哩_bilibili 该课程笔记1-16 wireshark基础 什么是抓包工具&#xff1a;用来抓取数据包的一个软件 wireshark的功能&#xff1a;用来网络故障排查&#xff1b;用来学习网络技术 wireshark下…

Web开发(一)HTML5

Web开发&#xff08;一&#xff09;HTML5 写在前面 参考黑马程序员前端Web教程做的笔记&#xff0c;主要是想后面自己搭建网页玩。 这部分是前端HTML5CSS3移动web视频教程的HTML5部分。主要涉及到HTML的基础语法。 HTML基础 标签定义 HTML定义 HTML(HyperText Markup Lan…

RabbitMQ 的工作模式

目录 工作模式 Simple&#xff08;简单模式&#xff09; Work Queue&#xff08;工作队列&#xff09; Publish/Subscribe&#xff08;发布/订阅&#xff09; Exchange&#xff08;交换机&#xff09; Routing&#xff08;路由模式&#xff09; Topics&#xff08;通配…

备战蓝桥杯:树的存储与遍历(dfs和bfs)

树的概念 树的逻辑结构是树形结构&#xff0c;和我们之前的线性结构又不太一样了&#xff0c;是一种一对多的关系 树的结点分为根节点&#xff0c;叶子结点&#xff08;没有分支的结点&#xff09; 以及分支结点 从上往下看&#xff0c;每个结点都有0个或多个后继 从下往上…