如何使用d3.js检查元素是否在视点中(在可见区域中)

时间:2018-10-12 22:38:15

标签: javascript html d3.js dom

我正在绘制大量元素,并且在许多情况下,大多数元素都在视点之外。

我想避免对隐藏元素进行昂贵的旋转变换。

这里是一个示例:

https://blockchaingraph.org/#ipfs-QmfXtMeUdjWBPQHUNKvF3nkYR57aZz7qarW5qikEUYWJvw

此图中的许多元素都是隐藏的(尝试缩小以查看)。但是目前,我必须在每个刻度上渲染每个元素,并且它变得越来越慢。

这是我的代码:

function transformLinks(svgLinks, nodeRadius, arrowSize) {
    if (svgLinks) {
        var link = svgLinks.selectAll('.link').filter(needRedraw);
        //console.log("total:", svgLinks.selectAll('.link').size(), ',needRedraw:', link.size());
        transformLinksLines(link);
        transformLinksTexts(link.selectAll('.text'));
        transformLinksOutlines(link, nodeRadius, arrowSize);
        transformLinksOverlays(link.selectAll('.overlay'));
        link.each(function (n) {
            n.source.lx = n.source.x;
            n.source.ly = n.source.y;
            n.target.lx = n.target.x;
            n.target.ly = n.target.y;
        });
    }
}

function needRedraw(link) {
    if (!link.source) {
        link = link.parentNode;
    }
    return nodeMoved(link.source) || nodeMoved(link.target);
}

var minDistToRedraw = 0.8;
function nodeMoved(n) {
    return utils.isNumber(n.x) && utils.isNumber(n.y)
            && !(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}

我想扩展needRedraw()函数以检查可见性。目前,该功能仅检查两个链接的节点是否移动足够大。

1 个答案:

答案 0 :(得分:0)

由于我没有找到任何开箱即用的解决方案,所以我不得不进入那些坐标转换方面。

首先,我创建了将外部容器坐标转换为SVG内部客户端坐标系统的函数-containerToSVG()

然后将其应用于.getBoundingClientRect();以获得SVG坐标空间中的可见区域。

然后在过滤器中检查两个节点的大小是否均超出可见区域-不要重绘链接。

在某些情况下,两个节点都超出了该区域的大小,但是链接仍然可以越过该区域。但这并不是大问题,只要用户看不到链接分离。

代码如下:

function transformLinks(svgLinks, nodeRadius, arrowSize) {
    if (svgLinks) {
        var containerRect = container.node().getBoundingClientRect();
        var p = containerToSVG(-nodeRadius, -nodeRadius);
        var r = containerToSVG(containerRect.width + nodeRadius, containerRect.height + nodeRadius);
        svgVisibleRect = {left: p.x, top: p.y, right: r.x, bottom: r.y};
        minDistToRedraw = (svgVisibleRect.right - svgVisibleRect.left) / (containerRect.width + nodeRadius * 2);
        var link = svgLinks.selectAll('.link').filter(needRedraw);
        transformLinksLines(link);
        transformLinksTexts(link.selectAll('.text'));
        transformLinksOutlines(link, nodeRadius, arrowSize);
        transformLinksOverlays(link.selectAll('.overlay'));
        link.each(function (n) {
            updateLastCoord(n.source);
            updateLastCoord(n.target);
        });
    }
}

function needRedraw(link) {
    if (!nodeMoved(link.source) && !nodeMoved(link.target)) {
        return false;
    }
    return isVisible(link.source) || isVisible(link.target);
}

function nodeMoved(n) {
    return utils.isNumber(n.x) && utils.isNumber(n.y) &&
            !(utils.isNumber(n.lx) && Math.abs(n.x - n.lx) <= minDistToRedraw && Math.abs(n.y - n.ly) <= minDistToRedraw);
}

function isVisible(n) {
    var result = n.x > svgVisibleRect.left && n.x < svgVisibleRect.right &&
            n.y > svgVisibleRect.top && n.y < svgVisibleRect.bottom;
    return result;
}

function updateLastCoord(n) {
    n.lx = n.x;
    n.ly = n.y;
}

function containerToSVG(containerX, containerY) {
    var svgPount = svgNode.createSVGPoint();
    svgPount.x = containerX;
    svgPount.y = containerY;
    return svgPount.matrixTransform(document.getElementById("links-svg").getScreenCTM().inverse());
}

function transformLinksLines(link) {
    link.attr('transform', function (d) {
        var angle = rotation(d.source, d.target);
        return 'translate(' + d.source.x + ', ' + d.source.y + ') rotate(' + angle + ')';
    });

}