D3: Можно ли масштабировать + панорамировать одну ось и только панорамировать другую?
У меня есть масштабирование и панорамирование для оси x, но я хотел бы добавить панорамирование для оси y. Я попытался использовать d3.behavior.zoom
и d3.event.translate[1]
, чтобы получить значение перевода y и использовать его, но значение перевода изменяется при масштабировании, поэтому, когда перетаскивание по клику перемещает ось y, масштабирование также поворачивает ось y (в неинтуитивном путь).
Я также попытался использовать два экземпляра d3.behavior.zoom
, один для оси x и один для оси y, но только добавленный последний вызывается при событиях масштабирования.
Вот пример, который работает для масштабирования и панорамирования в направлении x, который я также хотел бы добавить y для панорамирования (но не для увеличения):
var x = d3.scale.linear()
.domain([0, 800])
.range([0, 800]);
var y = d3.scale.linear()
.domain([0, 800])
.range([0, 800]);
var rectangleSelector = d3.select('svg')
.append('g')
.selectAll('rect')
.data([[0, 0], [50, 50], [100, 100]])
.enter()
.append('rect')
.attr('fill', 'black')
.attr('x', d => x(d[0]))
.attr('y', d => y(d[1]))
.attr('width', d => x(d[0] + 40) - x(d[0]))
.attr('height', d => y(40));
d3.select('svg')
.call(d3.behavior.zoom().x(x).on('zoom', () => {
rectangleSelector
.attr('x', d => x(d[0]))
.attr('y', d => y(d[1]))
.attr('width', d => x(d[0] + 40) - x(d[0]))
.attr('height', d => y(40));
}));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="800" height="800"></svg>
Ответы
Ответ 1
JSFIDDLE: https://jsfiddle.net/sladav/o8vaecyn/
Для начала поведение масштабирования обрабатывает как панорамирование, так и масштабирование для оси x, и обрабатывается с помощью...
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 10])
.on("zoom", zoomed);
Таким образом, мы будем использовать масштабирование для обработки элементов оси x.
ДЛЯ Y AXIS, чтобы получить JUST поведение панорамирования...
Создайте отдельное поведение перетаскивания, которое захватывает "dy", сдвигает y-домен и повторно применяет его.
-
Добавить в поведение перетаскивания
var drag = d3.behavior.drag()
.on("drag", dragging)
-
Пусть область масштабирования y является переменной (мы будем изменять это при перетаскивании)
var yPan = 0;
var yMin = (-height / 2);
var yMax = (height / 2);
var y = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0]);
-
Добавить функцию для масштабирования оси Y на событии перетаскивания
function dragging(d) {
yPan = d3.event.dy;
yMin += yPan;
yMax += yPan;
y.domain([yMin, yMax])
d3.select(".y.axis").call(yAxis)};
-
Вызовите функцию перетаскивания из элемента SVG
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom)
.call(drag);
Ответ 2
Я выяснил один из способов сделать это, но это похоже на гигантский взлом:
var lastY = 0;
var zoom = d3.behavior.zoom().x(x);
zoom.on('zoom', () => {
var translateY;
if (d3.event.sourceEvent.type === 'mousemove') {
// if it a drag event then use the value from the drag
translateY = d3.event.translate[1];
lastY = translateY;
} else {
// if it a wheel event then set the y translation to the last value
translateY = lastY;
zoom.translate([d3.event.translate[0], translateY]);
}
// translateY can now be used here as an offset to any drawing like so:
rectangleSelector.attr('y', y(d[1] + translateY));
});
Ответ 3
Вы можете использовать
zoom.center
, чтобы получить код немного чище, например:
var lastY = 0;
var x = d3.scale.linear()
.domain([0, 800])
.range([0, 800]);
var y = d3.scale.linear()
.domain([0, 800])
.range([0, 800]);
var rectangleSelector = d3.select('svg')
.append('g')
.selectAll('rect')
.data([[0, 0], [50, 50], [100, 100]])
.enter()
.append('rect')
.attr('fill', 'black')
.attr('x', d => x(d[0]))
.attr('y', d => y(d[1]))
.attr('width', d => x(d[0] + 40) - x(d[0]))
.attr('height', d => y(40));
var zoom = d3.behavior.zoom().center([400, 400]).x(x);
zoom.on('zoom', () => {
var translateY = d3.event.translate[1];
zoom.center([400, translateY]);
rectangleSelector.attr('x', d => x(d[0]))
.attr('y', d => y(d[1] + translateY))
.attr('width', d => x(d[0] + 40) - x(d[0]))
.attr('height', d => y(40));
});
d3.select('svg').call(zoom);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="800" height="800"></svg>