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

Konva中双击无法一次性删除Label组合形状的技术求助

Konva组合形状双击删除解决方案

问题说明

基于Konva框架构建的Label组合形状(包含Tag和Text子元素),双击删除功能仅能删除单个子元素(Tag或Text),无法一次性移除整个组合,导致多组合场景下操作成本极高。

解决方案

双击事件触发时,e.target指向的是点击到的子元素(Tag/Text),而非顶层的Label容器。只需通过子元素向上追溯找到对应的Label父节点,删除整个父节点即可实现一次性删除组合。

修改步骤

  1. 在双击事件处理函数中,从当前点击的子元素向上遍历,找到类型为Label的顶层父节点
  2. 删除该Label节点,而非当前点击的子元素

修改后的核心代码(仅展示双击事件部分)

stageDraw.on('dblclick dbltap', function (e) {
    if (e.target === stageDraw) {
        return;
    }

    // 向上遍历找到顶层Label节点
    let currentShape = e.target;
    while (currentShape && currentShape.className !== 'Label') {
        currentShape = currentShape.getParent();
    }

    // 确认找到有效节点后执行删除
    if (currentShape) {
        currentShape.remove();
        currentShape.destroy();
        layerDraw.draw();
        generateJsonLayout();
    }
});

完整代码(保留原有逻辑,仅更新双击事件)

var numberLabel = new Konva.Label({
    x: width / 2,
    y: 30,
    id: 'cNumberLabel',
    draggable: true,
});

numberLabel.add(new Konva.Tag({
    fill: bgCollor,
    stroke: 'black',
    lineJoin: 'round',
    pointerDirection: direction,
    pointerWidth: 8,
    pointerHeight: 11,
    cornerRadius: 2,
    draggable: false,
}));

var textNode = new Konva.Text({
    text: '',
    fontSize: 15,
    draggable: false,
    align: 'center',
    width: 22,
    fontStyle: 'bold',
    id: 'cTextNodecNumberLabel',
});

numberLabel.add(textNode);
layerDraw.add(numberLabel);

textNode.on('dragend click', (e) => {
    textNode.hide();

    var textPosition = textNode.absolutePosition();

    var areaPosition = {
        x: stageDraw.container().offsetLeft + textPosition.x,
        y: stageDraw.container().offsetTop + textPosition.y,
    };

    var textarea = document.createElement('textarea');
    document.body.appendChild(textarea);

    textarea.value                 = textNode.text();
    textarea.style.position        = 'absolute';
    textarea.style.top             = areaPosition.y + 'px';
    textarea.style.left            = areaPosition.x + 'px';
    textarea.style.width           = textNode.width() - textNode.padding() * 2 + 'px';
    textarea.style.height          = textNode.height() - textNode.padding() * 2 + 5 + 'px';
    textarea.style.fontSize        = textNode.fontSize() + 'px';
    textarea.style.border          = 'none';
    textarea.style.padding         = '0px';
    textarea.style.margin          = '0px';
    textarea.style.overflow        = 'hidden';
    textarea.style.background      = 'none';
    textarea.style.outline         = 'none';
    textarea.style.resize          = 'none';
    textarea.style.lineHeight      = textNode.lineHeight();
    textarea.style.fontFamily      = textNode.fontFamily();
    textarea.style.transformOrigin = 'left top';
    textarea.style.textAlign       = textNode.align();
    textarea.style.color           = textNode.fill();

    rotation = textNode.rotation();
    var transform = '';

    if (rotation) {
        transform += 'rotateZ(' + rotation + 'deg)';
    }

    var px = 0;
    var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
    
    if (isFirefox) {
        px += 2 + Math.round(textNode.fontSize() / 20);
    }

    transform += 'translateY(-' + px + 'px)';
    textarea.style.transform = transform;
    textarea.style.height = 'auto';
    textarea.style.height = textarea.scrollHeight + 3 + 'px';
    textarea.focus();

    function removeTextarea() {
        textarea.parentNode.removeChild(textarea);
        window.removeEventListener('click', handleOutsideClick);
        textNode.show();
    }

    function setTextareaWidth(newWidth) {
        if (!newWidth) {
            newWidth = textNode.placeholder.length * textNode.fontSize();
        }
        
        var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
        var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
        
        if (isSafari || isFirefox) {
            newWidth = Math.ceil(newWidth);
        }

        var isEdge = document.documentMode || /Edge/.test(navigator.userAgent);
        if (isEdge) {
            newWidth += 1;
        }
        textarea.style.width = newWidth + 'px';
    }

    textarea.addEventListener('keydown', function (e) {
        if (e.keyCode === 13 && !e.shiftKey) {
            textNode.text(textarea.value);
            removeTextarea();
        }
        if (e.keyCode === 27) {
            removeTextarea();
        }
    });

    textarea.addEventListener('mouseout', function (e) {
        textNode.text(textarea.value);
        removeTextarea();
    });

    textarea.addEventListener('keydown', function (e) {
        scale = textNode.getAbsoluteScale().x;
        setTextareaWidth(textNode.width() * scale);
        textarea.style.height = 'auto';
        textarea.style.height = textarea.scrollHeight + textNode.fontSize() + 'px';
    });

    function handleOutsideClick(e) {
        if (e.target !== textarea) {
            textNode.text(textarea.value);
            removeTextarea();
        }
    }
    setTimeout(() => {
        window.addEventListener('click', handleOutsideClick);
    });
});

textNode.on('mouseover', function () {
    stageDraw.container().style.cursor = "pointer";
});

textNode.on("mouseleave", function() {
    stageDraw.container().style.cursor = "default";
});


stageDraw.on('dblclick dbltap', function (e) {
    if (e.target === stageDraw) {
        return;
    }

    // 向上遍历找到顶层Label节点
    let currentShape = e.target;
    while (currentShape && currentShape.className !== 'Label') {
        currentShape = currentShape.getParent();
    }

    if (currentShape) {
        currentShape.remove();
        currentShape.destroy();
        layerDraw.draw();
        generateJsonLayout();
    }
});

内容的提问来源于stack exchange,提问作者José Maia

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:45:48