整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

「推荐」一个前端画图的javascript库—Met

「推荐」一个前端画图的javascript库—MetricFlow.js

各位网友大家好,今天给大家推荐一个前端画图的javascript库,可用于构建数据流图、任务流图、知识图谱展示等,他就是——MetricFlow.js。

介绍

MetricFlow是一个前端画图的javascript库,可用于构建可拖拽、可灵活定义的数据流图、任务流图、知识图谱展示等

展示


文档

快速上手

1. 创建画布

创建画布只需要定义div标签即可,并声明画布的widthheight

<div id="graph" height="700px" width="1100px" ></div>

2. 创建节点

/*在画布上创建图*/
let metricFlow=MetricFlow("graph")
/*定义节点数据*/
let nodeData={
    "id":"primaryKey",//id在全局中不能重复,否则会被覆盖
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};
/*在(20,200)坐标上创建一个节点*/
let node1=metricFlow.createNode(nodeData,10,200);
/*或者直接在nodeData中定义位置x和y属性,然后*/
/*metricFlow.createNode(nodeData);*/


3. 创建连线

let metricFlow=MetricFlow("graph")
let node1Data={
    "x":10,
    "y":200,
    "id":"node1",
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};
let node1=metricFlow.createNode(node1Data);

let node2Data={
    "x":300,
    "y":200,
    "id":"node2",
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};
let node2=metricFlow.createNode(node2Data);
/*连接两个节点*/
metricFlow.createLink(node1,node2);

或者直接在末尾节点中指定from属性,无需metricFlow.createLink(node1,node2);

let metricFlow=MetricFlow("graph")
let node1Data={
    "x":10,
    "y":200,
    "id":"node1",
    "title":{'name':"运行时间统计"},
    "data":[]
};
let node1=metricFlow.createNode(node1Data);

let node2Data={
    "x":300,
    "y":200,
    "id":"node2",
    "from":"node1",//from的值可以是string 也可以是数组
    "title":{'name':"运行时间统计"},
    "data":[]
};
let node2=metricFlow.createNode(node2Data);


4. 批量创建节点

批量创建节点有两种方式,第一种使用list创建,并在每个节点数据中指定from属性即可创建关系:


            let metricFlow=MetricFlow("graph")

            /*定义节点数据*/
            let node1Data={
                "x":20,
                "y":200,
                "id":"node1",
                "title":{'name':"节点1"}
            };

            let node2Data={
                "x":300,
                "y":50,
                "id":"node2",
                "from":"node1",
                "title":{'name':"节点2"}
            };
            let node3Data={
                "x":300,
                "y":300,
                "id":"node3",
                "from":"node1",
                "title":{'name':"节点3"}
            };
            let nodes=[node1Data,node2Data,node3Data];
            metricFlow.createNodes(nodes);


第二种使用children属性创建,根节点的x和y需指定,其余子节点会自动向右排列

let nodes={
            "x":20,
            "y":200,
             ...
            "children":[
                {
                    "id":"node3",
                     ...
                    "children":[
                        {...}
                    ]
                },
            ]
        };

metricFlow.createNodes(nodes);

自动创建的时候如果节点间距不合适,可调整

let options={
                'node-distance-offsetx':5,//左右间距+5
                'node-distance-offsety':-5//上下间距-5
            };
let metricFlow=MetricFlow("graph",options)

自动创建的时候如果数据格式不匹配,又不想遍历处理,可传入函数调整

function format(data){
    data['value']=data['value']+"ms";
    return data;
}
metricFlow.createNodes(nodes,format);


样式定义

1. 更改节点样式

节点由三部分组成,分别是 标题、元素集和剩余的背景,每一个部分都支持两种样式定义方式:

第一种是沿用了css的样式,在数据上添加style即可,如,更改整个节点的边框颜色和粗细:

let node2Data={
    "id":"node2",
    "style":"border-color:red;border-width:2px;",
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};

第二种是提取了background-colorborder-color两个样式,可以单独配置

let node2Data={
    "id":"node2",
    "background-color":"red",
    "border-color":"red",
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};

样式定义demo-demo4.html

2. 更改标题样式

let node2Data={
    "id":"node2",
    "title":{'name':"运行时间统计","style":"background-color:red;border-color:red;font-size:15px;"},
    "data":[
       {'name':'方法:IndexController#method1'},
       {'name':'平均耗时:0.333ms'},
     ]
};


3. 更改元素样式

let node2Data={
    "id":"node2",
    "title":{'name':"运行时间统计"},
    "data":[
       {'name':'方法:IndexController#method1',"style":"font-size:10px;"},
       {'name':'平均耗时:0.333ms',"style":"font-size:10px;"},
     ]
};


连线配置

连线位置目前仅支持偏移量定义,可在创建MetricFlow时定义

连线分为起始点和终点,可设置全局的起始点的偏移位置,主要考虑前端框架使用较为复杂时css冲突所导致位置偏移的校正

let options={
                'link-start-offsetx':1,//起始点向右偏移一个单位
                'link-start-offsety':-1,//起始点向上偏移一个单位
                'link-end-offsetx':-1,
                'link-end-offsety':-1,
                'link-width-offset':-1,//线段粗细减小1个单位
                'link-color':"blue", //更改线段颜色
            };
let metricFlow=MetricFlow("graph",options)

连线位置demo-demo5.html

节点事件

支持基本的鼠标事件,如clickdblclickmousedownmouseup等,与原生js一致,事件在对应节点身上配置即可:

let node1Data={
            "id":"primaryKey",
            "title":{'name':"双击"},
            'click':"sinclick", //单击 sinclick为函数名
            'dblclick':"doubleClick",//双击
            "data":[
               {'name':'方法:IndexController#method1'},
               {'name':'平均耗时:0.333ms'},
             ]
        };


版本说明

V1.0:创建节点;连线;样式定义;鼠标事件

版权说明

1.本项目版权属作者所有,并使用 Apache-2.0进行开源;

2.您可以使用本项目进行学习、商用或者开源,但任何使用了本项目的代码的软件和项目请尊重作者的原创权利;

3.如果您使用并修改了本项目的源代码,请注明修改内容以及出处;

4.其他内容请参考Apache-2.0

Python数据分析与可视化领域,Matplotlib作为一款功能强大的图形库,不仅能够创建静态图表,更支持创建生动的动画效果,使数据的演变过程得以直观展现。本文将深入探讨Matplotlib的动画功能,通过实例代码解析,帮助您掌握如何利用Matplotlib绘制出令人眼前一亮的动态图表,为您的数据分析工作注入活力。


Matplotlib动画基础

Matplotlib动画基于matplotlib.animation模块,通过迭代更新图表元素(如线条、文本、图像等),实现数据随时间变化的动态可视化。主要涉及以下核心类和方法:

  • FuncAnimation:创建基于回调函数的动画,是最常用的动画创建方式。
  • ArtistAnimation:基于一组预计算的图表元素(Artists)创建动画。
  • save:将动画保存为视频文件(如.mp4.gif等)或HTML5视频。

使用FuncAnimation创建动态图表

1. FuncAnimation基础用法

以下是一个使用FuncAnimation绘制动态正弦波的例子:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

fig, ax=plt.subplots()

x_data, y_data=[], []
line,=ax.plot([], [], lw=2)

def update(frame):
    t=frame / 10.0
    x=np.linspace(0, 2 * np.pi, 1000)
    y=np.sin(x + t)
    
    x_data.append(x)
    y_data.append(y)
    
    line.set_data(x_data[-1], y_data[-1])
    return line,

ani=FuncAnimation(fig, update, frames=np.arange(100), interval=20, blit=True)

plt.show()

在这个例子中:

  • update函数定义了每一帧动画时,图表元素(线段line)如何更新数据。这里我们计算当前帧对应的正弦波曲线,并设置给线段。
  • FuncAnimation接收fig(图表)、update(更新函数)、frames(帧数)等参数,创建并返回一个动画对象。
  • interval参数指定每帧之间的时间间隔(单位:毫秒),blit=True可以提高动画性能。

2. FuncAnimation进阶应用

  • 添加标题与坐标轴标签:在update函数中动态调整。
def update(frame):
    ...
    ax.set_title(f"Time: {frame / 10.0:.1f} s")
    ax.set_xlabel("X-axis label")
    ax.set_ylabel("Y-axis label")
    ...
  • 保存动画为视频
ani.save('sine_wave_animation.mp4', writer='ffmpeg')

绘制复杂动态图表实例

1. 动态散点图:模拟粒子运动

import random

fig, ax=plt.subplots()
scat=ax.scatter([], [])

def update(frame):
    N=?
    data=[(random.uniform(-1, 1), random.uniform(-1, 1)) for _ in range(N)]
    scat.set_offsets(data)
    return scat,

ani=FuncAnimation(fig, update, frames=np.arange(100), interval=20, blit=True)

plt.show()

2. 动态条形图:实时数据更新

import time

data=[random.randint(0, 100) for _ in range(5)]

fig, ax=plt.subplots()
bars=ax.bar(range(5), data)

def update(frame):
    for bar, value in zip(bars, (random.randint(0, 100) for _ in range(5))):
        bar.set_height(value)
    return bars,

ani=FuncAnimation(fig, update, frames=np.arange(100), interval=20, blit=True)

plt.show()

Matplotlib动画在Python Web项目中的应用

实时监控系统

# 假设已连接到实时数据源
data_stream=get_realtime_data()

fig, ax=plt.subplots()
line,=ax.plot([], [], lw=2)

def update(frame):
    new_data=next(data_stream)
    line.set_data(new_data['time'], new_data['value'])
    return line,

ani=FuncAnimation(fig, update, frames=np.arange(100), interval=20, blit=True)

start_web_server(ani)

交互式Web应用

# 假设已搭建Flask环境
from flask import Flask, render_template, jsonify

app=Flask(__name__)

@app.route('/data')
def stream_data():
    return jsonify(next(get_realtime_data()))

@app.route('/')
def index():
    return render_template('index.html')

if __name__=='__main__':
    app.run(debug=True)

templates/index.html中使用JavaScript库(如D3.js)对接API,动态更新图表。

总结与展望

Matplotlib的动画功能极大地丰富了数据可视化的表现形式,使复杂的数据演变过程得以生动呈现。通过掌握FuncAnimation的使用方法,您可以轻松创建各类动态图表,如动态散点图、条形图等,以适应不同的数据分析需求。在Python Web项目中,结合实时数据源与Web框架(如Flask),您可以构建实时监控系统、交互式Web应用等,将动态图表融入到线上服务中,提升用户体验与数据洞察力。

ode环境安装

这里以一个Vue3工程为例子

首先安装Nodehttps://nodejs.org/zh-cn/download/请根据电脑操作系统安装对应的安装包

步骤 1 : 双击下载后的安装包,如下所示:

步骤 2 : 点击上图的Run(运行),将出现如下界面:

步骤 3 : 勾选接受协议选项,点击 next(下一步) 按钮 :

步骤 4 : Node.js默认安装目录为 C:\Program Files\nodejs\, 你可以修改目录,并点击 next(下一步):

步骤 5 : 点击树形图标来选择你需要的安装模式 , 然后点击下一步 next(下一步)

步骤 6 :点击 Install(安装) 开始安装Node.js。你也可以点击 Back(返回)来修改先前的配置。 然后并点击 next(下一步):

安装过程:

点击 Finish(完成)按钮退出安装向导。

2.Node安装完成后需要配置环境变量

检测PATH环境变量是否配置了Node.js,点击开始=》运行=》输入"cmd"=> 输入命令"path", 输出如下结果:

我们可以看到环境变量中已经包含了 C:\Program Files\nodejs\

如果没有,我们就需要把我们前面安装Node 步骤四中安装目录设置为环境变量:

找到我的电脑=》鼠标右键选择=》 选择属性=》 找到高级系统设置=》 环境变量=》 变量path=》 点击编辑=》将node安装目录填上并确定

最后检查一下Node.js版本看是否安装成功

编辑器 vscode

也可以是代码其他编辑器,这里以vscode为例

1.安装vscodehttps://code.visualstudio.com/

2.安装vetur 插件点击install安装

3.vscode 命令行权限不足的问题: 例如执行vue、yarn等命令报错 在安装了@vue/cli提示vue不是内部命令这种情况,应该以管理员身份运行powerell命令行

4.如果在powerell也无法运行对应的命令就是没有设置对应的环境变量,比如yarn安装了,并且用yarn下载了@vue/cli 那么 vue不是内部命令就是yarn或者vue没有配置对应的环境变量,这里yarn环境配置 默认位置 :C:\Users\Mx\AppData\Local\Yarn\bin

配置完成后再重新安装@vue/cli就行了

执行命令 设置权限

get-ExecutionPolicy
set-ExecutionPolicy RemoteSigned

选择并输入Y或者A,然后再重新启动vscode打开命令行就好。


项目应用构建

基于Vue-cli构建Vue单页面应用程序

1.打开命令行安装vue-cli脚手架工具(已安装跳过此步骤)

npm install -g @vue/cli
# OR(或)
yarn global add @vue/cli
# 通过查看版本检查是否安装成功
vue --version

2.命令行运行命名创建新项目

# 创建名为mxdraw-test-vue3 的一个新Vue项目
vue create mxdraw-test-vue3
# 如果选择default则会直接创建项目,创建项目包括babel\eslin这些工具,比如Router/Vuex等其他依赖需要自己手动安装
? P1ease pick a preset:
  #  默认选项 
  Default (vue 3)([vue 3] babel, eslint) 
  # 手动选择功能
  Manually select features

如果选择Manually select features(手动安装)则会进入下一步选项:(这里推荐大家进行手动配置)

? Check the features needed for your project :
(*) Choose vue version                    # 选择vue版本
(*) Babel                                 # 代码编译
(*) TypeScript                            # ts
( ) Progressive Web App (PWA)Support      # 支持渐进式网页应用程序
(*) Router                                # vue路由
( ) vuex                                  # 状态管理模式
( ) css Pre-processors                    # css预处理
(*) Linter ; Formatter                    # 代码风格、格式校验
( ) Unit Testing                          # 单元测试
( ) E2E Testing                           # 端对端测试

TypeScript 选项配置

# 选择使用哪个版本的vue框架
? Choose a version of Vue.js that you want to start the project with
2.x # vue2.x
3.x # vue3.x
# 询问的是是否使用class风格的组件语法,如果在项目中想要保持使用TypeScript的class风格的话,建议大家选择y。
? Use class-style component syntax? (Y/n)
# 使用Babel与TypeScript一起用于自动检测的填充?这里一定要选择y
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? (Y/n)

Router 选项配置

# 路由是否使用history模式?如果项目中存在要求就使用history(即:y),但是一般还是推荐大家使用ha模式,毕竟history模式需要依赖运维。
? Use history mode for router? (Requires proper server setup for index fallback in production) (Y/n)

CSS Pre-processors css 选项配置

# 选择一种CSS预处理类型,根据各个项目的要求使用对应css编译处理
? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): (Use arrow keys)
> Sass/SCSS (with dart-sass)
  Sass/SCSS (with node-sass)
  Less
  Stylus

Linter / Formatter 选项配置

# TSLint只有在选择TypeScript时才会存在。
? Pick a linter / formatter config: (Use arrow keys)
> ESLint with error prevention only     #  只进行报错提醒
  ESLint + Airbnb config                #  不严谨模式
  ESLint + Standard config              #  正常模式
  ESLint + Prettier                     #  严格模式
  TSLint (deprecated)                   #  TypeScript格式验证工具
# 选择校验时机,一般都会选择保存时校验,好及时做出调整
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert selection)
  (*) Lint on save               # 保存时检测
  ( ) Lint and fix on commit     # 修复和提交时检测

Unit Testing 选项配置

# 选择单元测试解决方案,普遍用到最多的是Mocha + chai
? Pick a unit testing solution: (Use arrow keys)
> Mocha + Chai
  Jest

E2E Testing E2E(End To End)选项配置

# 选择端对端测试的类型
? Pick a E2E testing solution: (Use arrow keys)
> Cypress (Chrome only)
  Nightwatch (WebDriver-based)

额外选项

# 选择Babel,PostCSS, ESLint等自定义配置的存放位置。这里建议大家选择第一个
? Where do you prefer placing config for Babel, ESLint, etc.? (Use arrow keys)
> In dedicated config files     #  存放在专用配置文件中
  In package.json               #  存放在package.json中
# 是否保存当前选择的配置项,如果当前配置是经常用到的配置,建议选择y存储一下当前配置项
? Save this as a preset for future projects? (y/N)
# 选择n之后则会直接开始创建项目了,选择y之后则会输入一个存储当前配置项的名称:
? Save preset as:


3.运行项目

cd mxdraw-test-vue3
yarn serve

详细说明请参考 [vue-cli文档指南]

vue框架使用请参考[Vue.js 中文文档]

安装 npm

yarn add mxdraw 或 npm install mxdraw

用法

1.引入

import Mx from "mxdraw"

2.加载

<canvas id="mxcad">
  </canvas>

3.javascript

import Mx from "mxdraw"
// 动态加载 js库核心代码
Mx.loadCoreCode().then(()=> {
  // Mx.MxFun.setMxServer("ws://localhost:5090") // 开启socket通信 可编辑图纸
  // 创建控件对象
  Mx.MxFun.createMxObject({
      canvasId: "mxcad", // canvas元素的id
      cadFile: "/demo/buf/$hhhh.dwg.mxb1.wgh", // http方式(预览): 加载public/demo文件夹下转换后的图纸
         // cadFile: "test2.dwg", //  socket通信方式请直接提供图纸名称 如:text.dwg
      callback: (mxDraw, {
          canvas,
          canvasParent
      })=> {
          // 可以拿到canvas元素和它的父级元素
          console.log(canvas, canvasParent)
          console.log(mxDraw)
          // 拿到图层数据
          mxDraw.addEvent('uiSetLayerData', (listLayer)=> {
              console.log(listLayer)
          })
      },
      isNewFile: true // 是否新建文件
  })
})

如何实现一个画线的功能命令?

1.实现画线功能

参考资料:

* [MrxDbgUiPrPoint | getPoint] 构建取点对象

* [status] MrxDbgUiPrBaseReturn表示对应状态

* [McEdGetPointWorldDrawObject | pWorldDraw] 用于构建一个动态绘制回调对象

* [pt1 | pt2 | lastPt] THREE.Vector3 数据类型


javascript

import Mx from "mxdraw"
// 画线的函数
function BR_Line() {
  // 构建取点对象
  const getPoint=new Mx.MrxDbgUiPrPoint();
  // 构建动态绘制对象
  const worldDrawComment=new Mx.McEdGetPointWorldDrawObject();
  // 开始动态拖动 行为: 鼠标点击画布时只执行一次回调函数,后续点击无效
  getPoint.go((status)=> {
    if (status !==0) {
      return;
    }
    // 获取鼠标在画布上的第一个点
    const pt1=getPoint.value();
    // 将第一个点作为起始点
    let lastPt=pt1.clone();
    // 设置动态绘制的回调函数
    worldDrawComment.setDraw((currentPoint, pWorldDraw)=> {
      // 绘制当前鼠标移动点到起始点的线段
      pWorldDraw.drawLine(currentPoint, lastPt);
    });
    // 设置取点对象交互过程中的动态绘制调用对象
    getPoint.setUserDraw(worldDrawComment)
    
    // 开启动态拖动,连续取点,直到ESC退出。 行为: 鼠标点击一下执行一次回调函数
    getPoint.goWhile((status)=> {
      if (status===0) {
        // 获取第二个点的位置
        const pt2=getPoint.value();
        // 拿到Three的场景对象
        let sence=Mx.MxFun.getCurrentDraw().getScene();
        // 创建一条 从起始点到 当前点击位置的线段
        let line=Mx.MxThreeJS.createLine(lastPt, pt2, 0xffffff);
        
        // 将线段添加到场景中
        sence.add(line);
        // 将第二点作为起始点
        lastPt=pt2
      }
    });
  });
}

2.注册使用命名

参考资料:

* [MxFun.addCommand] 注册命名方法

* [MxFun.sendStringToExecute] 执行命名方法

* [MxFun.isRunningCommand]检查是否有命令在运行


javascript

import Mx from "mxdraw"
// 注册命名
Mx.MxFun.addCommand("BR_Line", ()=> {
  if(Mx.MxFun.isRunningCommand()) {
      return
  }
  BR_Line()
})
// 执行命令
Mx.MxFun.sendStringToExecute("BR_Line")

更多Api使用说明请参考[Mx模块集]对应模块中的Api接口说明

按需引入配置

使用babel插件babel-plugin-import 实现按需引入

需要安装 npm i babel-plugin-import-D 然后找到或创建项目根目录的 .babelrc文件新增如下内容:

json

{
  "plugins": [
    [
      "import", {
        "libraryName": "mxdraw",
        "libraryDirectory": "dist/lib/MxModule",
        "camel2UnderlineComponentName": false,
        "camel2DaComponentName": false
      }
    ]
  ]
}

基于babel-plugin-import 按需引入

javascript

import { MxFun } from "mxdraw"

或者直接通过import MxFun from "mxdraw/dist/lib/MxModule/MxFun" 这样的方式直接引入对应模块