Вы можете отслеживать второе преобразование масштаба (я назову это yTransform
) и использовать его для изменения масштаба оси y. Поскольку вы хотите, чтобы x масштабировался нормально, вы все равно можете использовать d3.event.transform.rescaleX()
для изменения масштаба по оси X, в то время как yTransform
можно использовать для изменения масштаба по оси Y.
При панорамировании значение перевода y yTransform
должно быть обновлено в соответствии с текущим состоянием масштабирования. И наоборот, при масштабировании следует использовать yTransform
для отмены изменения y-преобразования состояния масштабирования.
Возможно что-то вроде:
var yTransform = d3.zoomIdentity; // initial state for the y transform
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform; // zoom state
x2 = t.rescaleX(x); // rescale x as normal (t.y is irrelevant)
// for a pan event, update the y translate
if (d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
// for a scroll, use the current y translate
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y); // rescale y.
render();
})
Значения k
и x
для yTranslate
значения не имеют: масштаб всегда равен 1, так как мы не увеличиваем масштаб, а преобразование x не имеет значения для изменения масштаба по оси y. Выше не учитываются события двойного щелчка, но я добавлю это ниже .
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yTransform = d3.zoomIdentity;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") yTransform.y = t.y;
else t.y = yTransform.y;
y2 = yTransform.rescaleY(y);
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>
Поскольку я изменяю свойства напрямую - это не самое идеальное.
В качестве альтернативы, мы могли бы отслеживать смещение перевода на y (разница между ay переводится с dbl щелчками / событиями колеса и без этих событий). Оба смещения y и y translate могут быть использованы для создания соответствующего преобразования масштабирования:
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
var dots = d3.range(100)
.map(function() {
return {x: Math.random(), y: Math.random()}
})
var x = d3.scaleLinear().range([0,500])
var x2 = x.copy();
var y = d3.scaleLinear().range([0,300])
var y2 = y.copy();
var canvas = d3.select("canvas")
var context = canvas.node().getContext("2d");
// Just for reference:
var axis = d3.axisRight(y);
var g = d3.select("svg").append("g");
g.call(d3.axisRight(y2))
render();
var yOffset = 0;
var lastY = 0;
var zoom = d3.zoom()
.on("zoom", function() {
var t = d3.event.transform;
x2 = t.rescaleX(x);
// For dbl clicks, d3.event.sourceEvent is null.
if (d3.event.sourceEvent && d3.event.sourceEvent.type != "wheel") {
lastY = t.y - yOffset;
y2 = d3.zoomIdentity.translate(0,t.y-yOffset).rescaleY(y);
}
else {
yOffset = t.y - lastY; // ignore change in y for dbl click and wheel events
}
render();
})
canvas.call(zoom);
function render() {
context.clearRect(0,0,500,300);
dots.forEach(function(d) {
context.beginPath();
context.arc(x2(d.x), y2(d.y), 5, 0, 2 * Math.PI);
context.stroke();
})
g.call(d3.axisRight(y2));
}
canvas, svg {
position: absolute;
top: 0;
left: 0;
}
svg {
pointer-events:none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="500" height="300"></canvas>
<svg width="500" height="300"></svg>