Riot.js предоставляет легковесный и реактивный способ создания компонентов пользовательского интерфейса. D3.js специализируется на визуализации данных, управлении документами через данные и создании интерактивных графиков и диаграмм. Объединение этих двух библиотек позволяет строить динамичные и интерактивные компоненты с минимальной связкой к DOM.
Ключевая задача при интеграции — сохранение реактивности
Riot.js при управлении элементами D3.js. D3 оперирует прямым
доступом к DOM через селекторы и методы визуализации
(enter(), update(), exit()), в то
время как Riot.js управляет DOM через виртуальные компоненты. Основная
стратегия — использовать D3 только внутри lifecycle-хуков Riot, чтобы
изменения данных в Riot корректно отражались на визуализации.
<bar-chart>
<div ref="chart"></div>
<script>
import * as d3 from "d3";
export default {
props: {
data: Array
},
onMounted() {
this.drawChart(this.props.data);
},
onUpdated() {
this.updateChart(this.props.data);
},
drawChart(data) {
const width = 500;
const height = 300;
const svg = d3.select(this.$refs.chart)
.append("svg")
.attr("width", width)
.attr("height", height);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * 50)
.attr("y", d => height - d.value)
.attr("width", 40)
.attr("height", d => d.value)
.attr("fill", "steelblue");
this.svg = svg;
},
updateChart(data) {
const height = 300;
const svg = this.svg;
const bars = svg.selectAll("rect")
.data(data);
bars.enter()
.append("rect")
.merge(bars)
.transition()
.duration(500)
.attr("y", d => height - d.value)
.attr("height", d => d.value);
bars.exit().remove();
}
}
</script>
</bar-chart>
Ключевые моменты:
ref="chart" создаёт точку привязки DOM для D3.onMounted используется для первичного рендеринга
графика.onUpdated позволяет обновлять график при изменении
props.data.enter(), merge() и exit()
обеспечивают корректное управление добавлением, обновлением и удалением
элементов.Для динамических визуализаций данные обычно хранятся в состоянии
родительского компонента. Riot.js автоматически вызывает
onUpdated при изменении props или состояния.
Это позволяет связать D3 с реактивной моделью следующим образом:
<app>
<bar-chart data={this.chartData}></bar-chart>
<script>
import BarChart from './bar-chart.riot';
export default {
components: { BarChart },
state: {
chartData: [
{ value: 50 }, { value: 100 }, { value: 150 }
]
},
onMounted() {
setInterval(() => {
this.updateData();
}, 2000);
},
updateData() {
this.state.chartData = this.state.chartData.map(d => ({
value: Math.floor(Math.random() * 200)
}));
this.update();
}
}
</script>
</app>
Особенности подхода:
this.state и вызов
this.update() автоматически инициирует обновление
D3-графика.D3 поддерживает богатую интерактивность через события
(on("click"), on("mouseover")). В сочетании с
Riot.js можно связывать события с методами компонента:
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * 50)
.attr("y", d => height - d.value)
.attr("width", 40)
.attr("height", d => d.value)
.attr("fill", "steelblue")
.on("mouseover", (event, d) => {
this.emit("barHover", d);
});
this.emit("barHover", d) позволяет передавать события
из дочернего компонента в родительский.<bar-chart onbarHo ver={this.handleHover}>.Для динамического изменения размеров графика используется D3-система масштабов:
const xScale = d3.scaleBand()
.domain(data.map((d, i) => i))
.range([0, width])
.padding(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([height, 0]);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => height - yScale(d.value))
.attr("fill", "orange");
Использование scaleBand и scaleLinear
обеспечивает:
transition) для
анимации обновлений.<svg> при
обновлениях — лучше хранить ссылку на элемент и использовать D3-методы
для модификации.keyed data при связывании данных с
элементами для корректного обновления.transition для плавных визуальных эффектов
вместо полного перерисовывания.Комбинирование Riot.js и D3 позволяет строить:
Каждый график может быть изолированным компонентом с собственным состоянием и lifecycle-хуками, что делает архитектуру приложения модульной и легко поддерживаемой.