d3: Multi-Foci Force key code component understanding

徘徊边缘 提交于 2019-12-04 15:38:29

The animation is driven by alpha. It's a geometric series which is always the same: set to 0.1 in .start() and multiplied by 0.99 at each tick, the animation stops when it is less than 0.005

alpha: 0.0990
alpha: 0.0980
alpha: 0.0970
alpha: 0.0961
alpha: 0.0951
alpha: 0.0941

...etc.

force.tick = function() {
    if ((alpha *= .99) < .005) {
        event.end({
            type: "end",
            alpha: alpha = 0
        });
        return true;
    }
    //other code...
};

It represents the "heat" in the layout because it is used to determine the velocity of the nodes. This in analogous to temperature in a gas, which is proportional to the average kinetic energy of it's molecules. The "cooling" is pre-programmed to be be always -1% of the current "temperature".

The initial positions of the elements is also set in the .start() function as Math.random() * size for x and y, where size is width and height respectively. This is done before the first forEach in the tick function.

function tick(e) {
    //var k = .1 * e.alpha;
  var k = .1 * e.alpha;
  log.text('alpha: ' + d3.format(".4f")(e.alpha * 1000))
    // Push nodes toward their designated focus.
    nodes.forEach(function (o, i) {
        o.y += (foci[o.id].y - o.y) * k;
        o.x += (foci[o.id].x - o.x) * k;
    });

In the above, forEach statement, if the element y position is greater than the focus y position, then it will be given a smaller y, similar for the x positions. That means they will move towards their foci at a speed proportional to their distance from it. The proportionality constant k is 0.1*alpha which is decreasing geometrically from k = 0.1*0.1 to k = 0.1*0.005, as the animation proceeds. The final positions are a function of their initial positions and k and the other forces of gravity, charge and friction.

The nodes are g elements which have no positioning other than a reference (positioning context) for their child elements. This is the origin (top left corner) of the containing svg element and it's position is a result of the page flow and CSS positioning. The positioning context of the g elements can be altered by their transform property and this is inherited by all of their child elements. Without the g elements, the circles and text elements would both have to be positioned separately so the work is halved this way. Without the transforms, all of the circles and text would be positioned, centered on the top, left corner of the svg element.

The new positions calculated each tick are absolute values, not changes in value.

The change in node position is (foci[o.id].y - o.y) * k and this will move them towards their foci. This is "added" to the existing value (although it could be negative) and stored on the node datum (o.x and o.y), this statement

node.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; });

uses the new datum (d) to update the translation, which is still relative to the svg origin. It is a transform, not a move, so it doesn't translate relative to the current position, it changes the translation relative to the svg element origin (which is the positioning context for the g). So if we start at [10,10] and the new calculation is [10,10] then the position will remain at [10,10] relative to the svg positioning context.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!