Я изучаю d3.js, и у меня есть вопрос:
Следующий код в d3
в основном рисует bar chart
с кнопкой обновления, которая сортирует данные один раз в порядке убывания и один раз в порядке возрастания. Кроме того, появляются числовые метки на столбцах.
Я хотел бы переместить числовую метку из текущего значения в обновленное значение. Например, - если первая полоса имеет числовую метку 20, а новое обновленное значение после сортировки - 100, то я бы хотел, чтобы эта метка переходила с 20 на 100 как (20, 21, ..., 100) во время определенного времени перехода, и наоборот, если исходная метка равна 100, а обновленное значение равно 20, переход идет в порядке убывания, как 100, 99, ..., 20.
const data = [
{key: 0, value: 50},
{key: 1, value: 20},
{key: 2, value: 100},
{key: 3, value: 30},
{key: 4, value: 40},
{key: 5, value: 70}
]
// const dataset = [50, 20, 100, 30, 40]
const svgWidth = 800;
const svgHeight = 400;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([0, svgWidth])
.paddingInner(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, svgHeight]);
const svg = d3.select('#chart')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight);
let bars = svg.selectAll('rect').data(data, d => d.key);
let labels = svg.selectAll('text').data(data);
bars.enter()
.append('rect')
.each(function(d){return this._old = d;})
.attr('width', xScale.bandwidth)
.attr('height', d => yScale(d.value))
.attr('fill', d => `rgb(${d.value}, ${d.value * 2}, ${d.value * 3})`)
.attr('x', (d, i) => xScale(i))
.attr('y', d => svgHeight - yScale(d.value))
.attr('stroke', 'black')
.attr('stroke-width', 3)
labels.enter()
.append('text')
.attr('x', (d, i) => xScale(i) + (xScale.bandwidth() / 2))
.attr('y', d => svgHeight - yScale(d.value) + 20)
.attr('font-size', 20)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.text(d => d.value);
let asc = false;
d3.select('button').on('click', () => {
if(!asc){
data.sort((a,b) => b.value - a.value );
}else{
data.sort((a,b) => a.value - b.value );
};
asc = !asc;
bars = svg.selectAll('rect').data(data, d => d.key);
labels = svg.selectAll('text').data(data);
bars
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.each(function(d){return this._old = d;})
.attr('x', (d, i) => xScale(i))
.attr('height', d => yScale(d.value))
.attr('y', d => svgHeight - yScale(d.value));
labels
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.tween("text", function(d) {
var that = this;
var i = d3.interpolate(0, d.value); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(0));
}
})
.attr('y', d => svgHeight - yScale(d.value) + 20);
})
Я знаю, что могу просто перенести числовое значение с помощью bar
, но я хотел бы знать, как выполнить переход от текущего числового значения к новому значению обновления в качестве упражнения.
Еще один вопрос, касающийся функции анимации: почему мы присваиваем var, который = this, и выбираем его в возвращаемой функции?
Свободный перевод вопроса Transition the numeric labels in a bar chart от участника @BlackMath.
Вы можете получить текущее значение для каждого текста различными способами:
var current = +(this.textContent);
Или используя геттер D3:
var current = +(d3.select(this).text());
Вот ваш код с этим изменением:
const data = [{
key: 0,
value: 50
},
{
key: 1,
value: 20
},
{
key: 2,
value: 100
},
{
key: 3,
value: 30
},
{
key: 4,
value: 40
},
{
key: 5,
value: 70
}
]
// const dataset = [50, 20, 100, 30, 40]
const svgWidth = 800;
const svgHeight = 400;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([0, svgWidth])
.paddingInner(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, svgHeight]);
const svg = d3.select('body')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight);
let bars = svg.selectAll('rect').data(data, d => d.key);
let labels = svg.selectAll('text').data(data);
bars.enter()
.append('rect')
.each(function(d) {
return this._old = d;
})
.attr('width', xScale.bandwidth)
.attr('height', d => yScale(d.value))
.attr('fill', d => `rgb(${d.value}, ${d.value * 2}, ${d.value * 3})`)
.attr('x', (d, i) => xScale(i))
.attr('y', d => svgHeight - yScale(d.value))
.attr('stroke', 'black')
.attr('stroke-width', 3)
labels.enter()
.append('text')
.attr('x', (d, i) => xScale(i) + (xScale.bandwidth() / 2))
.attr('y', d => svgHeight - yScale(d.value) + 20)
.attr('font-size', 20)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.text(d => d.value);
let asc = false;
d3.select('button').on('click', () => {
if (!asc) {
data.sort((a, b) => b.value - a.value);
} else {
data.sort((a, b) => a.value - b.value);
};
asc = !asc;
bars = svg.selectAll('rect').data(data, d => d.key);
labels = svg.selectAll('text').data(data);
bars
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.each(function(d) {
return this._old = d;
})
.attr('x', (d, i) => xScale(i))
.attr('height', d => yScale(d.value))
.attr('y', d => svgHeight - yScale(d.value));
labels
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.tween("text", function(d) {
var current = +(d3.select(this).text());
var that = this;
var i = d3.interpolate(current, d.value); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(0));
}
})
.attr('y', d => svgHeight - yScale(d.value) + 20);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Click</button>
<br>
Свободный перевод ответа от участника @Gerardo Furtado.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
С клиента получаю архив в base64, перевожу в utf, сохраняю, но архив оказывается битымОшибка и код ниже
При нажатии кнопки в центре карты появляется метка, нужно, чтобы новые метки были соединены с последней добавленной и при этом их все можно...
подскажите пожалуйста каким образом из javascript закрыть собственное окно в kodexplorer? Какую функцию top фрейма нужно вызвать и c какими параметрами?...