Здесь есть несколько соображений, и, учитывая то, что они, конечно, были спокоены ими в прошлом, я надеюсь, что смогу объяснить их здесь ясно.
Экстент масштабирования
Давайте рассмотрим экстент масштабирования (zoom.extent
) - не переводить экстент.Экстентом по умолчанию является «[[0, 0], [width, height]]
, где width - это ширина клиента элемента, а height - его высота клиента» ( d3-zoom docs ).Поскольку вы вызываете масштабирование на svg
, экстент по умолчанию должен быть [0,0],[width,height]
, где ширина и высота в вашем случае равны 500.
Ваш экстент перевода, [100,100],[400,400]
меньше чем ваш масштаб, это не сработает, от Майка Бостока (Mike Bostock) по аналогичной проблеме: «Проблема в том, что указанный вами translateExtent меньше, чем экстент масштабирования. Поэтому нет способа удовлетворить запрошенное ограничение."( средство отслеживания проблем d3-zoom ).
TranslateExtent
Проблема заключается в том, что вы используете неверный перевод экстента.Указанный вами экстент перевода - это границы, которые вы хотите ограничить кругом.Но это не равно экстенту перевода, который представляет собой границы координатного пространства, которое вы хотите показать (границы мира, в котором находится круг) при заданном экстенте масштабирования.
Давайте рассмотрим круг в [100,100]
, он центрирован там с трансфокацией с помощью translate(0,0)
: он находится в начальной позиции.Это помечает верхнюю левую позицию для ограничительной рамки, в которой вы надеетесь ограничить окружность. Верхняя левая координата увеличения в этой точке равна [0,0]
.В правом нижнем углу экстента масштабирования или области просмотра находится [500,500]
.
Если окружность находится в [400,400]
, в правом нижнем углу ее предполагаемого движения она имеет преобразование translate(300,300)
, так как оно равно 300пиксели вправо и 300 пикселей вниз от того места, где это началось (изначально с помощью cx / cy).Учитывая, что все смещено на 300 пикселей вниз и вправо, верхний левый угол области просмотра или масштабирования теперь равен [-300,-300]
(круг с cx, cy из -300 будет иметь центр в верхнем левом углу SVG, учитывая преобразование масштабирования).И в правом нижнем углу - [200,200]
.
. Для начала, когда круг не может двигаться дальше вверх или влево, у нас есть показанный экстент [0,0],[500,500]
, а когда круг находится в правом нижнем углу, когдакруг не может двигаться дальше вниз или вправо, у нас есть показанный экстент [-300,-300],[200,200]
.
Если брать крайности, то максимальный экстент, который мы хотим получить, равен: [-300,-300],[500,500]
, это экстент мира, который мы хотим показать, чтобы круг оставался перекрывающимся с прямоугольником:
var height = 500;
var width = 500;
var zoom = d3.zoom()
.translateExtent([[-300, -300], [500, 500]])
.on("zoom", zoomed);
var svg = d3.select("body")
.append("svg")
.attr("width", height)
.attr("height", width)
.append("g")
svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("height", 300)
.attr("width", 300);
var circle = svg.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 20)
.style("fill", "red")
svg.call(zoom);
function zoomed() {
circle.attr("transform", d3.event.transform);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
Возможное уточнение
Если мы используем масштабирование с шириной и высотой, равными ширине и высотепрямоугольник:
.extent([[0,0],[300,300]])
Нам не нужно расширять наш translateExtent для учета пустого пространства вокруг прямоугольника, который все еще находится внутри SVG:
.translateExtent([[-300,-300],[300,300]])
var height = 500;
var width = 500;
//if extent is specified, sets the translate extent to the specified array of points [[x0, y0], [x1, y1]], where [x0, y0] is the top-left corner of the world and [x1, y1] is the bottom-right corner of the world, and returns this zoom behavior.
var zoom = d3.zoom()
.translateExtent([[-300,-300],[300,300]])
.extent([[0,0],[300,300]])
.on("zoom", zoomed);
console.log(zoom.extent());
// Feel free to change or delete any of the code you see in this editor!
var svg = d3.select("body")
.append("svg")
.attr("width", height)
.attr("height", width);
svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("height", 300)
.attr("width", 300);
var circle = svg.append("circle")
.attr("cx", 100)
.attr("cy", 100)
.attr("r", 20)
.style("fill", "red")
svg.call(zoom);
function zoomed() {
circle.attr("transform", d3.event.transform);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>