I am writing a d3 program that reads in stats from NFL teams from a csv file and the user selects the team to view from a dropdown menu. The program then creates circles in the formation of the offense and defense for the stats for that team and displays it. I have the program able to display the some of it so far, but when I select another team, the old circles stay on the screen and the new ones are just appended on top. My question is how do I fix this? I tried adding remove at the end of the below function but it just removes that circle completely
function menuChanged()
{
var selectedValue = d3.event.target.value;
var picked;
for(var i = 0; i < 32; i++)
if(nest[i].key == selectedValue)
picked = i;
rT = svg.selectAll("rTcircle")
.data(nest)
.enter().append("circle")
.attr("class", "dot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 40)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
rG = svg.selectAll("rGcircle")
.data(nest)
.enter().append("circle")
.attr("class", "rGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 190)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
cO = svg.selectAll("oCcircle")
.data(nest)
.enter().append("circle")
.attr("class", "oCdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 340)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lG = svg.selectAll("lGcircle")
.data(nest)
.enter().append("circle")
.attr("class", "lGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 490)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lT = svg.selectAll("lTcircle")
.data(nest)
.enter().append("circle")
.attr("class", "lTdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].rushing) * 0.7; })
.attr("cx", 640)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
var oLineT = svg.selectAll(".text")
.data(nest)
.enter().append("text")
.attr("class","text")
.style("text-anchor", "middle")
.attr("x", 40)
.attr("y", 300)
.style("fill", function() { return nest[picked].values[0].color1; })
.style("stroke", function() { return nest[picked].values[0].color2; })
.style("font-family", "verdana")
.style("stroke-width", 0.7)
.text(function () {return nest[picked].values[0].team; });
rT.data(nest).transition()
.duration(500)
.attr("class", "dot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 40)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
rG.data(nest).transition()
.duration(500)
.attr("class", "rGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 190)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
cO.data(nest).transition()
.duration(500)
.attr("class", "oCdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 340)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lG.data(nest).transition()
.duration(500)
.attr("class", "lGdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 490)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
lT.data(nest).transition()
.duration(500)
.attr("class", "lTdot")
.attr("r", function() { return Math.sqrt(nest[picked].values[0].passing) * 0.7; })
.attr("cx", 640)
.attr("cy", 300)
.style("fill", function () { return nest[picked].values[0].color1; })
.style("stroke", function () { return nest[picked].values[0].color2; });
oLineT.data(nest).transition()
.duration(500)
.attr("class","text")
.style("text-anchor", "middle")
.attr("x", 40)
.attr("y", 300)
.style("fill", function() { return nest[picked].values[0].color1; })
.style("stroke", function() { return nest[picked].values[0].color2; })
.style("font-family", "verdana")
.style("stroke-width", 0.7)
.text(function () {return nest[picked].values[0].team; });
}
This is a classic case of not properly defining the "enter" and the 'update" selections (and the "exit", if any). Unfortunately, just quickly skimming your menuChanged function, it seems to me that you will have to do a lot of changes, in order to make it the "D3 way". Also, you could store the variable parts in... well, variables!
I made a simple demo to show you how the "enter" and the "update" selections work (I'm using v4 here). First, bind the data:
var circles = svg.selectAll(".teamCircles")
.data(data[0][team].positions);
Both "enter" and "update" selections rely on this bound data.
Then, set the "enter" selection (here, I put it inside the menuChanged function, but if the number of players never change, it can be outside the function):
circlesEnter = circles.enter()
.append("circle")
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
And, finally, the "update" selection. The update selection only works on previously existing elements:
circlesUpdate = circles.transition()
.duration(1000)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
Here is the demo:
var w = 500,
h = 300;
var svg = d3.select("#svg").append("svg")
.attr("width", w)
.attr("height", h);
var data = [{
"San Francisco 49": {
color: "red",
positions: [{
x: 110,
y: 50
}, {
x: 35,
y: 56
}, {
x: 230,
y: 200
}, {
x: 431,
y: 50
}, {
x: 310,
y: 250
}]
},
"Green Bay Packers": {
color: "green",
positions: [{
x: 360,
y: 120
}, {
x: 51,
y: 156
}, {
x: 30,
y: 60
}, {
x: 130,
y: 210
}, {
x: 410,
y: 250
}]
},
"Baltimore Ravens": {
color: "purple",
positions: [{
x: 200,
y: 200
}, {
x: 34,
y: 236
}, {
x: 390,
y: 98
}, {
x: 330,
y: 66
}, {
x: 10,
y: 210
}]
}
}];
d3.select("#selection").on("change", menuChanged);
function menuChanged() {
var team = this.value;
var circles = svg.selectAll(".teamCircles")
.data(data[0][team].positions);
circlesEnter = circles.enter()
.append("circle")
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
circlesUpdate = circles.transition()
.duration(1000)
.attr("class", "teamCircles")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", 8)
.attr("fill", data[0][team].color);
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<select name="select" id="selection">
<option value="">Select</option>
<option value="San Francisco 49">San Francisco 49</option>
<option value="Green Bay Packers">Green Bay Packers</option>
<option value="Baltimore Ravens">Baltimore Ravens</option>
</select>
<div id="svg"></div>
PS: by the way, the reason your circles are piling up is because in your enter selections you're selecting something that doesn't exist. For instance:
rT = svg.selectAll("rTcircle")
rTcircle doesn't exist, and thus your enter selection is never empty.
Related
Dragging of rectangles and graph work as intended in the given example. But I would like to bind the graph nodes to the rectangles underneath, so that moving a rectangle also moves the bound graph node and with it the graph link.
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = { "nodes": [{ "x": 170, "y": 120 }, { "x": 400, "y": 100 }], "links": [{ "source": 0, "target": 1 }] }
graph.links.forEach(function (d) {
d.source = graph.nodes[d.source];
d.target = graph.nodes[d.target];
});
var dataRects = [
{ "x": 20, "y": 50, "width": 200, "height": 100 },
{ "x": 350, "y": 75, "width": 150, "height": 50 }
]
var rects = svg.append("g")
.attr("id", function () { return "g_0" })
.attr("class", "rect")
.selectAll("rect")
.data(dataRects)
.enter()
.append("rect")
.attr("id", function (d, i) { return "box_" + i })
.attr("x", function (d) { return d.x; })
.attr("y", function (d) { return d.y; })
.attr("width", function (d) { return d.width })
.attr("height", function (d) { return d.height })
.call(d3.drag()
//.on("start", function(d) {}
.on("drag", function (d) {
const matrix = d3.select(this).node().transform.baseVal.consolidate().matrix;
const x = matrix.e + d3.event.dx;
const y = matrix.f + d3.event.dy;
d3.select(this).attr("transform", `translate(${x},${y})`);
})
.on("end", function (d) {
idOnEnd = d3.select(this).attr("id");
})
)
.attr("transform", "translate(0,0)");
var link = svg.append("g")
.attr("class", "link")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.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; });
var node = svg.append("g")
.attr("class", "node")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("id", function (d, i) { return "node_" + i; })
.attr("r", 9)
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.call(d3.drag().on("drag", dragged));
function dragged(d) {
d.x = d3.event.x, d.y = d3.event.y;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
link.filter(function (l) { return l.source === d; }).attr("x1", d.x).attr("y1", d.y);
link.filter(function (l) { return l.target === d; }).attr("x2", d.x).attr("y2", d.y);
}
// var nodeSelect = d3.select("#node_0")
// d3.select("#box_0").append(function(){return nodeSelect.node()});
// d3.select("#g_0").append(() => nodeSelect.node());
.svg {
border: 1px solid red;
}
.node {
stroke: #555;
fill: #F00;
stroke-width: 3px;
}
.link {
stroke: #555;
stroke-width: 3px;
}
.rect {
fill: lightgray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Tried various things that didn't work, among them making a rectangle or its group the node parent:
var nodeSelect = d3.select("#node_0")
d3.select("#box_0").append(function(){return nodeSelect.node()});
d3.select("#g_0").append(() => nodeSelect.node());
The group change is reflected in the DOM but doesn't produce the desired effect.
Wasn't lucky searching for pertinent posts.
The larger goal to create a flow chart, where links between rectangles are created by mouse clicks, and links are preserved and move along when rectangles are dragged around.
I am drawing circles with every update using a smooth transition which goes from one location to another.
Here is the code I am using...
function drawChart(newData)
{
var circles = slider.selectAll(".dot")
.data(newData);
circles.enter()
.append("circle")
.merge(circles)
.transition() // and apply changes to all of them
.duration(1000)
.ease(d3.easeLinear)
.attr("class", "dot")
.attr("r", 10.5)
.attr("cx", function(d,i) {
return Math.pow(d.open,i); })
.attr("cy", function(d,i) { return Math.pow(i,5)+d.close; })
.style("fill", function(d) { return color(d.class); });
circles.exit()
.remove();
}
This is how data is updated using the filterData function.
function filterData(dd){
var newData = dataset.filter(function(d) {
return d.date.getDate() == dd.getDate() && d.date.getMonth() == dd.getMonth();
})
drawChart(newData)
}
This code shows the simple circle and transition, whereas I want to have the transition in a way circles are leaving trails while moving as in this picture. .
Is there any way to do this? Any help would be appreciated.
I made your starting positions a little easier to mock, the true calculations are in the .tween function. Note that I execute the function only a few times, otherwise you get a continuous flow of circles.
You can often find solutions like this by looking at similar problems. In this case, I based it on this answer, which led me to tween.
var svg = d3.select('svg');
var color = (v) => v;
var nTrails = 20;
function createTraceBall(x, y) {
svg.append('circle')
.classed('shadow', true)
.attr('cx', x)
.attr('cy', y)
.attr('r', 10)
.style('fill', 'grey')
.style('opacity', 0.5)
.transition()
.duration(500)
.ease(d3.easeLinear)
.style('fill', 'lightgrey')
.style('opacity', 0.1)
.attr('r', 3)
.remove();
}
function drawChart(newData) {
var circles = svg.selectAll(".dot")
.data(newData);
circles.enter()
.append("circle")
.attr("cx", (d) => d.open.x)
.attr("cy", (d) => d.open.y)
.merge(circles)
.transition() // and apply changes to all of them
.duration(1000)
.ease(d3.easeLinear)
.tween("shadow", function(d) {
var xRange = d.close.x - d.open.x;
var yRange = d.close.y - d.open.y;
var nextT = 0;
return function(t) {
// t is in [0, 1), and we only want to execute it nTrails times
if(t > nextT) {
nextT += 1 / nTrails;
createTraceBall(
d.open.x + xRange * t,
d.open.y + yRange * t
);
}
};
})
.attr("class", "dot")
.attr("r", 10.5)
.attr("cx", (d) => d.close.x)
.attr("cy", (d) => d.close.y)
.style("fill", function(d) { return color(d.class); });
circles.exit()
.remove();
}
drawChart([
{open: {x: 20, y: 20}, close: {x: 150, y: 150}, class: 'red'},
{open: {x: 150, y: 20}, close: {x: 20, y: 150}, class: 'blue'},
{open: {x: 20, y: 20}, close: {x: 150, y: 20}, class: 'green'}
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>
I currently have a hover effect on each node, as well as a hover effect on each element of the legend.
I want to execute the effect when the user interacts with the legend so the relevant node on the chart is highlighted and visa versa. I also want to toggle the effect on and off.
So when the user clicks/hovers over a node the relevant legend item is highlighted in grey.
And when the user clicks/hovers over a legend item the relevant node is highlighted with a black stroke.
I have had a look at the following (Selectable Elements) but can't work out how to apply this to my code.
My understanding so far is that I need to apply a class (something like .selected) to the related element on mouseover & when toggled.
JSFiddle
//Nodes V2
var width = 300,
height = 300,
colors = d3.scale.category20b();
var force = d3.layout.force()
.gravity(.2)
.charge(-3000)
.size([width, height]);
//Svg Chart SVG Settings
var svg = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height);
var root = getData();
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
nodes.forEach(function(d, i) {
d.x = width/2 + i;
d.y = height/2 + 100 * d.depth;
});
root.fixed = true;
root.x = width / 2;
root.y = height / 2;
force.nodes(nodes)
.links(links)
.start();
var link = svg.selectAll("line")
.data(links)
.enter()
.insert("svg:line")
.attr("class", "link");
var node = svg.selectAll("circle.node")
.data(nodes)
.enter()
.append("svg:circle")
.attr("r", function(d) { return d.size/200; })
//.attr('fill', function(d) { return d.color; }) // Use Data colors
.attr('fill', function(d, i) { return colors(i); }) // Use D3 colors
.attr("class", "node")
.call(force.drag)
.on('click', function(){
d3.select( function (d){
return i.li;
})
.style('background', '#000')
})
//Adding an event - mouseover/mouseout
.on('mouseover', function(d) {
d3.select(this)
.transition()//Set transition
.style('stroke', '#222222')
.attr("r", function(d) { return (d.size/200) + 2; })
})
.on('mouseout', function(d) {
d3.select(this)
.transition()
.style('stroke', '#bfbfbf')
.attr("r", function(d) { return (d.size/200) - 2; })
d3.select('ul')
});
//Add a legend
var legend = d3.select('#key').append('div')
.append('ul')
.attr('class', 'legend')
.selectAll('ul')
.data(nodes)
.enter().append('li')
.style('background', '#ffffff')
.text(function(d) { return d.name; })
.on('mouseover', function(d) {
d3.select(this)
.transition().duration(200)//Set transition
.style('background', '#ededed')
})
.on('mouseout', function(d) {
d3.select(this)
.transition().duration(500)//Set transition
.style('background', '#ffffff')
})
.append('svg')
.attr('width', 10)
.attr('height', 10)
.style('float', 'right')
.style('margin-top', 4)
.append('circle')
.attr("r", 5)
.attr('cx', 5)
.attr('cy', 5)
.style('fill', function(d, i) { return colors(i); });
//Add Ticks
force.on("tick", function(e) {
link.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; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
//Center Node
var userCenter = d3.select("svg").append("svg:circle")
.attr('class', 'user')
.attr('r', 30)
.attr('cx', width/2)
.attr('cy', height/2)
.style('fill', '#bfbfbf')
var label = d3.select('svg').append("text")
.text('USER')
.attr('x', width/2)
.attr('y', height/2)
.attr('text-anchor','middle')
.attr('transform', 'translate(0, 5)')
.style('font-size','12px')
.attr('fill','#666666')
//Fix Root
function flatten(root) {
var nodes = [];
function recurse(node, depth) {
if (node.children) {
node.children.forEach(function(child) {
recurse(child, depth + 1);
});
}
node.depth = depth;
nodes.push(node);
}
recurse(root, 1);
return nodes;
}
//Data
function getData() {
return {
"name": "flare",
"size": 0,
"children": [
{ "name": "Jobs", "size": 3743 },
{ "name": "Contact", "size": 3302 },
{ "name": "Dietary", "size": 2903 },
{ "name": "Bookings", "size": 4823 },
{ "name": "Menu", "size": 3002 },
{ "name": "Cards", "size": 3120 },
{ "name": "Newsletter", "size": 3302 }
]
};
}
You should use the same event(mouseover, mouseout) functions for the nodes and legend items.
//Adding an event - mouseover/mouseout
.on('mouseover', onMouseover)
.on('mouseout', onMouseout);
...
//Legend
.on('mouseover', onMouseover)
.on('mouseout', onMouseout)
Then, you use the data passed to the functions to select the correct elements to change the style.
function onMouseover(elemData) {
d3.select("svg").selectAll("circle")
.select( function(d) { return d===elemData?this:null;})
.transition()//Set transition
.style('stroke', '#222222')
.attr("r", function(d) { return (d.size/200) + 2; })
d3.select('#key').selectAll('li')
.select( function(d) { return d===elemData?this:null;})
.transition().duration(200)//Set transition
.style('background', '#ededed')
}
function onMouseout(elemData) {
d3.select("svg").selectAll("circle")
.select( function(d) { return d===elemData?this:null;})
.transition()
.style('stroke', '#bfbfbf')
.attr("r", function(d) { return (d.size/200) - 2; })
d3.select('#key').selectAll('li')
.select( function(d) { return d===elemData?this:null;})
.transition().duration(500)//Set transition
.style('background', '#ffffff')
}
Here is the updated JSFiddle
I am currently developing a program to create a graph(vertexes + edges) and apply an algorithm. I chose 3d.js to make this program.
I am confronted a problem about the repaintfunction when I add a vertex.
When I add the first vertex, it's work but for the following, my vertexes multiply rapidly(vertex&label)...
Screenshots:
Source code:
var graph = (function() {
return {
modules: {},
nodes: [],
links: []
}
})();
graph.modules.engin = (function() {
var w, h, circleWidth, svgApp, force, link, node, nodes;
var palette = {
"white": "#FFFFFF",
"gray": "#708284",
"orange": "#BD3613",
"red": "#D11C24",
"blue": "#2176C7"
};
return {
init: function() {
var w = 960,
h = 450;
circleWidth = 7;
svgApp = d3.select("body")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("id", "svg")
.attr("pointer-events", "all")
.attr("viewBox", "0 0 " + w + " " + h)
.attr("perserveAspectRatio", "xMinYMid")
.append('svg:g');
force = d3.layout.force()
.nodes(graph.nodes)
.links([])
.gravity(0.1)
.charge(-1000)
.size([w, h]);
//console.log(nodes);
//nodes.push({})
graph.modules.engin.repaint();
graph.modules.engin.insertNode('V1');
graph.modules.engin.insertNode('V2');
graph.modules.engin.insertNode('V3');
},
repaint: function() {
console.log('update');
nodes = force.nodes();
var links = force.links();
link = svgApp.selectAll(".link")
.data(links);
link.enter().append("line")
.attr("class", "link")
.attr("stroke", palette.gray)
.attr("fill", "none");
link.exit().remove();
node = svgApp.selectAll("circle.node")
.data(nodes);
node.enter().append("g")
.attr("class", "node")
.on("mouseover", function(d, i) { //MOUSEOVER
if (!d.root) {
d3.select(this).selectAll("circle")
.transition()
.duration(250)
.style("cursor", "none")
.attr("r", circleWidth + 3)
.attr("fill", palette.orange);
d3.select(this).select("text")
.transition()
.style("cursor", "none")
.duration(250)
.style("cursor", "none")
.attr("font-size", "1.5em")
.attr("x", 15)
.attr("y", 5)
} else {
d3.select(this).selectAll("circle")
.style("cursor", "none");
d3.select(this).select("text")
.style("cursor", "none");
}
})
.on("mouseout", function(d, i) { //MOUSEOUT
if (!d.root) {
//CIRCLE
d3.select(this).selectAll("circle")
.transition()
.duration(250)
.attr("r", circleWidth)
.attr("fill", palette.blue);
//TEXT
d3.select(this).select("text")
.transition()
.duration(250)
.attr("font-size", "1em")
.attr("x", 8)
.attr("y", 4)
}
})
.call(force.drag);
node.append("svg:circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", circleWidth)
.attr("fill", function(d, i) {
if (!d.root) {
return palette.blue;
} else {
return palette.red
}
});
node.append("text")
.text(function(d, i) {
return d.name;
})
.attr("x", function(d, i) {
return circleWidth + 5;
})
.attr("y", function(d, i) {
if (!d.root) {
return circleWidth
} else {
return circleWidth + 5
}
})
.attr("font-family", "Bree Serif")
.attr("fill", function(d, i) {
return palette.white;
})
.attr("font-size", function(d, i) {
return "1em";
})
.attr("text-anchor", function(d, i) {
if (!d.root) {
return "beginning";
} else {
return "end"
}
});
node.exit().remove();
force.on("tick", function(e) {
node.attr("transform", function(d, i) {
return "translate(" + d.x + "," + d.y + ")";
});
link.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;
})
});
force.start();
},
insertNode: function(name) {
nodes.push({
name: name
})
graph.modules.engin.repaint();
}
}
})();
$(document).ready(function() {
graph.modules.engin.init();
});
html {
background-color: #042029;
}
<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/1.11.0/jquery.min.js"></script>
<button onclick="addNodes()">Restart Animation</button>
Thank you,
YM
I solved my problem: variable ref mistake and selector elements:
Source code:
var graph = (function() {
return {
modules: {},
nodes: [],
links: []
}
})();
graph.modules.engin = (function() {
var w, h, circleWidth, svgApp, force;
var palette = {
"white": "#FFFFFF",
"gray": "#708284",
"orange": "#BD3613",
"red": "#D11C24",
"blue": "#2176C7"
};
return {
init: function() {
h = $('body').height();
w = $('body').width();
circleWidth = 7;
svgApp = d3.select("body")
.append("svg:svg")
.attr("pointer-events", "all")
.attr("width", w)
.attr("height", h)
.attr("viewBox", "0 0 " + w + " " + h)
.attr("perserveAspectRatio", "xMinYMid");
force = d3.layout.force();
graph.nodes = force.nodes();
graph.links = force.links();
graph.modules.engin.repaint();
graph.modules.engin.insertNode('V1');
graph.modules.engin.insertNode('V2');
graph.modules.engin.insertNode('V3');
},
repaint: function() {
console.log('update');
var nodes = force.nodes();
var links = force.links();
console.log('BEFORE REPAINT');
console.log(nodes);
console.log(links);
console.log(graph.nodes);
console.log(graph.links);
var link = svgApp.selectAll("line.link")
.data(links);
var linkEnter = link.enter().append("line")
.attr("class", "link")
.attr("stroke", palette.gray)
.attr("fill", "none");
link.exit().remove();
var node = svgApp.selectAll("g.node")
.data(nodes, function(d) {
return d.name;
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("mouseover", function(d, i) {
if (!d.root) {
d3.select(this).select("circle")
.transition()
.duration(250)
.style("cursor", "none")
.attr("r", circleWidth + 3)
.attr("fill", palette.yellow);
d3.select(this) //.select("text")
.transition()
.style("cursor", "none")
.duration(250)
.style("cursor", "none")
.attr("font-size", "1.5em")
.attr("x", 15)
.attr("y", 5)
} else {
d3.select(this).select("circle")
.style("cursor", "none");
d3.select(this).select("text")
.style("cursor", "none");
}
})
.on("mouseout", function(d, i) {
if (!d.root) {
d3.select(this).select("circle")
.transition()
.duration(250)
.attr("r", circleWidth)
.attr("fill", palette.blue);
d3.select(this) //.select("text")
.transition()
.duration(250)
.attr("font-size", "1em")
.attr("x", 8)
.attr("y", 4)
}
})
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", circleWidth)
.attr("fill", function(d, i) {
if (!d.root) {
return palette.blue;
} else {
return palette.red
}
}).attr("id", function(d) {
return "Node-" + d.name;
});
nodeEnter.append("svg:text")
.text(function(d, i) {
return d.name;
})
.attr("x", function(d, i) {
return circleWidth + 5;
})
.attr("y", function(d, i) {
if (!d.root) {
return circleWidth - 2
} else {
return circleWidth + 5
}
})
.attr("font-family", "Bree Serif")
.attr("fill", function(d, i) {
return palette.white;
})
.attr("font-size", function(d, i) {
return "1em";
})
.attr("text-anchor", function(d, i) {
if (!d.root) {
return "beginning";
} else {
return "end"
}
});
node.exit().remove();
force.on("tick", function(e) {
node.attr("transform", function(d, i) {
return "translate(" + d.x + "," + d.y + ")";
});
link.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;
})
});
force.gravity(0.1)
.charge(-1000)
.size([w, h])
.start();
},
insertNode: function(name) {
graph.nodes.push({
name: name
})
graph.modules.engin.repaint();
}
}
})();
$(document).ready(function() {
graph.modules.engin.init();
});
html {
background-color: #042029;
}
body {
height: 600px;
width: 100%;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<button onclick="addNodes()">Restart Animation</button>
var svgcanvas = d3.select("body").append("svg:svg")
.attr("width", 725)
.attr("height", 500);
The Dataset
var jsoncirclehigh = [
{cx:100, cy: 100, r: 2.5,
label:"technology"},
{cx:200, cy: 200, r: 2.5,
label:"rocks"},
{cx:50, cy:50, r:2.5,
label:"blah"}
];
The actual shape that I have created
svgcanvas.append("svg:path")
.attr("d","M -200,0 A200,200 0 0,0 500,0 L -200,0")
.attr("transform", "translate(220,400) scale(1, -1)")
.style("stroke-width", 2)
.style("stroke", "steelblue")
.style("fill", "yellow");
I wish the circles to be restricted within the shape above
svgcanvas.selectAll("circle")
.data(jsoncirclehigh)
.enter().append("circle")
.attr("r", function (d) { return d.r; })
.attr("cx", function (d) { return d.cx; })
.attr("cy", function (d) { return d.cy; })
.on("mouseover", function(){d3.select(this).style("fill", "aliceblue");})
.on("mouseout", function() {d3.select(this).style("fill", "blue");})
.style("stroke", "steelblue")
.style("fill", "blue");
svgcanvas.selectAll("text")
.data(jsoncirclehigh)
.enter().append("svg:text")
.text(function(d) { return d.label; })
.attr("x", function (d) { return d.cx + 10; })
.attr("y", function (d) { return d.cy + 10; });
I have tried using d3.scale, but it hasn't worked out for me
This may be more simple than you're looking for, but have you looked into using a clipPath?