简体   繁体   中英

D3 Transition Breaking Redraw

I'm trying to animate the update of a D3 chart where I have rectangles with wrapped text inside them. If I do this without a transition, everything functions as expected. When I add a transition though, the text does not wrap at all. It looks like it would if the wrap function was never called.

To make things weirder, if I step through the debugger, I can see that is in fact going into the wrap function. At the end of the wrap function, the displayed result actually looks as it should. However, once I step out of the function and back into the D3 library code, it reverts back to the unwrapped view.

My best guess here is that the wrap function takes a while and causing some sort of issue with the transition, but I'm not sure.

Here are the relevant snippits of the redraw function, which gets called when I want to update the page:

MyChart.prototype.redraw = function(taskList) {
    var that = this;

    var svg = d3.select("svg");
    var chart = svg.select(".chart");

    var blocks = chart.selectAll(".block")
        .data(myData, this.keyFunction);

    blocks.transition()
        .attr("transform", function(d) {
            return rectTransform(d, that.xScale, that.yScale);
        })
        .attr("height", function(d) {
            return that.yScale.bandwidth() - GRIDLINE_WIDTH;
        })
        .attr("width", function(d) {
            return Math.max(that.xScale(new Date(d.date).addDays(1)) -
                            that.xScale(new Date(d.date)) -
                            GRIDLINE_WIDTH, 0);
        });

    blocks.exit().remove();

    // update block labels
    var blockLabels = svg.selectAll('.blockLabel')
        .data(myData, this.keyFunction);

    blockLabels.transition()
        .attr('visibility', function(d) {
            if (new Date(d.date) < that.xScale.domain()[0] ||
                new Date(d.date) >= that.xScale.domain()[1])
                return 'hidden';
            return 'visible';
        })
        .attr("transform", function(d) {
            return rectLabelTransform(d, that.xScale, that.yScale);
        })
        .text(function(d) {
            return getBlockText(d);
        })
        // 50 pixels for now, later will update to actual rect width
        .call(wrap, 50);

    return this;
}

And here is the wrap function, taken from this Mike Bostock example :

function wrap(text, width) {
    text.each(function(d) {
        var text = d3.select(this);
        // quickly ignore short labels
        if (text.text().length < 10) {
            text.attr('dy', '0.35em');
            return;
        }
        var words = text.text().split(/\s+/).reverse();
        var word;
        var line = [];
        var lineNumber = 0;
        var lineHeight = 0.9; // ems
        var y = text.attr("y");
        var dy = parseFloat(text.attr("dy"));
        var tspan = text.text(null)
            .append("tspan")
            .attr("x", 0)
            .attr("y", y)
            .attr("dy", dy + "em");
        while (!!(word = words.pop())) {
            line.push(word);
            tspan.text(line.join(" "));
            if (tspan.node().getComputedTextLength() > width) {
                line.pop();
                tspan.text(line.join(" "));
                line = [word];
                tspan = text.append("tspan")
                    .attr("x", 0)
                    .attr("y", y)
                    .attr("dy", ++lineNumber * lineHeight + dy + "em")
                    .text(word);
            }
        }
        // re-center if only one line
        if (lineNumber < 1) tspan.attr('dy', '0.35em');
    });
}

I'm thinking that I could try improving the speed of the wrap function, or, worst-case, just ditch the animations. But I'm curious as to what the specific issue is..

EDIT--------

Mark's answer did help, however when I change .call(wrap, 200) to .call(wrap, function(d) {return 200;}) it breaks again. I'm assuming this is timing thing, but this behavior seems pretty strange to me that such a small change could have such a large impact. Any ideas?

Your wrap function is being called within the transition. I think you really mean this:

blockLabels.transition()
    .text(function(d) {
        return getBlockText(d);
    })
    .call(wrap, 50)
    .attr('visibility', function(d) {
        if (new Date(d.date) < that.xScale.domain()[0] ||
            new Date(d.date) >= that.xScale.domain()[1])
            return 'hidden';
        return 'visible';
    })
    .attr("transform", function(d) {
        return rectLabelTransform(d, that.xScale, that.yScale);
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM