问题
I am attempting to use two different area generators inside one .data() selection, so that one set of points can have an area rendered from top to bottom while the second set's area can be rendered from left to right. I can pass fat arrow functions to every .attr() except 'd', which produces the following error:
Error: <path> attribute d: Expected moveto path command ('M' or 'm'), "function area(da…".
I need to access the selection index in order to set the area from an array of booleans. Please see the jsfiddle I've set up with the relevant code. I've commented two of my attempts to pass a function to the .attr('d').
回答1:
if you inspect your generated code, you'll see this:
So, you are not geting values. Instead call the function.
Add (d) to the return value so that the area generator function is executed:
.attr('d', function(d, i){
if (horizontalNotVertical[i]) {
return horizontalArea(d);
} else {
return verticalArea(d);
}
})
回答2:
The issue in your code is simply the famous first argument in D3.
Your comment:
I was wondering why D3 executes the area generator automatically if it's passed by itself (
.attr('d', myArea)) but doesn't automatically execute if the area generator is returned by an anonymous function...
Is not correct. D3 has no problems executing functions returned by functions.
When you do...
.attr('d', myArea)
... the datum is automatically passed as the first argument to myArea. Let's see a basic demo:
var data = ["foo", "bar", "baz"];
var sel = d3.select("body").selectAll(null)
.data(data)
.enter()
.append("whatever")
sel.attr("whatever", callback)
function callback(d) {
console.log("The datum is " + d)
}
<script src="https://d3js.org/d3.v5.min.js"></script>
So, if you want to put your myArea function inside an anonymous function, you have to specify the first argument, because the datum is passed to the outer function, not to myArea:
.attr('d', function(d){
//1st arg here------^
myArea(d);
//and here-^
});
Let's see it:
var data = ["foo", "bar", "baz"];
var sel = d3.select("body").selectAll(null)
.data(data)
.enter()
.append("whatever")
sel.attr("whatever", function(d) {
callback(d);
})
function callback(d) {
console.log("The datum is " + d)
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Therefore, in your case, you just need:
.attr('d', (d, i) => (horizontalNotVertical[i]) ? horizontalArea(d) : verticalArea(d));
Just to show that D3 has no problems executing a function returned by another function, have a look at this last snippet:
var data = ["foo", "bar", "baz"];
var sel = d3.select("body").selectAll(null)
.data(data)
.enter()
.append("whatever")
sel.attr("whatever", function() {
callback();
})
function callback() {
console.log("I don't have the datum!")
}
<script src="https://d3js.org/d3.v5.min.js"></script>
Here is the updated jsfiddle: https://jsfiddle.net/zt5vxbm6/
来源:https://stackoverflow.com/questions/49520535/passing-a-function-to-attrd