乐吾乐大屏上动态绘制多个图元并给图元绑定事件

45 阅读2分钟

# 乐吾乐大屏上动态绘制多个图元并给图元绑定事件

自动渲染界面上的图元

公司最近用乐吾乐大屏可视化组态软件这个编辑工具,因为工具支持以2D/2.5D等形式构建实时数据展示、监控告警、动态交互的组态画面,完全满足国产化需求。(上面这么多基本不用看 我就是复制官网的介绍语介绍下)其实主要是目的就是把自己写的东西记录下来

开始

1. 绘制图元,并给图元绑定事件,点击查看图元的详情数据

在初始化中写代码

// 初始化 - 保留特定文字版本
const initializeCanvas = () => {
    if (!context.meta2d) return;

    // 1. 先识别并保留左上角文字元素
    const titleTextId = 'title_text'; // 假设文字元素的ID,需要根据实际情况调整
    const elementsToKeep = [titleTextId]; // 可以添加多个需要保留的元素的ID

    // 2. 获取所有现有图元
    const allPens = context.meta2d.store.data.pens || [];
    const pensToDelete = [];

    // 3. 筛选需要删除的图元(瓶子和线条)
    allPens.forEach(pen => {
        const shouldKeep = elementsToKeep.includes(pen.id);

        if (!shouldKeep) {
            // 删除瓶子、线条和其他不需要的图元
            if (pen.id && (pen.id.startsWith('bottle_') || pen.id.startsWith('separator_') || pen.name === 'line')) {
                pensToDelete.push(pen.id);
            }
        } else {
            console.log(`保留元素: ${pen.id}`, pen);
        }
    });

    // 4. 批量删除不需要的图元
    pensToDelete.forEach(penId => {
        const pen = context.meta2d.findOne(penId);
        if (pen) {
            context.meta2d.delete(pen);
        }
    });

    // 5. 清理线条平滑设置(只针对需要删除的图元)
    allPens.forEach(pen => {
        if (pen.lineSmooth && !elementsToKeep.includes(pen.id)) {
            context.meta2d.setValue({ id: pen.id, lineSmooth: undefined });
        }
    });
    // 6. 延迟执行瓶子生成,确保清理完成
    setTimeout(() => {
        generateBottlesAndLines();
    }, 100);
};

// 生成瓶子和线条的完整逻辑
const generateBottlesAndLines = () => {
    const bottleImage = "/file/2025/0818/1/1/瓶子22_83ab1a9c.png";
    const rows = 12;
    const cols = 30;
    const horizontalSpacing = 5;
    const verticalSpacing = 20;
    const startX = 83;
    const startY = 130;
    const bottleSizeW = 52;
    const bottleSizeH = 52;

    const defaultBottleData = {
        packCode: "0",
        show: 0,  // 初始为隐藏状态
        depositTime: "测试数据",
        dueTime: "测试数据",
        isOverDue: "测试"
    };

    // 生成瓶子
    for (let row = 1; row <= rows; row++) {
        for (let col = 1; col <= cols; col++) {
            const bottleId = `bottle_${row}_${col}`;
            const pos = `${row}_${col}`;

            // 检查瓶子是否已存在
            const existingBottle = context.meta2d.findOne(bottleId);
            if (existingBottle) {
                continue; // 如果已存在,跳过创建
            }

            const penData = {
                id: bottleId,
                data: { ...defaultBottleData, pos }
            };
            const encodedPen = encodeURIComponent(JSON.stringify(penData));

            // 创建新瓶子
            context.meta2d.addPen({
                image: bottleImage,
                x: startX + (col - 1) * (bottleSizeW + horizontalSpacing),
                y: startY + (row - 1) * (bottleSizeH + verticalSpacing),
                width: bottleSizeW,
                height: bottleSizeH,
                id: bottleId,
                name: "image",
                visible: defaultBottleData.show == 1, // 初始隐藏
                data: { ...defaultBottleData, pos },
                events: [
                    {
                        name: "click",
                        conditions: [],
                        actions: [
                            {
                                action: 14,
                                extend: {
                                    width: 600,
                                    height: 400
                                },
                                value: "存样瓶详情",
                                params: `http://192.0.0.0:8345/view/v/?id=0199cc14-6b35-741a-a18a-67cd829ed32b&pen=${encodedPen}`,
                            }
                        ]
                    }
                ],
            }, true, true, true);

        }

        // 在每行下方添加分隔线
        if (row <= rows) {
            const separatorId = `separator_${row}`;

            // 检查分隔线是否已存在
            const existingLine = context.meta2d.findOne(separatorId);
            if (existingLine) {
                continue;
            }

            const currentRowBottom = startY + (row - 1) * (bottleSizeH + verticalSpacing) + bottleSizeH;
            const nextRowTop = startY + row * (bottleSizeH + verticalSpacing);
            const lineY = currentRowBottom + (nextRowTop - currentRowBottom) / 2;

            const lineStartX = startX - horizontalSpacing;
            const lineEndX = startX + cols * (bottleSizeW + horizontalSpacing) - horizontalSpacing;

            context.meta2d.addPen({
                name: 'line',
                x: lineStartX,
                y: lineY,
                width: lineEndX - lineStartX,
                height: 0,
                lineWidth: 1,
                strokeStyle: '#cccccc',
                dash: 5,
                id: separatorId,
            }, true, true, true);

        }
    }

    // 最后重绘画布
    setTimeout(() => {
        context.meta2d.render();
    }, 50);
};

// 调用初始化
initializeCanvas();

2. 从webscoker接口获取数据,并更新图元信息元素

创建一个ws接口

image.png

然后在解析中写:

meta2d.socketFn = (message, context) => {
    try {
        const newArr = JSON.parse(message);
        const tempMap = {};
        // 找到 ggName 为 "1"
        const targetItem = newArr.find(item => item.ggName === "1");

        if (targetItem && targetItem.detailA) {
            targetItem.detailA.forEach(detail => {
                const key = detail.pos;
                if (key) {
                    tempMap[key] = {
                        packCode: detail.packCode || "0",
                        show: detail.show || 1,
                        depositTime: detail.depositTime || "",
                        dueTime: detail.dueTime || "",
                        isOverDue: detail.isOverDue || "无"
                    };
                }
            });
        }
        // 动态更新瓶子数据(无需重新渲染)
        Object.keys(tempMap).forEach(pos => {
            const pen = meta2d.findOne(`bottle_${pos}`);

            if (pen) {
                // 1. 更新 pen.data
                pen.data = {
                    ...tempMap[pos],
                    pos: pen.data.pos,  // 保留初始的 pos
                };

                // 2. show:0-隐藏瓶子;1-显示瓶子;2-过期的瓶子,着色显示
                if (tempMap[pos].show == 1) {
                    meta2d.setVisible(pen, true);
                } else if (tempMap[pos].show == 0) {
                    meta2d.setVisible(pen, false);
                } else if (tempMap[pos].show == 2) {
                    meta2d.setVisible(pen, true);
                    meta2d.setValue({
                        id: pen.id,
                        image: '/file/2025/0825/1/1/瓶子red_146aa0ee.png'
                    })
                }

                // 3. 更新 events 里的 params(动态生成新的 URL)
                if (tempMap[pos].show != 0) {
                    const newPenData = { id: pen.id, data: pen.data, ggName: '1', detail: 'A' };
                    const newEncodedPen = encodeURIComponent(JSON.stringify(newPenData));

                    const newParams = `http://192.10.10.0:8345/view/v/?id=0199cc14-6b35-741a-a18a-67cd829ed32b&pen=${newEncodedPen}`;
                    // 使用setProps方法更新事件
                    meta2d.setValue({
                        id: pen.id,
                        events: [{
                            name: "click",
                            conditions: [],
                            actions: [{
                                action: 14,
                                extend: { width: 600, height: 400 },
                                value: "存样瓶详情",
                                params: newParams
                            }]
                        }]
                    }, { render: true });

                }
            }
        });
        meta2d.render();
        return { data: Object.values(tempMap) };
    } catch (error) {
        console.error("MQTT 数据解析失败:", error);
        return { data: [] };
    }
};

结束语

这种写法可以避免逐个绑定的低效操作,操作效率提升,支持百级体量实时管理。