如何执行d3拖放
Posted
技术标签:
【中文标题】如何执行d3拖放【英文标题】:how to perform d3 drag and drop 【发布时间】:2019-03-30 19:58:08 【问题描述】:角度 4 的 d3.drag 存在问题。每当我拖动矩形对象时,它第一次移动良好。释放 mousepress 并再次尝试拖动矩形后,它会返回上一个事件并且无法在可拖动对象上进行鼠标控制。请解决我的问题。
import Component,Input, ElementRef, OnInit from '@angular/core';
import * as d3 from 'd3';
interface LineData
xVal: number,
yVal:number
@Component(
selector: 'app-line-chart',
template:'<svg ></svg>',
styleUrl: []
)
export class LineChartComponent implements OnInit
@Input() data : LineData[];
private parentNativeElement : any;
constructor(private element:ElementRef)
this.parentNativeElement = element.nativeElement;
ngOnInit()
var width = 300;
var height = 300;
var margin = top: 10, right: 10, bottom: 30, left: 10
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x)
.scale(x)
.ticks(5);
var yAxis = d3.axisLeft(y)
.scale(y)
.ticks(5);
var valueline:any = d3.line()
.x(function (d)
return x(d['xVal']);
)
.y(function (d)
return y(d['yVal']);
);
console.log(valueline);
var svg = d3.select("svg");
d3.select(this.parentNativeElement)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
// Get the data
var data = [
"xVal": 1,
"yVal": 2
,
"xVal": 2,
"yVal": 4
,
"xVal": 3,
"yVal": 1
,
"xVal": 4,
"yVal": 5
,
"xVal": 5,
"yVal": 3
];
// Scale the range of the data
x.domain(d3.extent(data,
function (d)
return d.xVal;
));
y.domain([
0, d3.max(data,
function (d)
return d.yVal;
)
]);
let color = d3.scaleOrdinal(d3.schemeCategory10);
svg.append("path").datum(data).attr("class","path")// Add the valueline path.
.attr("fill", "none")
.attr("stroke", "red")
.attr("stroke-width", 1.5)
.attr("d", valueline(data)).attr("class", "line");
let rectangle:any = d3.range(1).map(function()
return
x: Math.floor(Math.random()*width),
y: Math.floor(Math.random()*height)
;
);
console.log(rectangle);
let dragRect = svg.selectAll('g').data(rectangle).enter().append("g")
dragRect.append("rect")
.attr("x",function(d)return d['x'];)
.attr("y",function(d)return d['y'];)
.attr("height",50)
.attr("width",50).style("fill", "steelblue");
svg.selectAll('g').attr("transform",
"translate(" + margin.left + "," + margin.top + ")").data(rectangle)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function dragstarted(d)
d3.select(this).raise().classed("active",true);
function dragged(d)
d.xVal = x.invert(d3.event.x);
d.yVal = y.invert(d3.event.y);
d3.select(this).select("rect")
.attr("x", x(d.xVal))
.attr("y", y(d.yVal))
.attr("transform","translate("+d.xVal+","+d.yVal+")")
console.log(d);
function dragended(d)
d3.select(this).raise().classed("active",false);
//d3.select('rect#no-drag').on('mousedown.drag',null);
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g") // Add the Y Axis
.attr("class", "y axis"`enter code here`)
.call(yAxis);
【问题讨论】:
这适用于具有可拖动对象(矩形)的折线图。在移动矩形时,将呈现拖动的轴值 【参考方案1】:基本问题似乎是dragged
函数不记得连续拖动事件之间的 x 和 y。
要做到这一点,你需要
d3.select(this)
.attr("x", d.x = x(d.xVal))
.attr("y", d.y = y(d.yVal))
而不是
d3.select(this)
.attr("x", x(d.xVal))
.attr("y", y(d.yVal))
运行这段代码sn-p检查一下
console.clear()
var width = 300;
var height = 300;
var margin = top: 10, right: 10, bottom: 30, left: 10
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x).scale(x).ticks(5);
var yAxis = d3.axisLeft(y).scale(y).ticks(5);
var valueline = d3.line()
.x(function (d) return x(d['xVal']); )
.y(function (d) return y(d['yVal']); );
var svg = d3.select("svg");
d3.select(this.parentNativeElement)
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
// Get the data
var data = [
"xVal": 1,
"yVal": 2
,
"xVal": 2,
"yVal": 4
,
"xVal": 3,
"yVal": 1
,
"xVal": 4,
"yVal": 5
,
"xVal": 5,
"yVal": 3
];
// Scale the range of the data
x.domain(d3.extent(data,
function (d)
return d.xVal;
));
y.domain([
0, d3.max(data,
function (d)
return d.yVal;
)
]);
let color = d3.scaleOrdinal(d3.schemeCategory10);
svg.append("path").datum(data).attr("class","path")
.attr("fill", "none")
.attr("stroke", "red")
.attr("stroke-width", 1.5)
.attr("d", valueline(data)).attr("class", "line");
let rectangle = d3.range(3).map(function()
return
x: Math.floor(Math.random()*width),
y: Math.floor(Math.random()*height)
;
);
let dragRect = svg.selectAll('g').data(rectangle).enter()
.append("g")
dragRect.append("rect")
.attr("x",function(d)return d['x'];)
.attr("y",function(d)return d['y'];)
.attr("height", 50)
.attr("width", 50)
.style("fill", "steelblue")
svg.selectAll('rect')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.data(rectangle)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
const dragBounds =
const tickHeight = 10;
function setDragBounds(subject)
dragBounds.top = 0 - margin.top;
dragBounds.left = 0 - margin.left;
dragBounds.bottom = height - tickHeight - subject.attr('height');
dragBounds.right = width - margin.right - subject.attr('width');
function dragstarted(d)
/*
Calculate drag bounds at dragStart because it's one event vs many
events if done in 'dragged()'
*/
setDragBounds(d3.select(this))
d3.select(this).raise().classed("active", true);
function dragged(d)
d3.select(this)
.attr("x", getX(d.x = d3.event.x) )
.attr("y", getY(d.y = d3.event.y) );
function getX(x)
return x < dragBounds.left ? dragBounds.left
: x > dragBounds.right ? dragBounds.right
: x
function getY(y)
return y < dragBounds.top ? dragBounds.top
: y > dragBounds.bottom ? dragBounds.bottom
: y
function dragended(d)
d3.select(this).classed("active", false);
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
svg.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg ></svg>
请注意,我将拖动事件附加到 rect
而不是 g
,我认为这是一个错字。
保持在界限内
为了将拖动限制在我发现最有效的方式是对 x 和 y 值的最大/最小函数。
function dragged(d)
d3.select(this)
.attr("x", getX(d.x = d3.event.x) )
.attr("y", getY(d.y = d3.event.y) );
function getX(x)
return x < dragBounds.left ? dragBounds.left
: x > dragBounds.right ? dragBounds.right
: x
function getY(y)
return y < dragBounds.top ? dragBounds.top
: y > dragBounds.bottom ? dragBounds.bottom
: y
在拖动开始时设置界限以避免重复任何计算。
const dragBounds =
const tickHeight = 10;
function setDragBounds(subject)
dragBounds.top = 0 - margin.top;
dragBounds.left = 0 - margin.left;
dragBounds.bottom = height - tickHeight - subject.attr('height');
dragBounds.right = width - margin.right - subject.attr('width');
function dragstarted(d)
/*
Calculate drag bounds at dragStart because it's one event vs many
events if done in 'dragged()'
*/
setDragBounds(d3.select(this))
d3.select(this).raise().classed("active", true);
【讨论】:
我怎样才能设置只指定到 x 和 y 轴的可拖动矩形对象?我该如何设置它,它不应该移动到轴或线路径之外? 好问题,请看脚注。 Richard Matsen - 它对我有用。太感谢了 。我学会了。以上是关于如何执行d3拖放的主要内容,如果未能解决你的问题,请参考以下文章