SOURCE

console 命令行工具 X clear

                    
>
console
var svg = d3.select("body").append("svg")
    .attr("width", 960)
    .attr("height", 600);

var tickDuration = 500;

var top_n = 15;
var height = 600;
var width = 800;

const margin = {
    top: 10,
    right: 0,
    bottom: 5,
    left: 0
};

let barPadding = (height - (margin.bottom + margin.top)) / (top_n * 5);

let year = 2017;

var data = [
    { "name": "铂顿", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "菲住布渴", "value": "1", "year": "2018", "lastValue": "1", "rank": "1" },
    { "name": "布偶未来", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "Rest Hour", "value": "1", "year": "2017", "lastValue": "1", "rank": "1" },
    { "name": "魔住", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "乐易住", "value": "7", "year": "2018", "lastValue": "10", "rank": "1" },
    { "name": "乐易住", "value": "3", "year": "2019", "lastValue": "10", "rank": "1" },
    { "name": "潍坊未来智能", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "苏宁雅悦", "value": "1", "year": "2018", "lastValue": "1", "rank": "1" },
    { "name": "Freera", "value": "1", "year": "2020", "lastValue": "1", "rank": "1" },
    { "name": "深坑酒店", "value": "1", "year": "2018", "lastValue": "1", "rank": "1" },
    { "name": "猫山屋", "value": "4", "year": "2019", "lastValue": "4", "rank": "1" },
    { "name": "博睿 AI酒店", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "蒙尼", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" },
    { "name": "米家", "value": "1", "year": "2019", "lastValue": "1", "rank": "1" }
];
const halo = function (text, strokeWidth) {
    text.select(function () { return this.parentNode.insertBefore(this.cloneNode(true), this); })
        .style('fill', '#ffffff')
        .style('stroke', '#ffffff')
        .style('stroke-width', strokeWidth)
        .style('stroke-linejoin', 'round')
        .style('opacity', 1);

}

data.forEach(d => {
    d.value = +d.value,
        d.lastValue = +d.lastValue,
        d.value = isNaN(d.value) ? 0 : d.value,
        d.year = +d.year,
        d.colour = d3.hsl(Math.random() * 360, 0.75, 0.75)
});

// console.log(data);

let yearSlice = data.filter(
    d => d.year == year && !isNaN(d.value)
)
    .sort((a, b) => b.value - a.value)
    .slice(0, top_n);

yearSlice.forEach((d, i) => d.rank = i);

console.log('yearSlice: ', yearSlice)

let x = d3.scale.linear()
    .domain([0, d3.max(yearSlice, d => d.value)])
    .range([margin.left, width - margin.right - 65]);

let y = d3.scale.linear()
    .domain([top_n, 0])
    .range([height - margin.bottom, margin.top]);

// let xAxis = d3.svg.axis()
//     .scale(x)
//     .orient('bottom')
//     .ticks(width > 500 ? 5 : 2)
//     .tickSize(-(height - margin.top - margin.bottom))
//     .tickFormat(d => d3.format(',')(d));

// svg.append('g')
//     .attr('class', 'axis xAxis')
//     .attr('transform', `translate(0, ${margin.top})`)
//     .call(xAxis)
//     .selectAll('.tick line')
//     .classed('origin', d => d == 0);

svg.selectAll('rect.bar')
    .data(yearSlice, d => d.name)
    .enter()
    .append('rect')
    .attr('class', 'bar')
    .attr('x', x(0) + 1)
    .attr('width', d => x(d.value) - x(0) - 1)
    .attr('y', d => y(d.rank) + 5)
    .attr('height', y(1) - y(0) - barPadding)
    .style('fill', d => d.colour);

svg.selectAll('text.label')
    .data(yearSlice, d => d.name)
    .enter()
    .append('text')
    .attr('class', 'label')
    .attr('x', d => x(d.value) - 8)
    .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1)
    .style('text-anchor', 'end')
    .html(d => d.name);

svg.selectAll('text.valueLabel')
    .data(yearSlice, d => d.name)
    .enter()
    .append('text')
    .attr('class', 'valueLabel')
    .attr('x', d => x(d.value) + 5)
    .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1)
    .text(d => d3.format(',.0f')(d.value));

let yearText = svg.append('text')
    .attr('class', 'yearText')
    .attr('x', width - margin.right)
    .attr('y', height - 25)
    .style('text-anchor', 'end')
    .html(year)
    .call(halo, 100);

let datas = [];
var ticker = setInterval(function () {
    yearSlice = data.filter(d => d.year == year && !isNaN(d.value))
        .sort((a, b) => b.value - a.value)
        .slice(0, top_n);

    if (yearSlice.length > 0) {
        // datas = yearSlice; 
        if (datas.length > 0) {
            yearSlice.forEach((d, j) => {
                let flag = false;
                datas.forEach((dd, i) => {
                    if (d.name == dd.name) {
                        dd.value += d.value;
                        flag = true;
                    }
                });
                if (!flag) {
                    datas.push(d);
                }
            });
        } else {
            datas = yearSlice;
        }

        datas.sort((a, b) => b.value - a.value)
            .slice(0, top_n);
    }

    datas.forEach((d, i) => d.rank = i);

    console.log('IntervalYear: ', datas);
    x.domain([0, d3.max(datas, d => d.value)]);

    // svg.select('.xAxis')
    //     .transition()
    //     .duration(tickDuration)
    //     .ease('linear')
    //     .call(xAxis);

    let bars = svg.selectAll('.bar').data(datas, d => d.name);

    bars
        .enter()
        .append('rect')
        .attr('class', d => `bar ${d.name.replace(/\s/g, '_')}`)
        .attr('x', x(0) + 1)
        .attr('width', d => x(d.value) - x(0) - 1)
        .attr('y', d => y(top_n + 1) + 5)
        .attr('height', y(1) - y(0) - barPadding)
        .style('fill', d => d.colour)
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('y', d => y(d.rank) + 5);

    bars
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('width', d => x(d.value) - x(0) - 1)
        .attr('y', d => y(d.rank) + 5);

    bars
        .exit()
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('width', d => x(d.value) - x(0) - 1)
        .attr('y', d => y(top_n + 1) + 5)
        .remove();

    let labels = svg.selectAll('.label')
        .data(datas, d => d.name);

    labels
        .enter()
        .append('text')
        .attr('class', 'label')
        .attr('x', d => x(d.value) - 8)
        .attr('y', d => y(top_n + 1) + 5 + ((y(1) - y(0)) / 2))
        .style('text-anchor', 'end')
        .html(d => d.name)
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1);


    labels
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('x', d => x(d.value) - 8)
        .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1);

    labels
        .exit()
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('x', d => x(d.value) - 8)
        .attr('y', d => y(top_n + 1) + 5)
        .remove();



    let valueLabels = svg.selectAll('.valueLabel').data(datas, d => d.name);

    valueLabels
        .enter()
        .append('text')
        .attr('class', 'valueLabel')
        .attr('x', d => x(d.value) + 5)
        .attr('y', d => y(top_n + 1) + 5)
        .text(d => d3.format(',.0f')(d.value))
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1);

    valueLabels
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('x', d => x(d.value) + 5)
        .attr('y', d => y(d.rank) + 5 + ((y(1) - y(0)) / 2) + 1)
        .text(d => d3.format(',.0f')(d.value))
    /*.tween("text", function (d) {
        let i = d3.interpolateRound(d.lastValue, d.value);
        return function (t) {
            this.textContent = d3.format(',')(i(t));
        };
    });*/


    valueLabels
        .exit()
        .transition()
        .duration(tickDuration)
        .ease('linear')
        .attr('x', d => x(d.value) + 5)
        .attr('y', d => y(top_n + 1) + 5)
        .remove();

    yearText.html(~~year);

    if (year >= 2020) {
        clearInterval(ticker);
    } else {
        year = d3.format('.1f')((+year) + 0.1);
    }
}, tickDuration);
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8">

</script>

<body>

</body>
text {
    font-size: 12px;
    font-family: Open Sans, sans-serif;
}

text.title {
    font-size: 24px;
    font-weight: 500;
}

text.subTitle {
    font-weight: 500;
    fill: #777777;
}

text.caption {
    font-weight: 400;
    font-size: 14px;
    fill: #777777;
}

text.label {
    font-weight: 600;
}

text.valueLabel {
    font-weight: 300;
}

text.yearText {
    font-size: 64px;
    font-weight: 700;
    opacity: 0.25;
}

.tick text {
    fill: #777777;
}

.xAxis .tick:nth-child(2) text {
    text-anchor: start;
}

.tick line {
    shape-rendering: CrispEdges;
    stroke: #dddddd;
}

.tick line.origin {
    stroke: #aaaaaa;
}

path.domain {
    display: none;
}