简体   繁体   English

d3.js在悬停时传递多个函数

[英]d3.js passing in multiple functions on hover

I used a tutorial to get this function on mouseover: 我使用了一个教程来在鼠标悬停时获得此功能:

function arcTween(outerRadius, delay) {
    return function () {
        d3.select(this).transition().delay(delay).attrTween("d", function (d) {
            var i = d3.interpolate(d.outerRadius, outerRadius);
            return function (t) { d.outerRadius = i(t); return arc(d); };
        });
    };
} 

And I add it to parts of a pie chart this way: 然后通过以下方式将其添加到饼图的各个部分:

.on("mouseover", arcTween(outerRadius, 0, 0))

However, I also have text tags added to svg for each slice in the pie chart, and want those to fade away if you are hovering over a different slice. 但是,我还为饼图中的每个切片在svg中添加了文本标签,并且如果将鼠标悬停在另一个切片上,希望它们消失。 So I gave those tags IDs when I create them based on the index, then wrote these two methods: 因此,当我基于索引创建标签时,我给了它们标签ID,然后编写了这两种方法:

function visibilityShow(dataSetSize) {
    for (var i = 0; i < dataSetSize; i++) {
        $("#" + i).show();
    }
}

function visibilityHide(index, dataSetSize) {
    for (var i = 0; i < dataSetSize; i++) {
        if (i === index) {
            $("#" + i).show();
        } else {
            $("#" + i).hide();
        }
    }
}

Now these work in a vacuum, but when I try to put them on a mouseover event, it won't work. 现在这些都是在真空中工作的,但是当我尝试将它们置于鼠标悬停事件上时,它将无法工作。 arcTween stops working, and "i" is always 0. These were what I tried: arcTween停止工作,并且“ i”始终为0。这些是我尝试的操作:

Adding another .on("mouseover", ...) 添加另一个.on(“ mouseover”,...)

        .on("mouseover", arcTween(outerRadius, 0))
        .on("mouseover", visibility(0, dataSet.length));

and also tried passing in the index with: 并尝试通过以下方式传递索引:

        .on("mouseover", arcTween(outerRadius, 0))
        .on("mouseover", function (d, i) { return visibility(i, d.length) });

But that always passes in i = 0 in addition to seemingly overwriting the arcTween() call. 但是,除了看似覆盖了arcTween()调用之外,这总是传递i = 0。

I also tried 我也试过

.on("mouseover", function (d, i) {
     return function {
          arcTween(outerRadius, 0);
          visibility(i, d.length);
     }
})

Anyone have any advice? 有人有什么建议吗? (I'm using v3 because all the tutorials online are outdated.) (我使用的是v3,因为所有在线教程都已过时。)

Thanks! 谢谢!

EDIT: Code Snippet 编辑:代码段

 // This data will be gathered from API calls eventually dataDefault = []; dataController = [{ "label": "Example 1", "value": 1, "child": [{ "label": "Child 1", "value": 1 }] }, { "label": "Example 2", "value": 1, "child": [{ "label": "Child 1", "value": 1 }] }, { "label": "Example 3", "value": 1, "child": [{ "label": "Child 1", "value": 1 }] }, { "label": "Example 4", "value": 1, "child": [{ "label": "Child 1", "value": 1 }] }, { "label": "Example 5", "value": 1, "child": [{ "label": "Child 1", "value": 1 }] }]; var displaySize = 20; // This is used to keep track of what data is showing var mode = "Default"; // The amount of pixels the SVG will take up var width = 600, height = 675; // It's a donut, so it has an outer radius and an inner radius. 2r = width so r = width/2 var outerRadius = width / 2, innerRadius = outerRadius / 3; // Default color function for deciding the colros of the donut slices var color = d3.scale.category10(); // The pie function for deciding the size of the donut slices var pie = d3.layout.pie() .value(function (d) { return d["value"]; }); // At first we use the default data to create the pie var pieData = pie(dataDefault); // Create an arc var arc = d3.svg.arc() .innerRadius(innerRadius); // Add an SVG tag to the document var svg = d3.select("#graphs").append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + outerRadius + "," + (outerRadius + 50) + ")"); // Append an link tag for each point of the data, then add an path tag inside each a tag svg.selectAll("a") .data(pieData) .enter().append("a") .append("path") .each(function (d) { d.outerRadius = outerRadius - 20; }) .attr("d", arc) .attr("fill", function (d, i) { return color(i); }) .on("mouseover", arcTween(outerRadius, 0, 0)) .on("mouseout", arcTween(outerRadius - 20, 150)) .append("title") .text(function (d) { return d["value"] + " hits"; }); // Change the default data to the Apps data so it animates on load changeToAPI("Controller", dataController); // Function used to increase slice size on hover function arcTween(outerRadius, delay) { return function () { d3.select(this).transition().delay(delay).attrTween("d", function (d) { var i = d3.interpolate(d.outerRadius, outerRadius); return function (t) { d.outerRadius = i(t); return arc(d); }; }); }; } // Passes the color scale into the change function function getColor(name) { // Get the remainder when / 3 var bucket = hashify(name) % 4; // Setup the array of color functions var colors = [d3.scale.category10(), d3.scale.category20(), d3.scale.category20b(), d3.scale.category20c()]; // Return the correct bucket return colors[bucket]; } // Function used to swap the data being shown function changeToAPI(name, dataSet) { // Don't update if the data is already showing // JavaScript doesn't short circuit? if (dataSet === null) { dataSet = [{ "label": "No data...", "value": 1 }]; changeTo(name, dataSet); } else if (dataSet.length === 0) { dataSet = [{ "label": "No data...", "value": 1 }]; changeTo(name, dataSet); } else { mode = name; // Get the new pie and color functions var newData = pie(dataSet); var newColor = getColor(name); // Remove the labels, titles, and tooltips svg.selectAll("text").remove(); svg.selectAll("title").remove(); // Line below fixes an error that doesn't cause issues, but makes the graph ugly :( svg.selectAll("a").remove(); // Add the new slices if there are any var newSlices = svg.selectAll("a") .data(newData); newSlices.enter() .append("a") .append("path") .style("cursor", "pointer"); // Update the attributes of those slices and animate the transition newSlices.select("path") .each(function (d) { d.outerRadius = outerRadius - 20; }) .transition() .attr("d", arc) .attr("fill", function (d, i) { return newColor(i); }) .attr("title", function (d) { return d["value"]; }); newSlices.selectAll("path") .on("click", function (d) { checkForChild(d["data"]["label"], d["data"]); }) .on("mouseover.arcExpand", arcTween(outerRadius, 0)) .on("mouseover.textHide", function (d, i) { visibilityHide(i, dataSet.length); }) .on("mouseout.arcRetract", arcTween(outerRadius - 20, 150)) .on("mouseout.textShow", function (d, i) { visibilityShow(dataSet.length); }); // Remove excess slices newSlices.exit().remove(); // Add a title var title = svg.append("text") .attr("x", 0) .attr("y", -(outerRadius + 10)) .style("text-anchor", "middle") .text("Distrubution of " + name + " Usage"); // Add labels var labels = svg.selectAll(null) .data(newData) .enter() .append("text") .attr("fill", "white") .attr("id", function (d, i) { return i }) .attr("transform", function (d) { d.innerRadius = 0; d.outerRadius = outerRadius; return "translate(" + arc.centroid(d) + ")"; }) .attr("text-anchor", "middle") .text(function (d, i) { return dataSet[i]["label"]; }); // Add tooltips svg.selectAll("path").data(newData).append("title").text(function (d) { return d["value"] + " hits"; }); svg.append("circle") .attr("cx", 0) .attr("cy", 0) .attr("r", innerRadius) .style("fill", "white") .style("cursor", "pointer") .on("click", function () { changeToAPI("Controller", dataController); }); // Adds back button if not at controller level if (dataSet !== dataController) { svg.append("text") .attr("x", 0) .attr("y", 12) .style("text-anchor", "middle") .style("color", "#efefef") .style("font-size", "40px") .text("Back"); } } } function changeTo(name, dataSet) { // Don't update if the data is already showing // JavaScript doesn't short circuit? if (dataSet === null) { dataSet = [{ "label": "No data...", "value": 1 }]; } else if (dataSet.length === 0) { dataSet = [{ "label": "No data...", "value": 1 }]; } mode = name; // Get the new pie and color functions var newData = pie(dataSet); var newColor = getColor(name); // Remove the labels, titles, and tooltips svg.selectAll("text").remove(); svg.selectAll("title").remove(); // Line below fixes an error that doesn't cause issues, but makes the graph ugly :( //svg.selectAll("a").remove(); // Add the new slices if there are any var newSlices = svg.selectAll("a") .data(newData); newSlices.enter() .append("a") .append("path") .style("cursor", "pointer"); // Update the attributes of those slices and animate the transition newSlices.select("path") .each(function (d) { d.outerRadius = outerRadius - 20; }) .transition() .attr("fill", function (d, i) { return newColor(i); }) .attr("d", arc) .attr("title", function (d) { return d["value"]; }); newSlices.selectAll("path") .on("mouseover.arc", arcTween(outerRadius, 0)) .on("mouseover.text", function (d, i) { visibilityHide(i, dataSet.length); }) .on("mouseout.arc", arcTween(outerRadius - 20, 150)) .on("mouseout.text", function (d, i) { visibilityShow(dataSet.length); }); // Remove excess slices newSlices.exit().remove(); // Add a title svg.append("text") .attr("x", 0) .attr("y", -(outerRadius + 10)) .style("text-anchor", "middle") .text(function (e) { var title = "Distrubution of " + name + " Usage"; if (name === "Defualt") { title = "Loading..." } return title; }); // Add labels svg.selectAll(null) .data(newData) .enter() .append("text") .attr("fill", "white") .attr("id", function (d, i) { return i }) .attr("transform", function (d) { d.innerRadius = 0; d.outerRadius = outerRadius; return "translate(" + arc.centroid(d) + ")"; }) .attr("text-anchor", "middle") .text(function (d, i) { return dataSet[i]["label"]; }); // Add tooltips svg.selectAll("path").data(newData).append("title").text(function (d) { return d["value"] + " hits"; }); } function checkForChild(name, dataSet) { if (dataSet.hasOwnProperty("child")) { if (dataSet["child"] !== null) { if (dataSet["child"].length !== 0) { changeToAPI(name, dataSet["child"]); } } } } // Hashcode generator for strings function hashify(string) { var hash = 0; // Add the value of each char to the hash value for (var i = 0; i < string.length; i++) { hash += string.charCodeAt(i); } return hash; } function visibilityShow(dataSetSize) { for (var i = 0; i < dataSetSize; i++) { $("#" + i).show(); } } function visibilityHide(index, dataSetSize) { for (var i = 0; i < dataSetSize; i++) { if (i === index) { $("#" + i).show(); } else { $("#" + i).hide(); } } } 
 body { font-family: Arial; transition: all ease .5s; text-align: center; color: rgb(58,58,58); } 
 <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <script src="https://d3js.org/d3.v3.min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <title>General Statistics</title> </head> <body> <div id="graphs"> </div> </body> </html> 

If you have more than one event listener of the same type in the same selection, you have to namespace your event listeners (but this probably won't fix your issue, please also read the Post Scriptum further down). 如果在同一选择中有多个相同类型的事件侦听器,则必须为事件侦听器命名空间 (但这可能无法解决您的问题,请进一步阅读Post Scriptum )。

The problem right now, as @AndrewReid explained in his comment , is that the next event listener removes the previous one. 正如@AndrewReid在他的评论中解释的那样,当前的问题是下一个事件侦听器将删除上一个事件侦听器。 According to the API : 根据API

If an event listener was already registered for the same type on the selected element, the existing listener is removed before the new listener is added. 如果已在选定元素上为同一类型注册了事件侦听器,则在添加新侦听器之前,将删除现有侦听器。

Let's see it in the following demo. 让我们在下面的演示中看到它。

Since you didn't provide your working code, I create a simple one here, with two event listeners: the first one increases the circle, and the second one fades out the text: 由于您未提供工作代码,因此我在此处创建了一个简单的代码,其中包含两个事件侦听器:第一个事件侦听器增加了圆圈,第二个事件侦听器淡出了文本:

.on("mouseover", increaseCircle)//this one will not work!
.on("mouseover", fadeText)//only this one will work...

You can see that only the last one works: 您可以看到只有最后一个有效:

 var svg = d3.select("svg"); var circle = svg.append("circle") .attr("r", 20) .attr("cx", 100) .attr("cy", 50) .attr("fill", "tan") .attr("stroke", "black") .on("mouseover", increaseCircle) .on("mouseover", fadeText) .on("mouseout", function() { circle.transition().duration(500).attr("r", 20); text.transition().duration(500).style("opacity", 1); }) var text = svg.append("text") .attr("y", 55) .attr("x", 150) .style("font-family", "helvetica") .text("Hover over the circle"); function increaseCircle() { circle.transition().duration(500).attr("r", 40) } function fadeText() { text.transition().duration(500).style("opacity", 0) } 
 <script src="https://d3js.org/d3.v3.js"></script> <svg></svg> 

Solution: 解:

There is a very simple solution, though. 不过,有一个非常简单的解决方案。 According to the same API: 根据相同的API:

To register multiple listeners for the same event type, the type may be followed by an optional namespace, such as "click.foo" and "click.bar" 要为同一事件类型注册多个侦听器,该类型后可以跟一个可选的名称空间,例如“ click.foo”和“ click.bar”

Thus, in the above demo, we just need something like this: 因此,在上面的演示中,我们只需要这样的东西:

.on("mouseover.circle", increaseCircle)
.on("mouseover.text", fadeText)

Here is the demo, both event listeners work: 这是演示,两个事件侦听器都可以工作:

 var svg = d3.select("svg"); var circle = svg.append("circle") .attr("r", 20) .attr("cx", 100) .attr("cy", 50) .attr("fill", "tan") .attr("stroke", "black") .on("mouseover.circle", increaseCircle) .on("mouseover.text", fadeText) .on("mouseout", function() { circle.transition().duration(500).attr("r", 20); text.transition().duration(500).style("opacity", 1); }) var text = svg.append("text") .attr("y", 55) .attr("x", 150) .style("font-family", "helvetica") .text("Hover over the circle"); function increaseCircle() { circle.transition().duration(500).attr("r", 40) } function fadeText() { text.transition().duration(500).style("opacity", 0) } 
 <script src="https://d3js.org/d3.v3.js"></script> <svg></svg> 

Of course, a simple alternative to all this is just: 当然,所有这些的简单替代方案是:

selection.on("mouseover", function(){
    foo();
    bar();
    baz();
    etc...
});

PS : The above answer deals with the namespace issue. PS :以上答案涉及名称空间问题。 However, besides that issue, your code has a couple of problems, which we cannot test because you didn't provide a working demo. 但是,除了该问题之外,您的代码还有很多问题,因为您没有提供有效的演示,所以我们无法对其进行测试。

First problem: when you do this... 第一个问题:执行此操作时...

.on("mouseover", arcTween(outerRadius, 0, 0))

... you are calling arcTween immediately, and passing its value to the listener. ...您正在立即调用arcTween ,并将其值传递给侦听器。 You probably want: 您可能想要:

.on("mouseover", function(){ arcTween(outerRadius, 0, 0)})

Second, this is not correct: 其次,这是不正确的:

.on("mouseover", function (d, i) {
    return function {
        arcTween(outerRadius, 0);
        visibility(i, d.length);
    }
})

It should be just: 应该只是:

.on("mouseover", function (d, i) {
    arcTween(outerRadius, 0);
    visibility(i, d.length);
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM