Разработка интерактивных графиков для мобильных устройств требует отдельного подхода к проектированию интерфейсов и обработке данных. Ограниченная ширина экрана, сенсорное управление, производительность слабых устройств и особенности мобильных браузеров напрямую влияют на архитектуру визуализации.
D3.js предоставляет низкоуровневые инструменты для полной адаптации графиков под мобильные устройства:
Основой мобильной визуализации становится отказ от фиксированных размеров.
const svg = d3.select("#chart")
.append("svg")
.attr("width", 1200)
.attr("height", 600);
Фиксированные размеры приводят к:
viewBoxНаиболее распространённый способ адаптации — применение
viewBox.
const width = 1200;
const height = 600;
const svg = d3.select("#chart")
.append("svg")
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "auto");
D3.js часто комбинируется с обычным JavaScript для получения текущих размеров окна.
const screenWidth = window.innerWidth;
const screenHeight = window.innerHeight;
На основе этих значений создаются условия адаптации.
const isMobile = screenWidth < 768;
const width = 1000;
const height = 500;
const width = window.innerWidth - 20;
const height = 300;
const isMobile = window.innerWidth < 768;
const width = isMobile ? window.innerWidth - 20 : 1000;
const height = isMobile ? 300 : 500;
Одной из главных проблем мобильной визуализации становится перегрузка интерфейса.
Если график содержит:
то мобильный экран становится нечитаемым.
На мобильных устройствах часто отображают не полные данные, а агрегированные.
[
{ date: "2025-01-01", value: 12 },
{ date: "2025-01-02", value: 14 },
...
]
const weeklyData = d3.rollups(
data,
values => d3.mean(values, d => d.value),
d => d3.timeWeek(new Date(d.date))
);
Оси являются одной из главных причин визуального шума.
const axis = d3.axisBottom(xScale);
D3 автоматически создаёт большое количество подписей.
const axis = d3.axisBottom(xScale)
.ticks(5);
const ticksCount = isMobile ? 3 : 10;
const axis = d3.axisBottom(xScale)
.ticks(ticksCount);
Иногда количество меток невозможно уменьшить автоматически.
svg.selectAll(".x-axis text")
.style("display", (d, i) => i % 2 ? "none" : "block");
Длинные подписи оси X плохо помещаются на мобильных экранах.
svg.selectAll(".x-axis text")
.attr("transform", "rotate(-45)")
.style("text-anchor", "end");
"Сентябрь 2025"
"Сен 25"
const format = d3.timeFormat("%b %y");
Большие легенды занимают значительную часть экрана.
На узких экранах боковые легенды уменьшают рабочую область.
const legendY = isMobile ? height + 40 : 20;
На мобильных устройствах отображают только ключевые элементы.
if (isMobile) {
svg.select(".grid").remove();
}
Сложные анимации сильно нагружают мобильные устройства.
selection.transition()
.duration(3000)
.ease(d3.easeBounce);
selection.transition()
.duration(300);
Иногда мобильный режим полностью исключает transitions.
if (!isMobile) {
bars.transition()
.duration(1000)
.attr("height", d => y(d.value));
}
SVG плохо масштабируется при большом количестве элементов.
При тысячах объектов:
Canvas предпочтителен для:
D3 может использоваться только для расчётов.
const x = d3.scaleLinear()
.domain([0, 100])
.range([0, width]);
А отрисовка выполняется через Canvas API.
context.fillRect(
x(d.value),
y(d.other),
2,
2
);
Мобильные устройства используют касания вместо мыши.
selection.on("mouseover", handler);
selection.on("touchstart", handler);
Современный подход — использование pointer events.
selection.on("pointerenter", handler);
Мелкие элементы трудно нажимать пальцем.
.attr("r", 3)
.attr("r", isMobile ? 10 : 4)
Иногда визуальный элемент остаётся маленьким, но зона нажатия увеличивается.
svg.append("circle")
.attr("r", 20)
.style("opacity", 0);
Tooltip на мобильных устройствах работает иначе.
selection.on("click", (event, d) => {
tooltip
.style("display", "block")
.html(d.value);
});
На мобильных устройствах tooltip часто закрепляют сверху.
tooltip
.style("top", "10px")
.style("left", "10px");
Современные мобильные интерфейсы часто выводят информацию снизу экрана.
Слишком яркая палитра ухудшает восприятие на маленьких экранах.
Тонкие линии плохо видны на смартфонах.
.attr("stroke-width", 1)
.attr("stroke-width", 3)
Мелкий текст плохо читается при касании.
const fontSize = isMobile ? "14px" : "10px";
Многокомпонентные dashboards плохо работают на мобильных устройствах.
Необязательно отрисовывать весь контент сразу.
График создаётся только после появления в области видимости.
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.isIntersecting) {
renderChart();
}
});
});
Мобильные устройства часто меняют ориентацию экрана.
window.addEventListener("resize", () => {
updateChart();
});
Иногда проще удалить график и создать заново.
function updateChart() {
d3.select("svg").remove();
renderChart();
}
D3.js обычно комбинируется с адаптивными стилями.
@media (max-width: 768px) {
.chart-title {
font-size: 16px;
}
}
Широкие графики плохо помещаются на смартфонах.
Иногда упрощение невозможно без прокрутки.
.chart-wrapper {
overflow-x: auto;
}
Большое количество SVG-узлов замедляет мобильные браузеры.
data.forEach(() => {
svg.append("circle");
});
Тысячи SVG-элементов создают серьёзную нагрузку.
svg.append("line");
svg.append("line");
svg.append("line");
svg.append("path")
.attr("d", lineGenerator(data));
Карты особенно тяжело работают на мобильных устройствах.
Force simulation требует больших вычислений.
simulation.alphaDecay(0.1);
const visibleNodes = data.slice(0, 100);
На мобильных устройствах вычисления желательно выполнять заранее.
const processed = heavyCalculation(data);
внутри каждого обновления графика.
Без debounce resize вызывает множество перерисовок.
function debounce(fn, delay) {
let timeout;
return function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
fn();
}, delay);
};
}
window.addEventListener(
"resize",
debounce(updateChart, 200)
);
На мобильных устройствах часто показывают только базовую информацию.
Сложный zoom неудобен на мобильных устройствах.
if (!isMobile) {
svg.call(zoom);
}
Иногда разрешают только масштабирование по одной оси.
zoom.scaleExtent([1, 3]);
Даже корректно работающий график может тормозить на слабых смартфонах.
Причина:
Причина:
Причина:
Причина:
Современный адаптивный график в D3.js обычно включает: