D3.js力导向图渲染数据

我的d3.js力导向图如何解决数据在首次渲染时,所有节点剧烈抖动,为什么抖动,(特别剧烈!!!

 

force.on("tick", function () {
 
    if(force.alpha()<=0.05){  // 足够稳定时,才渲染一次
 
      link.attr("x1", function (d) { return d.source.x; })
          .attr("y1", function (d) { return d.source.y; })
          .attr("x2", function (d) { return d.target.x; })
          .attr("y2", function (d) { return d.target.y; });
      node.attr("cx", function (d) { return d.x; })
          .attr("cy", function (d) { return d.y; });
 
      force.stop(); // 渲染完成后立即停止刷新
    }
});

阅读了一下官方文档 ,发现问题出来 tick 上。

force.start() 后,有一个计时器不停地触发 tick 直到所有节点最终受力平衡稳定下来。

可以理解为,有个计时器不停在打点,每打一次点需要触发一次 tick() 里的动作。而 tick() 的默认动作是重绘所有节点和连线位置。因为图形渲染需要时间长,渲染的次数又多,所以需要等很长时间,且会抖动,你将源码进行如上的修改,应该就能解决这个问题。

force.on("tick", function () {
            edges_path.attr("d", function (d) {
                var tan = Math.abs((d.target.y - d.source.y) / (d.target.x - d.source
                    .x)); //圆心连线tan值
                var x1 = d.target.x - d.source.x > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius / (
                    tan * tan + 1)) + d.source.x :
                    d.source.x - Math.sqrt(d.sourceRadius * d.sourceRadius / (tan * tan +
                        1)); //起点x坐标
                var y1 = d.target.y - d.source.y > 0 ? Math.sqrt(d.sourceRadius * d.sourceRadius *
                    tan * tan / (tan * tan + 1)) + d.source.y :
                    d.source.y - Math.sqrt(d.sourceRadius * d.sourceRadius * tan * tan / (tan *
                        tan + 1)); //起点y坐标
                var x2 = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt(d.targetRadius * d
                    .targetRadius / (1 + tan * tan)) :
                    d.target.x + Math.sqrt(d.targetRadius * d.targetRadius / (1 + tan *
                        tan)); //终点x坐标
                var y2 = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d
                    .targetRadius * tan * tan / (1 + tan * tan)) :
                    d.target.y + Math.sqrt(d.targetRadius * d.targetRadius * tan * tan / (1 + tan *
                        tan)); //终点y坐标
                if (d.target.x - d.source.x == 0 || tan == 0) { //斜率无穷大的情况或为0时
                    y1 = d.target.y - d.source.y > 0 ? d.source.y + d.sourceRadius : d.source.y - d
                        .sourceRadius;
                    y2 = d.target.y - d.source.y > 0 ? d.target.y - d.targetRadius : d.target.y + d
                        .targetRadius;
                }
                // 防报错
                if (!x1 || !y1 || !x2 || !y2) {
                    return
                }
                if (d.linknum == 0) { //设置编号为0的连接线为直线,其他连接线会均分在两边  
                    d.x_start = x1;
                    d.y_start = y1;
                    d.x_end = x2;
                    d.y_end = y2;
                    return 'M' + x1 + ' ' + y1 + ' L ' + x2 + ' ' + y2;
                }
                var a = d.sourceRadius > d.targetRadius ? d.targetRadius * d.linknum / 3 : d
                    .sourceRadius * d.linknum / 3;
                var xm = d.target.x - d.source.x > 0 ? d.source.x + Math.sqrt((d.sourceRadius * d
                    .sourceRadius - a * a) / (1 + tan * tan)) :
                    d.source.x - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) / (1 + tan *
                        tan));
                var ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt((d.sourceRadius * d
                    .sourceRadius - a * a) * tan * tan / (1 + tan * tan)) :
                    d.source.y - Math.sqrt((d.sourceRadius * d.sourceRadius - a * a) * tan * tan / (
                        1 + tan * tan));
                var xn = d.target.x - d.source.x > 0 ? d.target.x - Math.sqrt((d.targetRadius * d
                    .targetRadius - a * a) / (1 + tan * tan)) :
                    d.target.x + Math.sqrt((d.targetRadius * d.targetRadius - a * a) / (1 + tan *
                        tan));
                var yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt((d.targetRadius * d
                    .targetRadius - a * a) * tan * tan / (1 + tan * tan)) :
                    d.target.y + Math.sqrt((d.targetRadius * d.targetRadius - a * a) * tan * tan / (
                        1 + tan * tan));
                if (d.target.x - d.source.x == 0 || tan == 0) { //斜率无穷大或为0时
                    ym = d.target.y - d.source.y > 0 ? d.source.y + Math.sqrt(d.sourceRadius * d
                        .sourceRadius - a * a) : d.source.y - Math.sqrt(d.sourceRadius * d
                            .sourceRadius - a * a);
                    yn = d.target.y - d.source.y > 0 ? d.target.y - Math.sqrt(d.targetRadius * d
                        .targetRadius - a * a) : d.target.y + Math.sqrt(d.targetRadius * d
                            .targetRadius - a * a);
                }

                var k = (x1 - x2) / (y2 - y1); //连线垂线的斜率
                var dx = Math.sqrt(a * a / (1 + k * k)); //相对垂点x轴距离
                var dy = Math.sqrt(a * a * k * k / (1 + k * k)); //相对垂点y轴距离
                if ((y2 - y1) == 0) {
                    dx = 0;
                    dy = Math.sqrt(a * a);
                }
                if (a > 0) {
                    var xs = k > 0 ? xm - dx : xm + dx;
                    var ys = ym - dy;
                    var xt = k > 0 ? xn - dx : xn + dx;
                    var yt = yn - dy;
                } else {
                    var xs = k > 0 ? xm + dx : xm - dx;
                    var ys = ym + dy;
                    var xt = k > 0 ? xn + dx : xn - dx;
                    var yt = yn + dy;
                }
                //记录连线起始和终止坐标,用于定位线上文字
                d.x_start = xs;
                d.y_start = ys;
                d.x_end = xt;
                d.y_end = yt;
                return 'M' + xs + ' ' + ys + ' L ' + xt + ' ' + yt;
            });

            //更新连接线上文字的位置
            edges_text.attr("transform", function (d) {
                // 防止报错
                if (!d.x_start || !d.y_start || !d.x_end || !d.y_end) {
                    return
                }
                return "translate(" + (d.x_start + d.x_end) / 2 + "," + ((+d.y_start) + (+d
                    .y_end)) / 2 +
                    ")" + " rotate(" + Math.atan((d.y_end - d.y_start) / (d.x_end - d.x_start)) *
                    180 / Math.PI + ")";
            });


            //更新结点图片和文字
            circle.attr("cx", function (d) {
                return d.x
            });
            circle.attr("cy", function (d) {
                return d.y
            });

            nodes_text.attr("x", function (d) {
                return d.x
            });
            nodes_text.attr("y", function (d) {
                return d.y
            });
        });

 

您好,我是有问必答小助手,您的问题已经有小伙伴解答了,您看下是否解决,可以追评进行沟通哦~

如果有您比较满意的答案 / 帮您提供解决思路的答案,可以点击【采纳】按钮,给回答的小伙伴一些鼓励哦~~

ps:问答VIP仅需29元,即可享受5次/月 有问必答服务,了解详情>>>https://vip.csdn.net/askvip?utm_source=1146287632