Meta2d.js 使用指北
做工业组态、设备监控这类可视化项目时,你大概率会面临一个选择题:是从零用 Canvas 手撸一套,还是找个现成的框架?如果你选了后者,Meta2d.js 值得认真看看。
Meta2d.js 是一个开源的 2D 可视化引擎,支持实时数据展示、动态交互和数据管理,基本覆盖了组态应用的核心需求。我在实际项目中用了一段时间,踩了不少坑,这篇文章就把上手过程和关键知识点梳理一遍,希望能帮你少走些弯路。
核心概念速览
在动手之前,先了解几个 Meta2d.js 的核心概念,后面的代码才看得明白:
| 概念 | 说明 | 类比 |
|---|---|---|
| Pen(图元) | 画布上的基本图形单元,可以是矩形、圆形、图片等 | PPT 里的形状 |
| Meta2d 实例 | 画布的管理者,负责渲染、事件、数据通信等 | Canvas 的上下文管理器 |
| 图元库 | 预置图形的集合,如流程图、类图、表单等 | 组件库 |
| socketFn | 接收实时数据后的回调函数,决定如何更新图元 | WebSocket 的 onmessage |
| ResultMap | 画布数据的 JSON 快照,可保存/加载/传输 | 序列化后的状态 |
从安装到跑起来
安装依赖
Meta2d.js 采用了模块化的包结构,核心库必装,其他图元库按需引入:
完整安装命令
# 安装核心库
pnpm install @meta2d/core --save
# 可选功能模块
pnpm install @meta2d/activity-diagram --save
pnpm install @meta2d/chart-diagram --save
pnpm install @meta2d/class-diagram --save
pnpm install @meta2d/flow-diagram --save
pnpm install @meta2d/fta-diagram --save
pnpm install @meta2d/form-diagram --save
pnpm install @meta2d/sequence-diagram --save
pnpm install @meta2d/le5le-charts --save
pnpm install @meta2d/svg --save
这些图元库分别对应活动图、类图、流程图、时序图等常见图形,不用的就别装,减小打包体积。
在 Vue3 中跑通第一个 Demo
[!NOTE] 本文主要介绍 Meta2d.js 在 Vue3 中的使用方法,其他框架的使用方式类似。
要让 Meta2d.js 在 Vue3 项目中正常工作,首先得在 index.html 里引入一些外部资源。这一步容易被忽略,但跳过了后面会出各种奇怪的问题:
index.html 资源引入
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Meta2d Vue3</title>
<script src="//at.alicdn.com/t/c/font_4042197_vr5c62twlzh.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="https://assets.le5lecdn.com/2d/canvas2svg.js"></script>
<script defer src="/meta2d/marked.min.js"></script>
</body>
</html>
准备工作做好了,接下来创建 Meta2d 实例。这里有个关键细节:Meta2d 在创建时会在全局挂载一个 meta2d 对象,后续可以直接通过它来操作画布。
<script setup lang="ts">
import { Meta2d } from '@meta2d/core'
import { onMounted, onUnmounted } from 'vue'
const meta2dOptions = {}
onMounted(() => {
new Meta2d('meta2d', meta2dOptions)
meta2d.on('contextmenu', (e) => console.log('右键菜单', e))
meta2d.on('click', (e) => console.log('点击', e))
})
onUnmounted(() => {
meta2d.destroy()
})
</script>
<template>
<div id="meta2d" class="w-full h-full" />
</template>
[!WARNING] 组件卸载时务必调用
meta2d.destroy()释放资源,否则会有内存泄漏的风险。Meta2d 内部维护了大量的事件监听和 Canvas 上下文,不销毁的话这些资源不会被 GC 回收。
到这里,一个最简单的 Meta2d 画布就跑起来了。接下来的重头戏是让它真正可用——支持拖拽图元、编辑属性、接收实时数据。
核心功能:拖拽与图元
组态应用的核心交互就是”拖拽”:用户从图元库里选中一个图元,拖到画布上,调整位置和大小。Meta2d.js 对这一流程提供了开箱即用的支持。
注册图元库
在创建 Meta2d 实例之后,需要把用到的图元库注册进去。这一步决定了画布上能使用哪些预置图形:
import { activityDiagram } from '@meta2d/activity-diagram'
import { classPens } from '@meta2d/class-diagram'
import { flowPens } from '@meta2d/flow-diagram'
import { formPens } from '@meta2d/form-diagram'
import { sequencePens, sequencePensbyCtx } from '@meta2d/sequence-diagram'
onMounted(() => {
new Meta2d('meta2d', meta2dOptions)
// 注册各类图元
meta2d.register(flowPens())
meta2d.register(activityDiagram())
meta2d.register(classPens())
meta2d.register(sequencePens())
meta2d.registerCanvasDraw(sequencePensbyCtx())
meta2d.registerCanvasDraw(formPens())
})
[!TIP]
register()用于注册通过路径(Path)绘制的图元,registerCanvasDraw()用于注册需要直接操作 Canvas 2D Context 的图元。两者的区别在于渲染方式不同,实际使用时按照图元库文档来就行。
搭建图元库面板
光注册了图元还不够,还需要一个侧边栏让用户能看到并拖拽它们。关键在于 dragStart 函数——它通过 HTML5 的 Drag API 把图元数据传递给 Meta2d 画布:
function dragStart(e: DragEvent | MouseEvent, elem: GraphicItem) {
if (!elem) return
e.stopPropagation()
if (e instanceof DragEvent) {
// 桌面端:通过 dataTransfer 传递数据
e.dataTransfer?.setData('Meta2d', JSON.stringify(elem.data))
} else {
// 平板端:点击添加图元
meta2d.canvas.addCaches = [elem.data]
}
}
模板中每个图元同时绑定 @dragstart 和 @click.prevent,确保桌面端拖拽和平板端点击都能正常工作。
完整图元库面板组件
<script setup lang="ts">
const graphicGroups = [
{
name: '基本形状',
show: true,
list: [
{
name: '正方形',
icon: 'l-rect',
id: 1,
data: { width: 100, height: 100, name: 'square' },
},
{
name: '矩形',
icon: 'l-rectangle',
id: 2,
data: { width: 200, height: 50, borderRadius: 0.1, name: 'rectangle' },
},
],
},
]
function dragStart(e, elem) {
if (!elem) return
e.stopPropagation()
if (e instanceof DragEvent) {
e.dataTransfer?.setData('Meta2d', JSON.stringify(elem.data))
} else {
meta2d.canvas.addCaches = [elem.data]
}
}
</script>
<template>
<div class="graphics-panel">
<div class="panel-title">图元库</div>
<div v-for="group in graphicGroups" :key="group.name" class="graphic-group">
<div class="group-header">{{ group.name }}</div>
<div class="group-content">
<div
v-for="elem in group.list"
:key="elem.id"
class="graphic-item"
:draggable="true"
@dragstart="dragStart($event, elem)"
@click.prevent="dragStart($event, elem)"
>
<svg class="icon" aria-hidden="true">
<use :xlink:href="`#${elem.icon}`" />
</svg>
<div class="graphic-name" :title="elem.name">{{ elem.name }}</div>
</div>
</div>
</div>
</div>
</template>
核心功能:属性编辑
光能把图元拖到画布上还不够,真正的组态应用需要让用户能编辑每个图元的属性——位置、大小、颜色、文字等等。这就需要监听图元的选中/取消选中事件,然后把属性显示到一个表单里。
管理图元选中状态
为了让属性面板知道当前选中的是什么,我封装了一个 useMeta2dSelection 组合式函数。它会根据选中图元的类型(普通图元、设备、测点、连线)自动判断并设置不同的编辑模式:
export enum SelectionMode {
File = 'file',
Pen = 'pen',
Equip = 'equip',
Point = 'point',
Line = 'line',
}
export function useMeta2dSelection() {
const selections = reactive<{
mode: SelectionMode
pen?: Pen
pens?: Pen[]
}>({
mode: SelectionMode.File,
pen: undefined,
pens: [],
})
const select = (pens?: Pen[]) => {
if (!pens || pens.length !== 1) {
selections.mode = SelectionMode.File
selections.pen = undefined
selections.pens = pens?.length ? pens : []
return
}
const pen = pens[0]
const penName = pen.name || ''
if (penName === 'image') selections.mode = SelectionMode.Equip
else if (penName.startsWith('point-combine')) selections.mode = SelectionMode.Point
else if (pen.type === 1) selections.mode = SelectionMode.Line
else selections.mode = SelectionMode.Pen
selections.pen = pen
selections.pens = pens
}
return { selections, select }
}
属性面板与双向绑定
有了选中状态管理,就可以构建属性面板了。核心逻辑是:选中图元时读取其属性填入表单,修改表单时通过 meta2d.setValue() 回写到图元上。
// 获取当前选中图元的属性
function getPenProperties() {
const pen = selections.pen
if (!pen) return
form.text = pen.text || ''
form.color = pen.color || ''
form.background = pen.background || ''
const rect = meta2d.getPenRect(pen)
if (rect) {
form.x = Math.round(rect.x) || 0
form.y = Math.round(rect.y) || 0
form.width = Math.round(rect.width) || 0
form.height = Math.round(rect.height) || 0
}
}
// 更新图元属性
function updateProperty(prop: string) {
if (!selections.pen) return
const update = { id: form.id }
update[prop] = form[prop]
meta2d.setValue(update, { render: true })
}
// 监听选中图元变化,自动刷新表单
watch(() => selections.pen?.id, getPenProperties)
[!TIP]
meta2d.setValue()的第二个参数{ render: true }会触发画布重绘。如果你在批量更新多个属性,可以先不传这个参数,最后统一调用meta2d.render()来避免不必要的重复渲染。
完整属性面板组件
<script setup lang="ts">
import { Meta2d } from '@meta2d/core'
import { onMounted, onUnmounted, reactive, watch } from 'vue'
import { useMeta2dSelection } from './useMeta2dSelection'
const { selections, select } = useMeta2dSelection()
const form = reactive({
id: '',
x: 0, y: 0, width: 0, height: 0,
text: '', color: '', background: '',
fontSize: 12, fontFamily: 'Arial', lineWidth: 1,
})
function active(pens) { select(pens) }
function inactive() { select() }
function getPenProperties() {
const pen = selections.pen
if (!pen) return
form.id = pen.id || ''
form.text = pen.text || ''
form.color = pen.color || ''
form.background = pen.background || ''
form.fontSize = pen.fontSize || 12
form.fontFamily = pen.fontFamily || 'Arial'
form.lineWidth = pen.lineWidth || 1
const rect = meta2d.getPenRect(pen)
if (rect) {
form.x = Math.round(rect.x) || 0
form.y = Math.round(rect.y) || 0
form.width = Math.round(rect.width) || 0
form.height = Math.round(rect.height) || 0
}
}
function updateProperty(prop) {
if (!selections.pen) return
const update = { id: form.id }
update[prop] = form[prop]
meta2d.setValue(update, { render: true })
}
watch(() => selections.pen?.id, getPenProperties)
onMounted(() => {
new Meta2d('meta2d', {})
meta2d.on('active', active)
meta2d.on('inactive', inactive)
})
onUnmounted(() => { meta2d.destroy() })
</script>
<template>
<div class="editor-container">
<div class="sidebar">
<GraphicsPanel />
</div>
<div class="canvas-container">
<div id="meta2d" class="canvas" />
</div>
<div v-if="selections.pen" class="properties-panel">
<div class="panel-title">属性设置</div>
<div class="property-group">
<div class="group-title">位置和大小</div>
<div class="property-row">
<div class="property-label">X 坐标</div>
<input v-model.number="form.x" type="number" @change="updateProperty('x')">
</div>
<div class="property-row">
<div class="property-label">Y 坐标</div>
<input v-model.number="form.y" type="number" @change="updateProperty('y')">
</div>
<div class="property-row">
<div class="property-label">宽度</div>
<input v-model.number="form.width" type="number" @change="updateProperty('width')">
</div>
<div class="property-row">
<div class="property-label">高度</div>
<input v-model.number="form.height" type="number" @change="updateProperty('height')">
</div>
</div>
<div class="property-group">
<div class="group-title">样式</div>
<div class="property-row">
<div class="property-label">边框颜色</div>
<input v-model="form.color" type="color" @change="updateProperty('color')">
</div>
<div class="property-row">
<div class="property-label">背景颜色</div>
<input v-model="form.background" type="color" @change="updateProperty('background')">
</div>
</div>
<div class="property-group">
<div class="group-title">文本</div>
<div class="property-row">
<div class="property-label">内容</div>
<input v-model="form.text" type="text" @change="updateProperty('text')">
</div>
<div class="property-row">
<div class="property-label">字体大小</div>
<input v-model.number="form.fontSize" type="number" @change="updateProperty('fontSize')">
</div>
</div>
</div>
</div>
</template>
进阶:多画布与实时数据
当基础功能搭建完成后,你很可能会遇到两个进阶需求:同一页面管理多个画布,以及接入实时数据让画布”活”起来。
多画布的那个坑
[!WARNING] Meta2d.js 每次
new Meta2d()都会把实例挂到全局meta2d对象上,后创建的会覆盖先创建的。如果你在主画布的弹窗里又开了一个次画布,全局meta2d就指向次画布了,主画布的操作全都会出问题。
解决方法是在创建次画布后,手动把全局引用恢复回来:
import type { Options } from '@meta2d/core'
import { Meta2d } from '@meta2d/core'
export function getExtraMeta2d(id: string, options?: Options) {
const mainMeta2d = (globalThis as any).meta2d
const newMeta2d = new Meta2d(id, options)
;(globalThis as any).meta2d = mainMeta2d
return newMeta2d
}
用法很简单:主画布正常创建,次画布通过 getExtraMeta2d() 创建。函数内部会在创建完次画布后立即把全局 meta2d 恢复成主画布的引用,两个画布互不干扰。
三种实时数据通信方式
组态应用的灵魂在于”实时”——设备状态变了,画布上的图元要跟着变。Meta2d.js 支持三种数据通信方式,适用于不同的场景:
| 通信方式 | 适用场景 | 实时性 | 复杂度 |
|---|---|---|---|
| MQTT | IoT 设备监控,设备量大、推送频繁 | 高 | 中 |
| WebSocket | 通用 Web 应用,双向通信 | 高 | 低 |
| HTTP 轮询 | 数据更新频率低,不想维护长连接 | 低 | 最低 |
三种方式都通过 meta2d.socketFn 回调来处理接收到的数据,核心模式是一样的:解析消息 -> 调用 meta2d.setValue() 更新图元。
[!IMPORTANT]
socketFn的返回值决定了后续行为:返回false只执行你的自定义回调;返回true则还会额外执行 Meta2d 核心库的默认处理逻辑。大多数场景下返回false就够了。
关于 socketFn 返回值的具体行为,可参考官方文档说明1。
MQTT 通信适合物联网场景,设备数量多、数据推送频繁。Meta2d 内置了 MQTT 支持:
// 连接 MQTT
meta2d.connectMqtt({
mqtt: 'ws://broker.example.com:8083/mqtt',
mqttTopics: 'topic1/#,topic2',
mqttOptions: {
clientId: `meta2d_${Date.now()}`,
username: 'username',
password: 'password',
},
})
// 自定义数据处理
meta2d.socketFn = (data: string) => {
const message = JSON.parse(data)
if (message.penId && message.value !== undefined) {
meta2d.setValue({ id: message.penId, value: message.value })
}
return false
}
[!NOTE] 浏览器环境只能用
ws(s)://协议连接 MQTT Broker,不能用mqtt://。如果你从后端文档复制了mqtt://开头的地址,记得改成ws://。
自定义 MQTT 客户端(使用 mqtt.js 库)
import mqtt from 'mqtt'
import { ref } from 'vue'
const MQTT_URL = 'ws://broker.example.com:8083/mqtt'
const MQTT_TOPICS = ['device/+/data', 'system/status']
const MQTT_OPTIONS = {
username: 'username',
password: 'password',
clientId: `meta2d_client_${Date.now()}`,
}
export function useCustomMqtt() {
const client = ref(null)
const connectionStatus = ref('disconnected')
function connect(messageHandler) {
client.value = mqtt.connect(MQTT_URL, MQTT_OPTIONS)
client.value.on('connect', () => {
connectionStatus.value = 'connected'
client.value.subscribe(MQTT_TOPICS, (err) => {
if (err) console.error('订阅失败:', err)
})
})
client.value.on('error', (err) => {
connectionStatus.value = 'error'
})
client.value.on('message', (topic, payload) => {
try {
const message = JSON.parse(payload.toString())
messageHandler(topic, message)
} catch (e) {
console.error('处理消息失败:', e)
}
})
}
function disconnect() {
if (client.value?.connected) {
client.value.end()
connectionStatus.value = 'disconnected'
}
}
function publish(topic, message, options = {}) {
if (!client.value?.connected) return false
const payload = typeof message === 'object' ? JSON.stringify(message) : message
client.value.publish(topic, payload, options)
return true
}
return { client, connectionStatus, connect, disconnect, publish }
}
WebSocket 通信是最常见的方案,适合大多数 Web 应用场景:
// 连接 WebSocket
meta2d.connectWebsocket('ws://your-server.com/ws')
// 自定义数据处理(和 MQTT 一样通过 socketFn)
meta2d.socketFn = (data: string) => {
const message = JSON.parse(data)
if (message.penId && message.value !== undefined) {
meta2d.setValue({ id: message.penId, value: message.value })
}
return false
}
HTTP 轮询虽然实时性不如前两者,但胜在简单可靠,不需要额外的服务端支持。特别适合数据更新频率不高的场景:
// 单个接口轮询
meta2d.store.data.http = 'https://api.example.com/device/status'
meta2d.store.data.httpTimeInterval = 3000 // 3 秒轮询
meta2d.store.data.httpHeaders = { Authorization: 'Bearer xxx' }
meta2d.connectHttp()
多接口轮询配置(1.0.26+ 支持)
// 配置多个 HTTP 轮询
meta2d.store.data.https = [
{
http: 'https://api.example.com/device/status',
method: 'GET',
httpTimeInterval: 3000,
httpHeaders: {},
},
{
http: 'https://api.example.com/alarm/list',
method: 'GET',
httpTimeInterval: 5000,
httpHeaders: {},
},
]
meta2d.connectHttp()
工程化实践
当项目复杂度上来之后,把 Meta2d 的各个功能模块拆分清楚就变得很重要了。下面分享几个在实际项目中总结出的实践。
组件化封装
与其让 Meta2d 的初始化代码散落在各个页面,不如封装成一个通用的 Vue 组件。通过 props 接收配置和初始数据,通过 emit 暴露事件,父组件用起来会清爽很多:
Meta2d 通用画布组件
<script setup lang="ts">
import type { Pen } from '@meta2d/core'
import { Meta2d } from '@meta2d/core'
import { onMounted, onUnmounted, ref, watch } from 'vue'
const props = defineProps({
options: { type: Object, default: () => ({}) },
initialData: { type: Object, default: null },
canvasId: { type: String, default: 'meta2d' },
})
const emit = defineEmits(['active', 'inactive', 'click', 'contextmenu'])
const meta2dInstance = ref(null)
function initMeta2d() {
meta2dInstance.value = new Meta2d(props.canvasId, props.options)
meta2dInstance.value.on('active', (pens: Pen[]) => emit('active', pens))
meta2dInstance.value.on('inactive', () => emit('inactive'))
meta2dInstance.value.on('click', (e: MouseEvent) => emit('click', e))
meta2dInstance.value.on('contextmenu', (e: MouseEvent) => emit('contextmenu', e))
if (props.initialData) {
meta2dInstance.value.open(props.initialData)
}
}
watch(() => props.initialData, (newData) => {
if (meta2dInstance.value && newData) {
meta2dInstance.value.open(newData)
}
}, { deep: true })
defineExpose({
getInstance: () => meta2dInstance.value,
})
onMounted(() => initMeta2d())
onUnmounted(() => meta2dInstance.value?.destroy())
</script>
<template>
<div :id="canvasId" class="meta2d-canvas" />
</template>
<style scoped>
.meta2d-canvas {
width: 100%;
height: 100%;
}
</style>
数据持久化
画布上精心搭建的组态画面,当然需要保存下来。核心 API 就两个:meta2d.data() 导出 JSON,meta2d.open(json) 加载 JSON。
// 导出画布数据
const data = meta2d.data()
// 加载画布数据
meta2d.open(data)
// 导出为图片
meta2d.toPng('my-canvas') // PNG
meta2d.toSVG('my-canvas') // SVG
完整数据持久化工具函数
export function useMeta2dData() {
function saveData(filename = 'meta2d-data.json') {
const data = meta2d.data()
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}
function loadFromFile(file: File): Promise<boolean> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = (e) => {
try {
const data = JSON.parse(e.target.result as string)
meta2d.open(data)
resolve(true)
} catch (err) {
reject(err)
}
}
reader.onerror = reject
reader.readAsText(file)
})
}
function exportImage(filename = 'meta2d-canvas', type: 'png' | 'svg' = 'png') {
type === 'svg' ? meta2d.toSVG(filename) : meta2d.toPng(filename)
}
return { saveData, loadFromFile, exportImage }
}
自定义图元
内置图元满足不了业务需求时,就需要自己造了。Meta2d.js 支持通过自定义渲染函数来绘制任意图形。核心是定义图元的 data 结构和可选的 onRender 回调:
const customDevicePen = {
name: 'customDevice',
icon: 'l-custom-device',
data: {
text: '自定义设备',
width: 120,
height: 80,
name: 'customDevice',
deviceId: '',
status: 'normal',
color: '#1890ff',
background: '#e6f7ff',
},
}
// 注册到 Meta2d
meta2d.register([customDevicePen])
带自定义渲染的完整示例
export function createCustomPens() {
const customDevicePen = {
name: 'customDevice',
icon: 'l-custom-device',
data: {
text: '自定义设备',
width: 120,
height: 80,
name: 'customDevice',
deviceId: '',
status: 'normal',
lineWidth: 2,
color: '#1890ff',
background: '#e6f7ff',
statusStyles: {
normal: { background: '#e6f7ff', color: '#1890ff' },
warning: { background: '#fff7e6', color: '#fa8c16' },
error: { background: '#fff1f0', color: '#f5222d' },
},
},
}
const customMetricPen = {
name: 'customMetric',
icon: 'l-custom-metric',
data: {
text: '0',
width: 80,
height: 40,
name: 'customMetric',
metricId: '',
unit: '',
fontSize: 16,
fontWeight: 'bold',
onRender: (ctx, pen) => {
ctx.fillStyle = pen.background || '#f0f0f0'
ctx.strokeStyle = pen.color || '#d9d9d9'
const { x, y, width: w, height: h } = pen.calculative.worldRect
const r = 4
ctx.beginPath()
ctx.moveTo(x + r, y)
ctx.lineTo(x + w - r, y)
ctx.arcTo(x + w, y, x + w, y + r, r)
ctx.lineTo(x + w, y + h - r)
ctx.arcTo(x + w, y + h, x + w - r, y + h, r)
ctx.lineTo(x + r, y + h)
ctx.arcTo(x, y + h, x, y + h - r, r)
ctx.lineTo(x, y + r)
ctx.arcTo(x, y, x + r, y, r)
ctx.closePath()
ctx.fill()
ctx.stroke()
ctx.fillStyle = pen.fontColor || '#000'
ctx.textAlign = 'center'
ctx.textBaseline = 'middle'
ctx.font = `${pen.fontWeight || ''} ${pen.fontSize || 16}px ${pen.fontFamily || 'Arial'}`
const text = pen.unit ? `${pen.text || '0'} ${pen.unit}` : (pen.text || '0')
ctx.fillText(text, x + w / 2, y + h / 2)
return true
},
},
}
return [customDevicePen, customMetricPen]
}
Demo 项目与总结
在学习和实践 Meta2d.js 的过程中,我搭了一个 Vue3 的演示项目,把上面提到的功能都集成了进去,有兴趣的话可以看看:
回顾整个使用过程,Meta2d.js 给我的感觉是”够用且灵活”。它不像一些重量级框架那样大而全,但在 2D 组态这个垂直领域,该有的功能基本都有——图元拖拽、属性编辑、多种数据通信方式、自定义图元、多画布管理。上手成本不算高,但要用好确实需要花些时间去理解它的设计思路,特别是全局 meta2d 对象的管理和 socketFn 回调的工作机制。
如果你正在做组态监控、流程图编辑器或者其他 2D 可视化项目,Meta2d.js 是一个值得尝试的选择。
Footnotes
-
socketFn的回调机制:当 Meta2d 通过 MQTT、WebSocket 或 HTTP 接收到数据时,会先调用用户通过meta2d.socketFn注册的自定义回调函数,并将原始消息字符串作为参数传入。如果回调返回true,Meta2d 还会继续执行内置的默认数据处理逻辑(即尝试将数据解析为{id, ...props}格式并自动调用setValue更新对应图元);返回false则跳过默认处理,完全由用户自行控制数据流向。 ↩