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)
});
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]);
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) {
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)]);
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))
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;
}