[英]D3: keeping position of SVG's relative while panning and zooming
我正在嘗試進行SVG平面圖的概念驗證,該平面圖可以平移和縮放,並且還可以在頂部放置標記。 縮放/平移發生時,標記不會停留在位置上。 我知道為什么會發生這種情況,但不確定在平移/縮放時將標記保持在適當位置的最佳方法。
這是代碼:
var svg = d3.select(".floorplan")
.attr("width", "100%")
.attr("height", "100%")
.call(d3.zoom().on("zoom", zoomed))
.select("g")
var marker = d3.selectAll(".marker")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
)
function zoomed() {
svg.attr("transform", d3.event.transform);
}
function dragstarted(d) {
console.log('dragstarted');
}
function dragged(d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
}
function dragended(d) {
console.log('drag ended: marker:'+ d3.select(this).attr('data-id') + ' position: ' + d3.event.x +', ' + d3.event.y);
}
還有一個Codepen可以在這里直觀地看到: https ://codepen.io/danielhoff/pen/WzQbRr
我還有其他限制,即標記元素不應包含在平面圖svg中。
這是您的Codepen的修改版本,可修正拖動事件期間標記的移動, 同時將標記保持在平面圖svg容器之外 :
https://codepen.io/xavierguihot/pen/OvyRPY?editors=0010
回到上下文,一個簡單的解決方案是將標記元素包含在平面圖容器中(以使標記獲得與平面圖相同的縮放事件),但是在這里,我們希望標記位於其自己的svg中容器。
而且這不是小事!
Appart通過在html標記中包含ID(以便從html中選擇這些元素),僅對javascript部分進行了修改。
讓我們深入了解達到這一點所需的步驟:
首先:讓我們修改zoomed
功能,使其同樣適用於標記:
最初,這是縮放功能:
function zoomed() {
svg.attr("transform", d3.event.transform);
}
以及修改后的版本:
function zoomed() {
// Before zooming the floor, let's find the previous scale of the floor:
var curFloor = document.getElementById('floorplan');
var curFloorScale = 1;
if (curFloor.getAttribute("transform")) {
var curFloorTransf = getTransformation(curFloor.getAttribute("transform"));
curFloorScale = curFloorTransf.scaleX;
}
// Let's apply the zoom
svg.attr("transform", d3.event.transform);
// And let's now find the new scale of the floor:
var newFloorTransf = getTransformation(curFloor.getAttribute("transform"));
var newFloorScale = newFloorTransf.scaleX;
// This way we get the diff of scale applied to the floor, which we'll apply to the marker:
var dscale = newFloorScale - curFloorScale;
// Then let's find the current x, y coordinates of the marker:
var marker = document.getElementById('Layer_1');
var currentTransf = getTransformation(marker.getAttribute("transform"));
var currentx = currentTransf.translateX;
var currenty = currentTransf.translateY;
// And the position of the mouse:
var center = d3.mouse(marker);
// In order to find out the distance between the mouse and the marker:
// (43 is based on the size of the marker)
var dx = currentx - center[0] + 43;
var dy = currenty - center[1];
// Which allows us to find out the exact place of the new x, y coordinates of the marker after the zoom:
// 38.5 and 39.8 comes from the ratio between the size of the floor container and the marker container.
// "/2" comes (I think) from the fact that the floor container is initially translated at the center of the screen:
var newx = currentx + dx * dscale / (38.5/2);
var newy = currenty + dy * dscale / (39.8/2);
// And we can finally apply the translation/scale of the marker!:
d3.selectAll(".marker").attr("transform", "translate(" + newx + "," + newy + ") scale(" + d3.event.transform.k + ")");
}
這大量使用getTransformation函數,該函數允許檢索元素的當前轉換詳細信息。
然后:但是現在,在縮放后,當我們拖動標記時,它會恢復其原始大小:
這意味着在應用拖動變換時,我們必須調整標記的拖動功能以保持其當前縮放比例:
這是最初的拖動功能:
function dragged(d) {
var x = d3.event.x;
var y = d3.event.y;
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
}
及其修改版本:
function draggedMarker(d) {
var x = d3.event.x;
var y = d3.event.y;
// As we want to keep the same current scale of the marker during the transform, let's find out the current scale of the marker:
var marker = document.getElementById('Layer_1');
var curScale = 1;
if (marker.getAttribute("transform")) {
curScale = getTransformation(marker.getAttribute("transform")).scaleX;
}
// We can thus apply the translate And keep the current scale:
d3.select(this).attr("transform", "translate(" + x + "," + y + "), scale(" + curScale + ")");
}
最后:拖動地板時,我們還必須相應地拖動標記:
因此,我們必須覆蓋地板的默認拖動,以便將相同的dragg事件包括到標記中。
這是應用於地板的拖動功能:
function draggedFloor(d) {
// Overriding the floor drag to do the exact same thing as the default drag behaviour^^:
var dx = d3.event.dx;
var dy = d3.event.dy;
var curFloor = document.getElementById('svg-floor');
var curScale = 1;
var curx = 0;
var cury = 0;
if (curFloor.getAttribute("transform")) {
curScale = getTransformation(curFloor.getAttribute("transform")).scaleX;
curx = getTransformation(curFloor.getAttribute("transform")).translateX;
cury = getTransformation(curFloor.getAttribute("transform")).translateY;
}
d3.select(this).attr("transform", "translate(" + (curx + dx) + "," + (cury + dy) + ")");
// We had to override the floor drag in order to include in the same method the drag of the marker:
var marker = document.getElementById('Layer_1');
var currentTransf = getTransformation(marker.getAttribute("transform"));
var currentx = currentTransf.translateX;
var currenty = currentTransf.translateY;
var currentScale = currentTransf.scaleX;
d3.selectAll(".marker").attr("transform", "translate(" + (currentx + dx) + "," + (currenty + dy) + ") scale(" + currentScale + ")");
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.