在 svg 元素上挂钩鼠标事件时,NVD3 图表控件不起作用
NVD3 chart controls don't work when hooking mouse events on svg elements
我在 nvd3 中有一个面积图:
var chart = nv.models.stackedAreaChart()
.x(function (d) { return d[0] })
.y(function (d) { return Math.round(d[1]) })
.clipEdge(true)
.showControls(true)
.useInteractiveGuideline(true);
如您所见,我启用了 showControls,它会在图表的左上角显示三个小按钮(Stacked、Stream 和 Expanded)。
由于希望通过将鼠标拖到图表的 select 子部分,我通过在包含图表的 SVG 元素上连接 mouseup、mousedown 和 mousemove 事件来实现以下解决方案。
var mouseDown = false;
var mouseDownCoords;
var rect = svg.append("rect")
.attr("x", 0).attr("y", 0)
.attr("width", 0).attr("height", 0)
.attr("fill", "rgba(43,48,87,0.3)");
svg.on('mousedown', function () {
var height = svg[0][0].height;
mouseDownCoords = d3.mouse(this);
mouseDown = true;
rect.attr("x", mouseDownCoords[0]);
rect.attr("height", height.animVal.value);
// Register mousemove when the mouse button is down
svg.on('mousemove', function () {
var coords = d3.mouse(this);
rect.attr("width", Math.max(coords[0] - mouseDownCoords[0], 0));
});
});
svg.on('mouseup', function () {
if (mouseDown) {
var coords = d3.mouse(this);
var width = Math.max(coords[0] - mouseDownCoords[0], 0);
mouseDown = false;
rect.attr("width", 0);
if (width > 0) {
var totalWidth = svg[0][0].width.animVal.value;
var totalPeriod = dateTo.getTime() - dateFrom.getTime();
var newDateFrom = new Date(Math.floor(dateFrom.getTime() + totalPeriod * mouseDownCoords[0] / totalWidth));
var newDateTo = new Date(Math.floor(newDateFrom.getTime() + totalPeriod * width / totalWidth));
window.setSearchTimeframe(newDateFrom, newDateTo);
}
}
// Unregister mousemove
svg.on('mousemove', null);
});
但是,注册这些事件回调会使控制按钮停止工作。当我点击它们时,没有任何反应,即使当我悬停它们时指针正确改变。
你是对的,在 NVD3 内置事件系统之外的元素上注册事件似乎真的会破坏内部的东西(我认为不应该是这种情况)。您可以通过在图表中需要自定义行为的部分放置一个不可见元素来解决此问题。
演示
红色矩形是具有自定义行为的图表部分(单击它)。
var chartElement = d3.select("#chart svg");
var chart;
nv.addGraph(function() {
chart = nv.models.pieChart()
.x(function(d) {
return d.label
})
.y(function(d) {
return d.value
})
.showLabels(true);
var chartData = [{
label: "Foo",
value: 67
}, {
label: "Bar",
value: 33
}];
chartElement
.datum(chartData)
.call(chart);
$("#customUI").on("mousedown", function() {
alert("Some custom behaviour...");
});
return chart;
});
#wrapper {
position: relative;
}
#chart {
position: absolute;
height: 500px;
}
#customUI {
position: absolute;
background: red;
opacity: 0.2;
width: 100px;
height: 100px;
left: 100px;
top: 200px;
}
#customUI:hover {
opacity: 0.5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.css" rel="stylesheet" />
<div id="wrapper">
<div id="chart">
<svg>
</svg>
</div>
<div id="customUI">
</div>
</div>
我在 nvd3 中有一个面积图:
var chart = nv.models.stackedAreaChart()
.x(function (d) { return d[0] })
.y(function (d) { return Math.round(d[1]) })
.clipEdge(true)
.showControls(true)
.useInteractiveGuideline(true);
如您所见,我启用了 showControls,它会在图表的左上角显示三个小按钮(Stacked、Stream 和 Expanded)。
由于希望通过将鼠标拖到图表的 select 子部分,我通过在包含图表的 SVG 元素上连接 mouseup、mousedown 和 mousemove 事件来实现以下解决方案。
var mouseDown = false;
var mouseDownCoords;
var rect = svg.append("rect")
.attr("x", 0).attr("y", 0)
.attr("width", 0).attr("height", 0)
.attr("fill", "rgba(43,48,87,0.3)");
svg.on('mousedown', function () {
var height = svg[0][0].height;
mouseDownCoords = d3.mouse(this);
mouseDown = true;
rect.attr("x", mouseDownCoords[0]);
rect.attr("height", height.animVal.value);
// Register mousemove when the mouse button is down
svg.on('mousemove', function () {
var coords = d3.mouse(this);
rect.attr("width", Math.max(coords[0] - mouseDownCoords[0], 0));
});
});
svg.on('mouseup', function () {
if (mouseDown) {
var coords = d3.mouse(this);
var width = Math.max(coords[0] - mouseDownCoords[0], 0);
mouseDown = false;
rect.attr("width", 0);
if (width > 0) {
var totalWidth = svg[0][0].width.animVal.value;
var totalPeriod = dateTo.getTime() - dateFrom.getTime();
var newDateFrom = new Date(Math.floor(dateFrom.getTime() + totalPeriod * mouseDownCoords[0] / totalWidth));
var newDateTo = new Date(Math.floor(newDateFrom.getTime() + totalPeriod * width / totalWidth));
window.setSearchTimeframe(newDateFrom, newDateTo);
}
}
// Unregister mousemove
svg.on('mousemove', null);
});
但是,注册这些事件回调会使控制按钮停止工作。当我点击它们时,没有任何反应,即使当我悬停它们时指针正确改变。
你是对的,在 NVD3 内置事件系统之外的元素上注册事件似乎真的会破坏内部的东西(我认为不应该是这种情况)。您可以通过在图表中需要自定义行为的部分放置一个不可见元素来解决此问题。
演示
红色矩形是具有自定义行为的图表部分(单击它)。
var chartElement = d3.select("#chart svg");
var chart;
nv.addGraph(function() {
chart = nv.models.pieChart()
.x(function(d) {
return d.label
})
.y(function(d) {
return d.value
})
.showLabels(true);
var chartData = [{
label: "Foo",
value: 67
}, {
label: "Bar",
value: 33
}];
chartElement
.datum(chartData)
.call(chart);
$("#customUI").on("mousedown", function() {
alert("Some custom behaviour...");
});
return chart;
});
#wrapper {
position: relative;
}
#chart {
position: absolute;
height: 500px;
}
#customUI {
position: absolute;
background: red;
opacity: 0.2;
width: 100px;
height: 100px;
left: 100px;
top: 200px;
}
#customUI:hover {
opacity: 0.5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.css" rel="stylesheet" />
<div id="wrapper">
<div id="chart">
<svg>
</svg>
</div>
<div id="customUI">
</div>
</div>