D3 Перекрытие переходов - PullRequest
0 голосов
/ 10 июля 2020

Я пытался сделать гонку на гистограмме в d3 js, следуя этому руководству https://observablehq.com/@d3 / bar-chart-race -hibited . Однако я все перепробовал и до сих пор не могу понять, почему в моем случае полоса перекрывается, а нижняя дата не обновляется. Мой код в основном такой же, как и в примере.

Это мой код:

    d3.csv("users-by-social-media-platform.csv", function (data) {
        return {
            year: data.year,
            social: data.social,
            value: +data.value,
        };
    }).then(function (data) {

            let n = 10;
            let duration = 30000;
            let names = new Set(data.map(d => d.social));
            let nest = Array.from(d3.nest()
                .key(function (d) {
                    return +d.year;
                }).rollup(function (v) {
                    return v.map(function (d) {
                        return {
                            social: d.social,
                            value: +d.value
                        }
                    })
                }).entries(data));

            let datevalues = nest.sort(function (a, b) {
                return d3.ascending(a.key, b.key)
            });

            let datevalues_map = []
            for (let i = 0; i < datevalues.length; i++) {
                let map = new Map(datevalues[i].value.map(i => [i.social, i.value]));
                datevalues_map.push([new Date(datevalues[i].key), map])
            }
            console.log(data.filter(d => d.social === "Facebook"))


            function rank(value) {
                const data = Array.from(names, social => ({social, value: value(social)}));
                data.sort((a, b) => d3.descending(a.value, b.value));
                for (let i = 0; i < data.length; ++i) data[i].rank = Math.min(n, i);
                return data;
            }

            console.log(rank(name => datevalues_map[0][1].get(name)))

            let k = 12;
            const keyframes = [];
            let ka, a, kb, b;
            console.log(d3.pairs(datevalues_map))
            for ([[ka, a], [kb, b]] of d3.pairs(datevalues_map)) {
                for (let i = 0; i < k; ++i) {
                    const t = i / k;
                    keyframes.push([
                        new Date(ka * (1 - t) + kb * t),
                        rank(social => (a.get(social) || 0) * (1 - t) + (b.get(social) || 0) * t)
                    ]);
                }
            }
            keyframes.push([new Date(kb), rank(social => b.get(social) || 0)]);
            let nest_keyframe = d3.nest().key((function (d) {
                return d.social;
            })).entries(keyframes.flatMap(([, data]) => data), d => d.name)
            console.log(nest_keyframe)

            let nameframes = []
            for (let i = 0; i < nest_keyframe.length; i++) {
                nameframes.push([nest_keyframe[i].key, nest_keyframe[i].values])
            }
            let prev = new Map(nameframes.flatMap(([, data]) => d3.pairs(data, (a, b) => [b, a])))
            let next = new Map(nameframes.flatMap(([, data]) => d3.pairs(data)))

            let formatNumber = d3.format(",d")
            let formatDate = d3.utcFormat("%Y")


            function textTween(a, b) {
                const i = d3.interpolateNumber(a, b);
                return function (t) {
                    this.textContent = formatNumber(i(t));
                };
            }

            function bars(svg) {
                let bar = svg.append("g")
                    .attr("fill-opacity", 0.6)
                    .selectAll("rect");
                return ([date, data], transition) => bar = bar
                    .data(data.slice(0, n), d => d.social)
                    .join(
                        enter => enter.append("rect")
                            .attr("fill", function (d) {
                                return color(d.social)
                            })
                            .attr("height", y.bandwidth())
                            .attr("x", x(0))
                            .attr("y", d => y((prev.get(d) || d).rank))
                            .attr("width", d => x((prev.get(d) || d).value) - x(0)),
                        update => update,
                        exit => exit.transition(transition).remove()
                            .attr("y", d => y((next.get(d) || d).rank))
                            .attr("width", d => x((next.get(d) || d).value) - x(0))
                    )
                    .call(bar => bar.transition(transition)
                        .attr("y", d => y(d.rank))
                        .attr("width", d => x(d.value) - x(0)));
            }

            function labels(svg) {
                let label = svg.append("g")
                    .style("font", "bold 12px var(--sans-serif)")
                    .style("font-variant-numeric", "tabular-nums")
                    .attr("text-anchor", "end")
                    .selectAll("text");
                console.log(label)

                return ([date, data], transition) => label = label
                    .data(data.slice(0, n), d => d.social)
                    .join(
                        enter => enter.append("text")
                            .attr("transform", d => `translate(${x((prev.get(d) || d).value)},${y((prev.get(d) || d).rank)})`)
                            .attr("y", y.bandwidth() / 2)
                            .attr("x", -6)
                            .attr("dy", "-0.25em")
                            .text(d => d.social)
                            .call(text => text.append("tspan")
                                .attr("fill-opacity", 0.7)
                                .attr("font-weight", "normal")
                                .attr("x", -6)
                                .attr("dy", "1.15em")),
                        update => update,
                        exit => exit.transition(transition).remove()
                            .attr("transform", d => `translate(${x((next.get(d) || d).value)},${y((next.get(d) || d).rank)})`)
                            .call(g => g.select("tspan").tween("text", d => textTween(d.value, (next.get(d) || d).value)))
                    )
                    .call(bar => bar.transition(transition)
                        .attr("transform", d => `translate(${x(d.value)},${y(d.rank)})`)
                        .call(g => g.select("tspan").tween("text", d => textTween((prev.get(d) || d).value, d.value))))
            }


            function axis(svg) {
                const g = svg.append("g")
                    .attr("transform", `translate(0,${margin.top})`);

                const axis = d3.axisTop(x)
                    .ticks(width / 160)
                    .tickSizeOuter(0)
                    .tickSizeInner(-barSize * (n + y.padding()));

                return (_, transition) => {
                    g.transition(transition).call(axis);
                    g.select(".tick:first-of-type text").remove();
                    g.selectAll(".tick:not(:first-of-type) line").attr("stroke", "white");
                    g.select(".domain").remove();
                };
            }

            function ticker(svg) {
                const now = svg.append("text")
                    .style("font", `bold ${barSize}px var(--sans-serif)`)
                    .style("font-variant-numeric", "tabular-nums")
                    .attr("text-anchor", "end")
                    .attr("x", width - 6)
                    .attr("y", margin.top + barSize * (n - 0.45))
                    .attr("dy", "0.32em")
                    .text(formatDate(keyframes[0][0]));
                return ([date], transition) => {
                    transition.end().then(() => now.text(formatDate(date)));
                };
            }


            let color = d3.scaleOrdinal(d3.schemeTableau10);

            let barSize = 48;
            let width = 1770;
            let margin = ({top: 16, right: 6, bottom: 6, left: 0})

            x = d3.scaleLinear([0, 1], [margin.left, width - margin.right])
            y = d3.scaleBand()
                .domain(d3.range(n + 1))
                .rangeRound([margin.top, margin.top + barSize * (n + 1 + 0.1)])
                .padding(0.1)
            height = margin.top + barSize * n + margin.bottom

            const svg = d3.select("div").append("svg")
                .attr("viewBox", [0, 0, width, height]);
            svg.node();

            const updateBars = bars(svg);
            const updateAxis = axis(svg);
            const updateLabels = labels(svg);
            const updateTicker = ticker(svg);

            svg.node()

            for (const keyframe of keyframes) {
                const transition = svg.transition()
                    .duration(duration)

                // Extract the top bar’s value.
                x.domain([0, keyframe[1][0].value]);
                updateAxis(keyframe, transition);
                updateBars(keyframe, transition);
                updateLabels(keyframe, transition);
                updateTicker(keyframe, transition);

            }
        }
    )
    ;

Большое спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...