我使用d3.js根据列表myList
中的数据在SVG容器上绘制一些绿色圆圈。
以下是该圈子的一个例子:
现在我想实现以下行为:
以下是我为解决此问题而编写的代码(@ Cyril的帮助,谢谢!)。但它不能正常工作。当鼠标指针悬停在圆圈上时,矩形是可见的。但是,当鼠标指针向东南方向移动到矩形中时(即使矩形的一部分与圆的象限重叠),圆形的mouseout
事件也会触发,矩形消失 - 甚至在之前矩形的mouseover
事件还没有发生。从技术上讲,我认为这仍然是在圈子里。但显然d3.js没有。
那么,鉴于这些鼠标事件的复杂性以及伴随它们的微小差异(以及竞争条件),我该如何实现此功能呢?
var myList = [
{"centerX": 200, "centerY": 300, "mouseIn": {"circle":false, "rectangle":false}},
{"centerX": 400, "centerY": 500, "mouseIn": {"circle":false, "rectangle":false}},
];
var myCircle = self.svgContainer.selectAll(".dots")
.data(myList).enter().append("circle")
.attr("class", "dots")
.attr("cx", function(d, i) {return d.centerX})
.attr("cy", function(d, i) {return d.centerY})
.attr("r", 15)
.attr("stroke-width", 0)
.attr("fill", function(d, i) {return "Green"})
.style("display", "block");
myCircle.on({
"mouseover": function(d) {
console.log('\n\nCircle MouseOver ******************************************');
var wasCursorIn = d.mouseIn.circle || d.mouseIn.rectangle;
console.log('wasCursorIn = ', JSON.stringify(wasCursorIn));
d.mouseIn.circle = true;
console.log('d.mouseIn = ', JSON.stringify(d.mouseIn));
var isCursorIn = d.mouseIn.circle || d.mouseIn.rectangle;
console.log('isCursorIn = ', isCursorIn);
if ((!wasCursorIn) && isCursorIn) {
if (typeof d.rectangle === 'undefined' || d.rectangle === null)
d.rectangle = self.svgContainer.append("rect")
.attr("x", d.centerX)
.attr("y", d.centerY)
.attr("width", 100)
.attr("height", 50)
.attr("stroke-width", 2)
.attr("fill", "DimGray")
.attr("stroke", "DarkKhaki")
.on("mouseover", function(e) {
console.log('\n\nRectangle MouseOver ***************************************');
console.log("d = ", JSON.stringify(d));
d.mouseIn.rectangle = true;
console.log("d = ", JSON.stringify(d));
}
)
.on("mouseout", function(e) {
console.log('\n\nRectangle MouseOut ****************************************');
console.log("d = ", JSON.stringify(d));
var wasCursorOut2 = (!d.mouseIn.circle) && (!d.mouseIn.rectangle);
console.log('wasCursorOut2 = ', wasCursorOut2);
d.mouseIn.rectangle = false;
console.log('d.mouseIn = ', JSON.stringify(d.mouseIn));
var isCursorOut2 = (!d.mouseIn.circle) && (!d.mouseIn.rectangle);
console.log('isCursorOut2 = ', isCursorOut2);
if ((!wasCursorOut2) && isCursorOut2) {
d3.select(this).style("cursor", "default");
d.rectangle.remove();
d.rectangle = null;
}
}
)
.style("display", "block");
else
d.rectangle.style("display", "block");
}
},
"mouseout": function(d) {
console.log('\n\nCircle MouseOut *******************************************');
var wasCursorOut = (!d.mouseIn.circle) && (!d.mouseIn.rectangle);
console.log('wasCursorOut = ', wasCursorOut);
d.mouseIn.circle = false;
console.log('d.mouseIn = ', JSON.stringify(d.mouseIn));
var isCursorOut = (!d.mouseIn.circle) && (!d.mouseIn.rectangle);
console.log('isCursorOut = ', isCursorOut);
if ((!wasCursorOut) && isCursorOut) {
if (!(typeof d.rectangle === 'undefined' || d.rectangle === null))
d.rectangle.style("display", "none");
}
}
}
);
答案 0 :(得分:1)
当SVG元素重叠时,鼠标事件将触发最顶层元素。当鼠标从一个元素移动到另一个元素时,事件的顺序是mouseout事件(对于鼠标离开的元素),然后是mouseover事件(对于鼠标正在进入的元素)。由于您只想在鼠标同时离开circle和rect元素时删除rect元素,因此您需要在circle和rect元素上监听mouseout事件,并且只在鼠标位置位于两者之外时才删除rect元素元件。
以下是确定鼠标位置是否在元素内的一种可能的解决方案。使用svg的getScreenCTM()。inverse()矩阵将鼠标事件的客户端坐标转换为svg坐标。使用该点构造1x1矩阵。使用svg的checkIntersection()来确定矩形是否与元素相交。
以下代码段以简单的javascript(即没有D3.js)演示了这个解决方案。
var svgNS = "http://www.w3.org/2000/svg";
var svg = document.getElementById("mySvg");
var circle = document.getElementById("myCircle");
var rect = null;
circle.addEventListener("mouseover", circle_mouseover);
circle.addEventListener("mouseout", circle_mouseout);
function circle_mouseover(e) {
if (!rect) {
rect = document.createElementNS(svgNS, "rect");
rect.setAttribute("x", circle.getAttribute("cx"));
rect.setAttribute("y", circle.getAttribute("cy"));
rect.setAttribute("width", 100);
rect.setAttribute("height", 50);
rect.setAttribute("style", "fill: gray;");
rect.addEventListener("mouseout", rect_mouseout);
svg.appendChild(rect);
}
}
function circle_mouseout(e) {
console.log("circle_mouseout");
if (rect) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
p = p.matrixTransform(svg.getScreenCTM().inverse());
var r = svg.createSVGRect();
r.x = p.x;
r.y = p.y;
r.width = 1;
r.height = 1;
if(!svg.checkIntersection(rect, r)) {
rect.removeEventListener("mouseout", rect_mouseout);
svg.removeChild(rect);
rect = null;
}
}
}
function rect_mouseout(e) {
var p = svg.createSVGPoint();
p.x = e.clientX;
p.y = e.clientY;
p = p.matrixTransform(svg.getScreenCTM().inverse());
var r = svg.createSVGRect();
r.x = p.x;
r.y = p.y;
r.width = 1;
r.height = 1;
if(!svg.checkIntersection(circle, r)) {
rect.removeEventListener("mouseout", rect_mouseout);
svg.removeChild(rect);
rect = null;
}
}

<svg id="mySvg" width="150" height="150">
<circle id="myCircle" cx="50" cy="50" r="25" style="fill: green;"/>
</svg>
&#13;
注意:我认为FireFox尚未实现checkIntersection()函数。如果您需要支持FireFox,那么您将需要一种不同的方法来检查点和元素的交集。如果你只处理圆形和矩形,那么很容易编写自己的函数来检查交集。