我試圖通過在 d3js 中拖動來移動一個圓圈,但我無法讓它在 y 軸方向上移動。
我正在使用 d3js 的 v7,經過多次嘗試,我發現拖動函式中的event.x和event.y是被拖動游標圖形上的坐標。因此,我嘗試用比例變換函式對這兩個坐標進行變換,得到在svg上顯示的坐標。
這里有兩個問題。
首先是由 獲得的 y 坐標event.y與實際垂直運動相反。例如,如果我實際運行代碼并將圓圈拖動到右上方,則圓圈將移動到右下方。這是圓初始位置的 y 坐標上的線對稱運動;y 坐標移動的正負值是相反的。對于event.dy. event.x和中的 x 坐標event.dx作業正常。此外,d3.pointer(event)不能按預期作業。
第二個問題是圓圈在拖動開始時跳躍。我通過查看在拖動程序中執行的函式找到了造成這種情況的原因。event.x和的初始event.y值是圓的坐標的初始值,即使在多次拖動之后,也會導致圓在被拖動時總是從圓的初始值開始移動。
我有一個重現問題的程式。
如何解決這些問題?
謝謝。
這是代碼。
var dataset = [
{x: 5, y: 20, name: "one"},
{x: 100, y: 33, name: "two"},
{x: 250, y: 50, name: "three"},
{x: 480, y: 90, name: "four"},
];
const width = 400;
const height = 300;
const margin = { "top": 30, "bottom": 60, "right": 30, "left": 60 };
const svgWidth = width margin.left margin.right;
const svgHeight = height margin.top margin.bottom;
var svg = d3.select("#graph-area").append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.style("border-radius", "20px 20px 20px 20px")
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d.x; }) 10])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d.y; }) 10])
.range([height, 0]);
var axisx = d3.axisBottom(xScale).ticks(5);
var axisy = d3.axisLeft(yScale).ticks(5);
var x = svg.append("g")
.attr("transform", "translate(" 0 "," height ")")
.call(axisx)
.attr("class", "x_axis")
x.append("text")
.attr("x", (width - margin.left - margin.right) / 2 margin.left)
.attr("y", 35)
.attr("text-anchor", "middle")
.attr("font-size", "10pt")
.attr("font-weight", "bold")
.text("X Label")
.attr("class", "x_axis")
var y = svg.append("g")
.call(axisy)
.attr("class", "y_axis")
y.append("text")
.attr("x", -(height - margin.top - margin.bottom) / 2 - margin.top)
.attr("y", -35)
.attr("transform", "rotate(-90)")
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("font-size", "10pt")
.text("Y Label")
.attr("class", "y_axis")
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width )
.attr("height", height )
.attr("x", 0)
.attr("y", 0);
var circle = svg.append("g")
.attr("id", "scatterplot")
.attr("clip-path", "url(#clip)");
circle.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return xScale(d.x); })
.attr("cy", function(d) { return yScale(d.y); })
.attr("fill", "steelblue")
.attr("r", 20)
.on('mouseover',function(elem, data) {
//console.log("over data name: " data.name);
})
.on('mouseout',function (elem, data) {
//console.log("out data name: " data.name);
})
.on("mousedown", function(elem, data){
console.log("down data name: " data.name);
})
.on("mouseup", function(elem, data){
console.log("up data name: " data.name);
})
.call(d3.drag().on("drag", circleDragged).on("start", circleStartDrag))
function circleStartDrag(event){
console.log("start drag");
}
function circleDragged(event){
d3.select(this).attr('cx', xScale(event.x)).attr('cy', yScale(event.y));
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Scatter Plot</title>
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="graph-area" stype="position: relative;"></div
</body>
</html>
編輯(拖動功能中坐標計算的感知)
該drag函式參考資料中設定的值 (cx和cy) 并指示游標的新坐標,這些坐標反映dy和dx來自這些值。因此,在我的情況下,我將未縮放的資料值設定為event.subject,因此當我直接在上方拖動時,它將(cx=250, cy=-40)根據(cx=250, cy=-50)和進行計算dy=-1。這反映在event.y. 這就是為什么我在拖動程序中所說的圓的 y 坐標是顛倒的原因。
正確的值被設定為執行(cx=204, cy=150)時資料的縮放值enter()。當圓圈直接向上拖動時,event.y設定為cy=140基于dy=-1。在svg中的像素坐標中,y坐標值減小意味著向上移動,所以圓向上移動。此時我需要更新d.x=event.y以考慮到event.subject對下一次拖動中的值的參考。
uj5u.com熱心網友回復:
這是給定代碼的預期結果。
您最初定位將scale(d.y)資料值轉換為像素值的資料點。當您拖動時,您獲取像素值 (event.y) 并再次應用比例 - 縮放像素值,就好像它們是資料一樣。
dy = 0 的資料值height按比例映射到。如果您拖動位于 SVG 頂部的圓圈(y=0),則比例將回傳heightSVG 底部 - 因此您的反轉。
解決方案是只使用 event.y / event.x 值,因為這些值已經代表像素值 - 無需縮放它們。如果要將像素值轉換為資料值,則可以使用 scale.invert() - 但這僅對更新資料有用,而不是每個圓的像素坐標。
從拖動中洗掉縮放功能揭示了最后一個問題 - 當拖動隨著滑鼠移動時,您仍然可以在拖動開始時跳轉:拖動主題(被拖動物體的參考坐標)默認情況下是 x,y 屬性你的資料。由于這表示基準中的資料值,而不是縮放的像素值,因此您會得到一個跳躍。最快的解決方案是將基準的 x,y 屬性重新定義為縮放的像素值,并在拖動時更新這些值。
var dataset = [
{x: 5, y: 20, name: "one"},
{x: 100, y: 33, name: "two"},
{x: 250, y: 50, name: "three"},
{x: 480, y: 90, name: "four"},
];
const width = 400;
const height = 300;
const margin = { "top": 30, "bottom": 60, "right": 30, "left": 60 };
const svgWidth = width margin.left margin.right;
const svgHeight = height margin.top margin.bottom;
var svg = d3.select("#graph-area").append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight)
.style("border-radius", "20px 20px 20px 20px")
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top})`);
var xScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d.x; }) 10])
.range([0, width]);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset, function(d) { return d.y; }) 10])
.range([height, 0]);
var axisx = d3.axisBottom(xScale).ticks(5);
var axisy = d3.axisLeft(yScale).ticks(5);
var x = svg.append("g")
.attr("transform", "translate(" 0 "," height ")")
.call(axisx)
.attr("class", "x_axis")
x.append("text")
.attr("x", (width - margin.left - margin.right) / 2 margin.left)
.attr("y", 35)
.attr("text-anchor", "middle")
.attr("font-size", "10pt")
.attr("font-weight", "bold")
.text("X Label")
.attr("class", "x_axis")
var y = svg.append("g")
.call(axisy)
.attr("class", "y_axis")
y.append("text")
.attr("x", -(height - margin.top - margin.bottom) / 2 - margin.top)
.attr("y", -35)
.attr("transform", "rotate(-90)")
.attr("text-anchor", "middle")
.attr("font-weight", "bold")
.attr("font-size", "10pt")
.text("Y Label")
.attr("class", "y_axis")
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width )
.attr("height", height )
.attr("x", 0)
.attr("y", 0);
var circle = svg.append("g")
.attr("id", "scatterplot")
.attr("clip-path", "url(#clip)");
circle.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) { return d.x = xScale(d.x); })
.attr("cy", function(d) { return d.y = yScale(d.y); })
.attr("fill", "steelblue")
.attr("r", 20)
.on('mouseover',function(elem, data) {
//console.log("over data name: " data.name);
})
.on('mouseout',function (elem, data) {
//console.log("out data name: " data.name);
})
.on("mousedown", function(elem, data){
console.log("down data name: " data.name);
})
.on("mouseup", function(elem, data){
console.log("up data name: " data.name);
})
.call(d3.drag().on("drag", circleDragged).on("start", circleStartDrag))
function circleStartDrag(event){
console.log("start drag");
}
function circleDragged(event){
d3.select(this).attr('cx', d=>d.x=event.x).attr('cy', d=>d.y=event.y);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Scatter Plot</title>
<script src="https://code.jquery.com/jquery-3.6.0.slim.min.js" integrity="sha256-u7e5khyithlIdTpu22PHhENmPcRdFiHRjhAuHcs05RI=" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<div id="graph-area" stype="position: relative;"></div
</body>
</html>
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/462646.html
標籤:d3.js
上一篇:默認情況下僅設定1級深度,而不是在d3-hierarchy中顯示全部
下一篇:D3強制具有嵌套圓圈的有向圖節點
