You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Forge Viewer自定义复合标记开发:矩形内嵌可编辑文本框需求

Forge Viewer 自定义联动复合Markup实现方案

针对你在Forge Viewer中创建矩形+底部文本框复合标记遇到的问题,以下是具体实现方案:

核心思路

通过自定义Markup子类,将矩形和文本框作为子元素统一管理,绑定两者的位置变换、禁用文本框单独拖拽,同时实现文本编辑功能,确保整个复合标记作为单个单元联动。

完整实现代码

class CustomCompoundMarkup extends Autodesk.Viewing.Extensions.Markups.Core.Markup {
    constructor(markupsCore, id) {
        super(markupsCore, id);
        this.rectMarkup = null;
        this.textMarkup = null;
        this.isEditingText = false;
    }

    // 初始化复合标记:创建矩形与文本框
    initialize(x, y, width, height) {
        // 创建外层矩形标记
        this.rectMarkup = this.markupsCore.createMarkup('rect', {
            x: x,
            y: y,
            width: width,
            height: height,
            fillColor: 'rgba(255, 255, 0, 0.2)',
            strokeColor: '#ff0000',
            strokeWidth: 2
        });

        // 计算文本框位置:矩形底部居中,预留内边距
        const textPadding = 5;
        const textHeight = 20;
        const textX = x + (width / 2);
        const textY = y + height - textHeight - textPadding;

        // 创建内部文本标记
        this.textMarkup = this.markupsCore.createMarkup('text', {
            x: textX,
            y: textY,
            text: '默认标注文本',
            fontSize: 14,
            fillColor: 'rgba(0, 0, 0, 0.8)',
            strokeColor: 'transparent',
            textAlign: 'center'
        });

        // 禁用文本框单独拖拽
        this.textMarkup.draggable = false;
        // 阻止文本框鼠标事件冒泡,避免干扰矩形拖拽
        this.textMarkup.onPointerDown = (e) => {
            e.stopPropagation();
            if (!this.isEditingText) {
                this.startTextEditing();
            }
        };

        // 监听矩形拖拽/缩放事件,同步文本框位置
        this.rectMarkup.onDrag = () => this.syncTextPosition();
        this.rectMarkup.onResize = () => this.syncTextPosition();

        // 将子标记加入复合标记管理
        this.addChild(this.rectMarkup);
        this.addChild(this.textMarkup);
    }

    // 同步文本框到矩形底部居中位置
    syncTextPosition() {
        const rectBounds = this.rectMarkup.getBoundingRect();
        const textBounds = this.textMarkup.getBoundingRect();
        const textPadding = 5;

        const targetX = rectBounds.x + (rectBounds.width / 2);
        const targetY = rectBounds.y + rectBounds.height - textBounds.height - textPadding;

        this.textMarkup.setPosition(targetX, targetY);
        this.markupsCore.render();
    }

    // 启动文本编辑功能
    startTextEditing() {
        this.isEditingText = true;
        const textBounds = this.textMarkup.getBoundingRect();

        // 创建临时输入框覆盖文本区域
        const input = document.createElement('input');
        input.type = 'text';
        input.value = this.textMarkup.text;
        input.style.position = 'absolute';
        input.style.left = `${textBounds.x}px`;
        input.style.top = `${textBounds.y}px`;
        input.style.width = `${textBounds.width}px`;
        input.style.height = `${textBounds.height}px`;
        input.style.fontSize = '14px';
        input.style.textAlign = 'center';
        input.style.border = 'none';
        input.style.background = 'rgba(255,255,255,0.9)';
        input.style.zIndex = 1000;

        document.body.appendChild(input);
        input.focus();

        // 编辑完成后同步文本并清理输入框
        const finishEditing = () => {
            this.textMarkup.text = input.value;
            this.markupsCore.render();
            document.body.removeChild(input);
            this.isEditingText = false;
        };

        input.addEventListener('blur', finishEditing);
        input.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') finishEditing();
        });
    }

    // 重写父类拖拽逻辑,确保复合标记整体联动
    onDrag(e) {
        super.onDrag(e);
        this.syncTextPosition();
    }

    // 重选选中逻辑,确保复合标记整体被选中
    setSelected(selected) {
        super.setSelected(selected);
        this.rectMarkup.setSelected(selected);
        this.textMarkup.setSelected(selected);
    }
}

使用方式

// 确保已加载Markups扩展并进入编辑模式
const markupsExtension = viewer.getExtension('Autodesk.Viewing.MarkupsCore');
markupsExtension.enterEditMode();

// 创建自定义复合标记(示例坐标:x=100, y=100, 宽200, 高150)
const compoundMarkup = new CustomCompoundMarkup(markupsExtension.core, 'custom-compound-001');
compoundMarkup.initialize(100, 100, 200, 150);
markupsExtension.core.addMarkup(compoundMarkup);
markupsExtension.core.render();

问题对应解决说明

  • 文本框显示在矩形内部底部:通过getBoundingRect()获取矩形和文本框的尺寸,计算底部居中坐标,确保文本框位置始终贴合矩形底部。
  • 拖拽矩形时文本同步跟随:监听矩形的onDrag和onResize事件,实时调用syncTextPosition()更新文本框位置。
  • 限制文本框单独拖拽:设置文本框draggable = false,并阻止其鼠标事件冒泡,仅允许父矩形触发拖拽。
  • 文本框可编辑:点击文本时创建临时输入框,编辑完成后同步文本内容到标记,实现编辑功能。

内容的提问来源于stack exchange,提问作者fumakiya dhara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:14:59