javascript - D3.js 将 realtive 拖动到图形 svg
问题描述
我的代码应该使用 D3.js v6 移动绘制在 svg 图形上的圆圈。圆圈应该被拖动到光标相对于图形的位置,但我认为给出的光标位置是相对于整个窗口而不是图形/svg。我不确定如何将鼠标位置修改为相对于 svg。我也尝试在这里使用此答案的建议:
编辑:
如果我要在 (0.5, 0.5) 这样的位置开始圆,我将如何制作它,以便圆只能沿着以 (0, 0) 为中心、半径为 0.5 的圆的圆周沿路径移动?我尝试使用以下方法将鼠标的位置缩放到 0.5 大小:
x = x*(0.5/(Math.sqrt(x**2 + y**2)));
y = y*(0.5/(Math.sqrt(x**2 + y**2)));
因为这是您如何将矢量缩放到一定长度,同时保持与此处所示相同的方向: https ://math.stackexchange.com/questions/897723/how-to-resize-a-vector-to-a-特定长度
然而,即使它应该将鼠标所在的任何点缩放到以原点为中心、半径为 0.5 的圆上,这似乎也不起作用。
var margin = {top: -20, right: 30, bottom: 40, left: 40};
//just setup
var svg = d3.select("#mysvg")
.attr("width", 300)
.attr("height", 300)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.scaleLinear()
.domain([-1.5, 1.5])
.range([0, 300]);
svg.append("g")
.attr("transform", "translate(0," + 300 + ")")
.call(d3.axisBottom(xAxis));
var yAxis = d3.scaleLinear()
.domain([-1.5, 1.5])
.range([300, 0]);
svg.append("g")
.call(d3.axisLeft(yAxis));
var circle1 = svg.append('circle')
.attr('id', 'circle1')
.attr('cx', xAxis(0))
.attr('cy', yAxis(0))
.attr('r', 10)
.style('fill', '#000000')
.call( d3.drag().on('drag', dragCircle) ); //add drag listener
function dragCircle(event) {
let x = d3.pointer(event, svg.node())[0];
let y = d3.pointer(event, svg.node())[1];
console.log("x: " + x + " y: " + y);
d3.select("#circle1")
.attr("cx", xAxis(x))
.attr("cy", yAxis(y));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.0.0/d3.min.js"></script>
<svg id="mysvg"></svg>
解决方案
两件事情,
D3.pointer 以像素为单位返回单位,这些不需要缩放 - 缩放的目的是采用任意单位并将其转换为像素:
所以而不是:
d3.select("#circle1")
.attr("cx", xAxis(x))
.attr("cy", yAxis(y));
尝试:
d3.select("#circle1")
.attr("cx", x)
.attr("cy", y);
此外,我们希望拖动相对于g
保存绘图并对其应用变换的拖动。这部分详细说明了您链接到的问题。我们可以指定我们希望拖动相对于父级g
:
let x = d3.pointer(event,svg.node())[0];
let y = d3.pointer(event,svg.node())[1];
然后我们可以使用一些三角函数将点约束到一个环上,并让阻力基于与任意点的角度:
let x = d3.pointer(event,svg.node())[0];
let y = d3.pointer(event,svg.node())[1];
let cx = xAxis(0);
let cy = yAxis(0);
let r = xAxis(0.5)-xAxis(0);
let dx = x- cx;
let dy = y-cy;
var angle = Math.atan2(dy,dx);
d3.select("#circle1")
.attr("cx", cx + Math.cos(angle)*r)
.attr("cy", cy + Math.sin(angle)*r);
这给了我们:
var margin = {top: -20, right: 30, bottom: 40, left: 40};
//just setup
var svg = d3.select("#mysvg")
.attr("width", 300)
.attr("height", 300)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var xAxis = d3.scaleLinear()
.domain([-1.5, 1.5])
.range([0, 300]);
svg.append("g")
.attr("transform", "translate(0," + 300 + ")")
.call(d3.axisBottom(xAxis));
var yAxis = d3.scaleLinear()
.domain([-1.5, 1.5])
.range([300, 0]);
svg.append("g")
.call(d3.axisLeft(yAxis));
var circle1 = svg.append('circle')
.attr('id', 'circle1')
.attr('cx', xAxis(0))
.attr('cy', yAxis(0))
.attr('r', 10)
.style('fill', '#000000')
.call( d3.drag().on('drag', dragCircle) ); //add drag listener
var dragCircle = svg.append('circle')
.attr('id', 'circle1')
.attr('cx', xAxis(0))
.attr('cy', yAxis(0))
.attr('r', xAxis(0.5)-xAxis(0))
.style('fill', 'none')
.style('stroke', 'black')
.style('stroke-line',1)
.call( d3.drag().on('drag', dragCircle) ); //add drag listener
function dragCircle(event) {
let x = d3.pointer(event,svg.node())[0];
let y = d3.pointer(event,svg.node())[1];
let cx = xAxis(0);
let cy = yAxis(0);
let r = xAxis(0.5)-xAxis(0);
let dx = x- cx;
let dy = y-cy;
var angle = Math.atan2(dy,dx);
d3.select("#circle1")
.attr("cx", cx + Math.cos(angle)*r)
.attr("cy", cy + Math.sin(angle)*r);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.0.0/d3.min.js"></script>
<svg id="mysvg"></svg>
推荐阅读
- android - android studio RecyclerView 宽度
- javascript - 我可以在 express 的同一个 api 端点中有一个 res.json 和 res.sendFile 吗?
- kubernetes - Rancher Kubernetes 证书支持哪些椭圆曲线?
- c# - 使用 VSTO 更改形状的动画效果设置
- python - 错误:DQN 期望模型对每个动作都有一个维度
- wordpress - 迁移后缺少 ACF 字段,但网站运行正常
- r - 用于查找相似名称的 R 函数?
- theory - 解读神经网络算法的分类报告
- abaqus - 如何使用 Abaqus python 获取表面节点?
- r - 如何将 geom_label 放入 geom_bar