I have a chart that works off two data sources and essentially the first [updateSetupData(data)] builds the elements the chart needs and the second [update(data)] does not append elements it only updates the html formed by the other function. I use an id as a key to keep things in sync.
function updateSetupData(data) {
var countsByParent = d3.nest()
.key(function (d) { return d.parent + '_tbl'; })
.key(function (d) { return d.SkillGroup + '_grp'; })
//.key(node => node.AgtName)
//.rollup(function(leaves) { return leaves.length;})
.entries(data);
var treeRoot = {
key: "root",
parent: null,
value: "100",
values: countsByParent };
var root = d3.hierarchy(treeRoot, function (d) { return d.values; })
.sum(function (d) { return d.value; });
// .sort(function(a, b) { return b.value - a.value; });
var nodes = pack(root);
//console.log(nodes);
var node = canvas.selectAll(".node")
.data(pack(root).descendants())
.enter().append("g")
.attr("class", function (d) {
return d.data.key == null ? "node " + d.data.AgtName + " agent " :
"node " + d.data.key;
})
.attr("id", function (d) { return d.data.AgtName + "a_" + d.data.AgtId +
"_s" + d.data.skillId + "_g" + d.data.groupId })
.attr("transform", function (d) { return "translate(" + d.x + "," + d.y
+ ")"; })
.attr("fill", "steelblue")
.on("mouseover", function (d) {
highlight(d.label ? d.label : d.data.AgtName);
}).on("mouseout", function (d) { highlight(null); });
function highlight(agtName) {
if (agtName == null) d3.selectAll(".node").classed("active", false);
else d3.selectAll(".node." + agtName).classed("active", true);
}
node.append("circle")
.attr("r", function (d) { return d.r; })
// .attr("fill", "steelblue")
.attr("opacity", 0.25)
.attr("stroke", "#ADADAD")
.attr("stroke-width", "2");
node
.append("svg:title").text(function (d) { return d.data.AgtName; });
var arc = arcGenerator
.outerRadius(function (d, i) { return d.r; })
.startAngle(0)
.endAngle(180);
node.append('defs')
.append('path')
.attr("id", function (d, i) { return "s" + i; })
.attr("d", arc);
//.attr("d", function (d, i) { return getPathData(d.r); } );
node.append("text")
.attr("transform", "rotate(90)")
.attr("text-anchor", function (d) { return d.data.key == null ? "start"
: d.data.key.split("_") [1] === "tbl" ? "end" : "start"; })
.append("textPath")
.attr("startOffset", '50%')
.attr("xlink:href", function (d, i) { return '#s' + i; })
.attr("fill", function (d) { return d.data.key == null ? "none" :
d.data.key.split("_") [1] === "tbl" ? "blue" : "black"; })
.text(function (d) {
return d.data.key == null ? "" :
d.data.key == "root" ? "" : d.data.key.split("_")[0];
});
});
The second function is where I am having the issue. Even though I call .data() and have the new [different] data only used to overlay live calls on the static chart; The classed function just after the .data(data, key) works fine; the (d) there has the new data.
for the var text variable (d) in the data functions is from the other function, so the data to set the text with is wrong.
function update(data) {
var agent = canvas.selectAll(".node.agent")
//sets all elements to false for the class before the update
.classed("newCall", false)
.data(data, function (d) {
// the key is either an element id or an id from the data
var myId = d.id ? d.id : this.id;
// console.log("data key: " + d.id + " element id: " + this.id + "
new: " + d.newCall);
return myId;
}).classed("newCall", function (d) {
var f = d.newCall ? d.newCall : false;
//console.log(this.id + " " + f )
return f;
})
var text = agent.selectAll(".newCall text")
.attr("transform", null)
.attr("startOffset", null)
.attr("xlink:href", null)
.attr("fill", function (d) { return "black"; })
.attr("dx", function (d) { return -4;})
.attr("dy", function (d) { return 4; })
.text(function (d) {
console.log(d);
return "3";
});
Is there something I need to do with the text var to get the right data? I was thinking that because I call .data on the agents var that the text var in the would be OK since it appears that when I class the elements the data is there.
Including the whole fixed function. First the data and classes are cleared then the new data is added. The text elements for agent.newCall are updated when the function runs.
function update(data) {
var agent = canvas.selectAll(".node.agent");
// clear call data
agent.select("text").text("");
// sets all elements to false for the class before the update
agent.classed("newCall", false)
.data(data, function (d) {
// the key is either an element id or an id from the data
var myId = d.id ? d.id : this.id;
// console.log("data key: " + d.id + " element id: " + this.id + " new: " + d.newCall);
return myId;
}).classed("newCall", function (d) {
var f = d.newCall ? d.newCall : false;
//console.log(this.id + " " + f )
return f;
})
agent
.select(".newCall text")
.attr("transform", null)
.style("text-anchor", "middle")
.attr("dy", function (d) { return 4; })
.attr("fill", "black")
.style("font-size", function (d) { return Math.min(2 * d.r, (2 * d.r - 8) / this.getComputedTextLength() * 24) + "px"; })
.text(function (d) {
var txt = d.Calls ? d.Calls.length > 0 ? d.Calls.length : "" : "";
return txt;
});
agent.enter().append("g")
.classed("newCall", function (d) {
return d.newCall ? d.newCall : false;
});
agent.exit().classed("newCall", function (d) {
// console.log(d);
return false;
});
};
Related
I am beginner in d3 v3. I created multiple donut charts. When I move my mouse over a slice, I get the tooltip that appears. But I would also like the slice to grow a little bit.
I have already tried several codes but I can't get there. I wonder if the problem may be related to d3 tip
this is my js file :
//Source : http://bl.ocks.org/mbostock/1305337
var m = 15,
r = 80,
z = d3.scale.ordinal()
.range(["#50FFC5", "#54E868", "#54CCE8", "#6395FF"]);
var pie = d3.layout.pie()
.value(function (d) {
return +d.count;
})
.sort(function (a, b) {
return b.count - a.count;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
var radius = d3.scale.linear()
.range([10, r])
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function (d) {
return d.data.genre + ": " + d.data.count;
})
.direction('s');
d3.csv("../CSV/genreHameau.csv", function (error, hameau) {
if (error) throw error;
var datas = d3.nest()
.key(function (d) {
return d.origine;
})
.entries(hameau);
datas.forEach(function (d) {
totalOrigin = d3.sum(d.values, function (d) {
return +d.count;
})
d.values.forEach(function (dd) {
dd.totalOrigin = totalOrigin
})
})
datas.sort(function (a, b) {
return d3.descending(a.values[0].totalOrigin, b.values[0].totalOrigin)
})
// définir le radius / rayon des arcs (rendre fonction de 'count')
var max = d3.max(datas, function (d) {
return d.values[0].totalOrigin
})
var min = d3.min(datas, function (d) {
return d.values[0].totalOrigin
})
radius.domain([min, max])
arc
.innerRadius(function (d) {
return radius(d.data.totalOrigin) / 2
})
.outerRadius(function (d) {
return radius(d.data.totalOrigin)
})
function size(d) {
return radius(d.values[0].totalOrigin) + m
}
var div = d3.select("body").selectAll("div")
.data(datas)
.enter().append("div") // http://code.google.com/p/chromium/issues/detail?id=98951
.style("display", "inline-block")
.style("width", function (d) {
return 2 * size(d) + "px"
})
.style("height", function (d) {
return 2 * size(d) + "px"
})
.style("min-width", "80px")
.style("min-height", "80px")
div.append("span")
.attr("class", "nomhameau")
.text(function (d) {
return d.key;
})
.append("span")
.attr("class", "nombrehameau")
.text(function (d) {
return " (" + d.values[0].totalOrigin + ")"
});
var svg = div.append("svg")
//.attr("width", (r + m) * 2)
//.attr("height", (r + m) * 2)
.attr("width", function (d) {
return 2 * size(d)
})
.attr("height", function (d) {
return 2 * size(d)
})
.append("g")
.attr("transform", function (d) {
return "translate (" + size(d) + "," + size(d) + ")"
});
svg.call(tip);
var g = svg.selectAll("g")
.data(function (d) {
return pie(d.values);
})
.enter().append("g")
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return z(d.data.genre);
})
.append("title")
});
Thank you for your help
I would do it this way:
Just like you created the arc, create a new one with a greater radius, either inner, outer or both, something like:
var arcHighlight = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r*1.1);
Then you add mouseover and mouseout events to the slice and modify the slice doing something like:
// Add a colored arc path, with a mouseover title showing the count.
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.carrier); })
// new code
.on('mouseover', function(d) {
d3.select(this)
.transition()
.attr('d', arcHighlight(d));
})
.on('mouseout', function(d) {
d3.select(this)
.transition()
.attr('d', arc(d));
})
.append("title")
.text(function(d) { return d.data.carrier + ": " + d.data.count; });
The transition is optional, but it looks fancier ;)
Trying to add new nodes to existing graph with add function end up with all new graph. here is my code:
function Graph(elementId) {
var svg;
var simulation;
var mNodesData = [];
var mEdgesData = [];
var mNode = null;
var mLink = null;
var elementId;
var heightDelta = 100;
var width = window.innerWidth;
var height = window.innerHeight - heightDelta;
return {
init: function () {
svg = d3.select('#' + elementId)
.append("svg")
.attr("width", width)
.attr("height", height);
simulation = d3.forceSimulation()
.force(".edge", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-600))
.force("center", d3.forceCenter(width / 2, height / 2));
},
clearGraph: function () {
$('#' + this.elementId).empty();
},
getNodes: function () {
return mNodesData;
},
getEdges: function () {
return mEdgesData;
},
addNodes: function (nodes) {
mNodesData = mNodesData.concat(nodes);
},
addEdges: function (edges) {
mEdgesData = mEdgesData.concat(edges);
},
draw: function () {
mLink = svg.selectAll(".edge")
.data(mEdgesData)
.enter()
.append("line")
.attr("class", "edge")
.style("stroke", "#ccc")
.style("stroke-width", function (e) {
return 1
/* e.width*/
});
mNode = svg.selectAll(".node")
.data(mNodesData)
.enter()
.append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
mNode.on('mouseover', function (d) {
function removePopup() {
$("#nodePopup").remove();
}
function showPopup(d) {
removePopup();
if (!d['data']) {
return;
}
var data = d['data'];
var htmlStr = '';
htmlStr += '<div id="nodePopup" >';
htmlStr += ' <div><button id="nodePopupCloseButton" type="button" class="close" data-dismiss="alert"><span class="glyphicon glyphicon-remove" style="font-size: 13px;"> </span> </div>';
htmlStr += ' <div class="nodePopupName">' + data['name'] + '</div>';
if (data['desc']) {
if (data['desc'].startsWith("http")) {
htmlStr += ' <a class="nodePopupLink" href="' + data['desc'] + '" target="_blank">Go to post..</a>';
}
else {
htmlStr += ' <div class="nodePopupDesc">' + data['desc'] + '</div>';
}
}
htmlStr += ' <div class="nodePopupGroup">GROUP: ' + data['groupId'] + '</div>';
htmlStr += ' <div class="nodePopupLeader">LEADER: ' + data['leaderId'] + '</div>';
htmlStr += ' <div class="nodePopupImage"><img src="' + d['image'] + '" style="width: 130px;" /></div>';
htmlStr += '</div>';
$("body").append(htmlStr);
$("#nodePopupCloseButton").click(removePopup);
}
showPopup(d);
mNode.filter(function (d1) {
return (d !== d1 && d1.adjacents.indexOf(d.id) == -1);
}).select("image").style("opacity", 0.2);
mNode.filter(function (d1) {
return (d !== d1 && d1.adjacents.indexOf(d.id) == -1);
}).select("circle").style("stroke", "#f6f6f6");
mLink.filter(function (d1) {
return (d !== d1.source && d !== d1.target);
}).style("opacity", 0.2);
mNode.filter(function (d1) {
return (d == d1 || d1.adjacents.indexOf(d.id) !== -1);
}).select("image").style("opacity", 1);
mNode.filter(function (d1) {
return (d == d1 || d1.adjacents.indexOf(d.id) !== -1);
}).select("circle").style("stroke", "gray");
mLink.filter(function (d1) {
return (d == d1.source || d == d1.target);
}).style("opacity", 1);
})
.on('mouseout', function () {
// removePopup();
mNode.select("image").style("opacity", 1);
mNode.select("circle").style("stroke", "gray");
mLink.style("opacity", 1);
});
var nodeCircle = mNode.append("circle")
.attr("r", function (d) {
return 0.5 * Math.max(d.width, d.height)
})
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", "white");
var nodeImage = mNode.append("image")
.attr("xlink:href", function (d) {
return d.image
})
.attr("height", function (d) {
return d.height + ""
})
.attr("width", function (d) {
return d.width + ""
})
.attr("x", function (d) {
return -0.5 * d.width
})
.attr("y", function (d) {
return -0.5 * d.height
})
.attr("clip-path", function (d) {
return "circle(" + (0.48 * Math.max(d.width, d.height)) + "px)"
});
simulation.nodes(mNodesData);
simulation.force(".edge").links(mEdgesData);
simulation.on("tick", function () {
mLink.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
})
mNode.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")"
});
mNode.attr("cx", function (d) {
return d.x = Math.max(d.width, Math.min(width - d.width, d.x));
})
.attr("cy", function (d) {
return d.y = Math.max(d.height, Math.min(height - heightDelta - d.height, d.y));
});
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
}
}
function getData() {
return $.ajax({
url: 'api/v1/data.json',
type: "GET",
});
}
var graph = Graph('d3Graph');
graph.init();
$.when(getData()).then(function (data) {
graph.addNodes(data.nodes);
graph.addEdges(data.edges);
graph.draw();
});
function add() {
graph.addNodes([{
"id": 4,
"image": "images/4.jpeg",
"height": 20,
"width": 20,
"adjacents": [0],
"data": {
"name": "Number4",
"groupId": "Bla4",
"desc": "Desc4",
"leaderId": "1234-1234"
}
}]);
graph.addEdges([{
"source": 4,
"target": 3,
"width": 1
}])
graph.draw();
// graph.draw();
}
if i call to add the original graph stack on the page and a new one with another edge & node is added. How do I achieve that correct? (update the old one without creating all new..
What you currently have:
mLink = svg.selectAll("link")
.data(mEdgesData)
.enter()
.append("line")
Normally selectAll("link") means that you select all html elements which are links. But you append a line and not a link. Therefore the appended element is not part of your selectAll. There is no possibility to know that you just wanna add data to something which already exists.
The solution is to add merge to both links & nodes:
mLink = svg.selectAll(".edge")
.data(mEdgesData)
.enter()
.append("line")
.attr("class", "edge")
.style("stroke", "#ccc")
.style("stroke-width", function (e) {
return 1
/* e.width*/
}).merge(mLink);
mNode = svg.selectAll(".node")
.data(mNodesData)
.enter()
.append("g")
.attr("class", "node").merge(mNode);
I had a similar issue when I tried to create a general update procedure for my graph with dynamic data. When the nodes or links where updated, the redraw function was triggered.
The idea works as follows; on redrawing the graph, i.e. when the data has changed, all old nodes and links are removed and drawn again. Using .join() the nodes remain in their position.
Here is my working example:
redraw = () => {
simulation.nodes(nodes);
simulation.force('link')
.links(links)
.initialize(nodes);
d3.selectAll('g.node').remove(); // remove all old nodes
d3.selectAll('g.node').data(nodes).join();
d3.selectAll("line").data(links).join();
draw();
simulation.alpha(0.1)
simulation.restart();
}
In my draw() function, the nodes and links are created accordingly:
draw = () => {
container.selectAll('line').data(links).join('line');
// creates nodes with rectangles that contain text
container.selectAll('g.node').raise()
.data(nodes).join("g")
.each(function (d, i) {
d3.select(this).append("rect");
var text = d3.select(this).append("text").text(d => d.name)
// calculate text width for function tick() to adapt rect x position
d.width = text.filter(t => t.index == d.index).node()
.getBBox().width;
})
.call(drag(simulation));
}
Where container is a zoomable and draggable SVG element:
container = svg.append('g');
svg.call(d3.zoom()
.on('zoom', () => this.container.attr('transform', d3.event.transform))
.scaleExtent([0.25, 2]))
For completeness, here is my tick() function:
tick = () => {
container.selectAll('rect')
.attr('x', d => d.x - d.width / 2)
.attr('y', d => d.y);
container.selectAll('text')
.attr('x', d => d.x - d.width / 2)
.attr('y', d => d.y);
container.selectAll('line')
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
}
If I have time, I post a JSFiddle later.
I've constructed a D3 visualization that was working on my local machine. However, now i've exported to my server, the code breaks and throws several errors:
Error: invalid value for <circle> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <text> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <circle> attribute r="NaN"
I've had these errors before with similar code and was able to solve them. However, i cannot grasp what is going wrong. Any suggestions? Thanx!
function drawBubbles() {
var margin = 20,
diameter = 740;
var color = d3.scale.linear()
.domain([-1, 10])
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
.interpolate(d3.interpolateHcl);
var pack = d3.layout.pack()
.size([diameter - margin, diameter - margin])
.value(function (d) { return d.size; })
var svg = d3.select("form").append("svg")
.attr("width", 1280)
.attr("height", 800)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("../Resources/output.json", function (error, root) {
if (error) throw error;
var focus = root,
nodes = pack.nodes(root),
view;
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function (d) { return d.children ? color(d.depth) : null; })
.on("click", function (d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
var text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
.style("display", function (d) { return d.parent === root ? "inline" : "none"; })
.text(function (d) { return d.name; });
var node = svg.selectAll("circle,text");
d3.select("form")
.on("click", function () { zoom(root); });
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) { zoomTo(i(t),d); };
});
transition.selectAll("text")
.filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
.each("start", function (d) { if (d.parent === focus) this.style.display = "inline"; })
.each("end", function (d) { if (d.parent !== focus) this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
console.log(d.x)
console.log(d.y)
console.log(d.r)
console.log(k)
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function(d) { return d.r * k; });
}
});
d3.select(self.frameElement).style("height", diameter + "px"); }
I had exactly this issue - although most browsers seemed to treat NaN as zero and so not complain, but Chrome wasn't happy at all.
For me I found the packed nodes collection returned from the packer had NaNs for zero value items passed in (which my data could contain). I just looped all the nodes after the pack call and set them to zero. Something like this should do it (it should probably be a recursive call to avoid duplication, but you get the idea!)...
// Replace any zero value items with zero x/y/r values
nodes.forEach( function (currNode) {
if (!currNode.count) {
currNode.x = 0;
currNode.y = 0;
currNode.r = 0;
currNode.children.forEach( function (currChild) {
if (!currChild.count) {
currChild.x = 0;
currChild.y = 0;
currChild.r = 0;
}
});
}
});
Hope this helps you
Hello I wanted to get the position of a node or text of d3js to submit a popup from its position, thanks. I have this:
nodeEnter.append("a")
.attr("xlink:href", function (d) {
return d.path;
})
.style("text-decoration", "none")
.style("display", function (d) {
return (d.name == node_info.name && d.not_beneficially_held == "true") ? "block" : "none";
})
.append("text")
.attr("x", function (d) {
return (d == root) ? 17 : 15;
})
.attr("y", function (d) {
return (d == root) ? 21 : 25;
})
.attr("text-anchor", function (d) {
return d.children || d._children ? "end" : "start";
})
.text(function (d) {
return "$";
})
.style("fill", function (d) {
return "#4682b4";
})
.on('click', function (d) {
action_click = true;
$('#popup_paid').fadeIn('slow');
$('.popup-overlay').fadeIn('slow');
return false;
})
.on('mouseout', function (d) {
d3.selectAll("a").remove();
});
And this code or the offset jQuery not work for me:
function getAbsoluteElementPosition(element) {
if (typeof element == "string")
element = document.getElementById(element)
if (!element) return { top:0,left:0 };
var y = 0;
var x = 0;
while (element.offsetParent) {
x += element.offsetLeft;
y += element.offsetTop;
element = element.offsetParent;
}
return {top:y,left:x};
var pos = getAbsoluteElementPosition(element);
When a normal HTML if I works perfectly.
Thanks in advance and greetings
So I am working with the d3 fisheye plugin, and I am having some pretty basic problems.
I implemented this very basic code, pretty much a direct copy from here https://github.com/d3/d3-plugins/tree/master/fisheye
fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
//initialize fisheye
chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
dataPoint.each(function(d) { d.fisheye = fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; });
});
But d.fisheye.x and d.fisheye.y are undefined. In fact, looking at fisheye(d), it returns:
{x: undefined, y: undefined, z: 1}
On the other hand, d3.mouse(this) is properly returning an array.
Does any one have suggestion on why this might be occurring?
More code: by the way, the code is like this because it is inside a ext-js panel, so each function (drawWords is a property of this object). It is kind of complicated which is why I hesitated to post it all, and this is still not all the code, but the relevant part I think. I didn't include the initialization of any of the other global variables, or helper functions.
//imagine some sort of onload function
onLoad: function () {
this.drawWords();
this.animateVis();
}
,drawWords: function () {
toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
//initialize word text
this.dataPoint = this.chart.selectAll('text')
.data(toolObject.termometerData, function (d) {return d.word;})
.enter().append('text')
.attr('class', 'points')
.attr('id', function(d) {return d.word + '-point';})
.attr('x', function() {
return toolObject.xScale(toolObject.shiftCount);
})
.attr('y', function (d) {
return toolObject.fanVertical(d, toolObject.shiftCount);
})
.attr('transform', function (d) {
var thisXPosition = toolObject.xScale(toolObject.shiftCount),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' ' + thisYPosition + ')';
})
.attr('text-anchor', 'middle')
.attr('fill-opacity', function (d) {return toolObject.opacityScale(0);})
.text(function(d) {return d.word;});
this.applyFisheye();
}
,fisheye: d3.fisheye.circular()
.radius(200)
.distortion(2)
,applyFisheye: function () {
var toolObject = this;
//initialize fisheye
this.chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
toolObject.dataPoint.each(function(d) { d.fisheye = toolObject.fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; })
.attr('transform', function(d){
return 'translate(0, 0) rotate(-20 ' + d.fisheye.x + ' '+ d.fisheye.y + ')';
});
});
}
,animateVis: function () {
var toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
var tick;
if(this.animationIdArray.length < 1){
tick = setInterval(function(){
animate();
}, this.duration);
this.animationIdArray.push(tick);
}
function animate() {
if(toolObject.shiftCount < toolObject.numDataPoints){
toolObject.shiftCount++;
//animate words
toolObject.dataPoint.transition()
.duration(toolObject.duration)
.ease('linear')
.attr('x', function(d){ return toolObject.xScale(toolObject.shiftCount - 1); })
.attr('y', function(d){ return toolObject.fanVertical(d, toolObject.shiftCount - 1); })
.attr('transform', function(d){
var thisXPosition = toolObject.xScale(toolObject.shiftCount - 1),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount - 1);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' '+ thisYPosition + ')';
})
.attr('fill-opacity', function(d){
return toolObject.opacityScale(d.series[toolObject.shiftCount - 1].freq);
});
toolObject.applyFisheye();
}else{
clearInterval(tick);
toolObject.animationIdArray.shift();
}
}
}
fisheye assumes that the x and y coordinates of your objects are defined by keys named "x" and "y". It's probably enough (but possibly overkill, depending how often this code is called) to use
this.dataPoint
.each(function(d) {
d.x = toolObject.xScale(toolObject.shiftCount);
d.y = toolObject.fanVertical(d, toolObject.shiftCount)
.attr('x', function(d) { return d.x; })
.attr('y', function(d) { return d.y; });
when you //initialize word text