I am trying to modify a d3 animation example at http://bl.ocks.org/JMStewart/6455921
!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var line = d3.svg.diagonal();
var lineData = {source:{x:25, y:25},
target:{x:400, y:400}};
var path = svg.append("path")
.attr("d", line(lineData))
.style("stroke", "black")
.style("fill", "none");
svg.append("circle")
.attr("cx", 25) //Starting x
.attr("cy", 25) //Starting y
.attr("r", 25)
.transition()
.delay(250)
.duration(1000)
.ease("linear")
.tween("pathTween", function(){return pathTween(path)})
// .tween("pathTween", pathTween); //Custom tween to set the cx and cy attributes
function pathTween(path){
var length = path.node().getTotalLength(); // Get the length of the path
var r = d3.interpolate(0, length); //Set up interpolation from 0 to the path length
return function(t){
var point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3.select(this) // Select the circle
.attr("cx", point.x) // Set the cx
.attr("cy", point.y) // Set the cy
}
}
</script>
</body>
</html>
What I would like to do is to replace the circle in the animation with a valid SVG segment and animate that along the path. This is what my code looks like. I am able to append the svg image of a person but I cannot animate the same as the circle. What am I missing?
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var line = d3.svg.diagonal();
var lineData = {source:{x:25, y:25},
target:{x:400, y:400}};
var person = `
<g transform="scale(.4 .4)">
<g>
<path d="M40.84,0.14 C36.68,0.82 33.28,3.92 32.24,7.99 C30.99,12.84 33.29,17.87 37.77,20.09 C39.42,20.91 40.54,21.15 42.55,21.14 C43.94,21.13 44.28,21.1 45.12,20.87 C48.75,19.9 51.61,17.13 52.64,13.59 C53.39,10.99 53.15,8.39 51.95,5.95 C50.56,3.14 48.08,1.12 45.05,0.34 C43.82,0.03 42.04,-0.06 40.84,0.14 z" fill="#000000"/>
<path d="M37.65,23.45 C36.16,23.69 34.56,24.31 33.22,25.17 C32.48,25.63 11.89,42.32 11.2,43 C10.6,43.59 10.21,44.21 9.96,44.97 C9.74,45.67 5.22,66.05 5.14,66.7 C5.06,67.42 5.29,68.5 5.7,69.3 C6.57,71.02 8.63,72.08 10.53,71.79 C11.44,71.65 12.48,71.11 13.19,70.39 C14.1,69.48 14.41,68.76 14.96,66.3 C15.51,63.8 18.47,50.32 18.52,50.07 C18.54,49.94 25.9,43.87 25.97,43.93 C25.98,43.94 23.43,55.27 20.3,69.11 L14.62,94.27 L7.77,105.77 C4,112.1 0.8,117.54 0.67,117.86 C-0.24,120.04 -0.21,121.98 0.76,123.99 C1.14,124.77 1.36,125.07 2.08,125.8 C3.07,126.79 3.94,127.32 5.12,127.67 C7.62,128.39 10.09,127.8 12.01,126.03 C12.34,125.72 12.81,125.19 13.04,124.86 C13.85,123.72 28.21,99.4 28.8,98.17 L29.39,96.95 L31.17,88.72 C32.14,84.2 32.97,80.51 33.01,80.52 C33.13,80.58 48.3,97.21 48.3,97.29 C48.3,97.45 52.89,121.54 53.06,122.25 C53.64,124.78 55.56,126.91 57.94,127.66 C61.24,128.7 64.9,127.16 66.46,124.08 C67.17,122.68 67.39,120.84 67.06,119.09 C66.98,118.68 66.61,116.76 66.25,114.84 C65.89,112.92 65.1,108.76 64.5,105.6 C63.9,102.44 63.11,98.27 62.75,96.35 C62.1,92.9 61.8,91.73 61.41,91.09 C61.3,90.91 56.99,85.77 51.83,79.67 C46.67,73.58 42.46,68.53 42.47,68.47 C42.55,68.03 47.22,47.54 47.25,47.51 C47.27,47.49 48.21,49.32 49.33,51.57 C50.91,54.72 51.48,55.77 51.84,56.17 C52.41,56.8 67.96,67.56 68.95,68.01 C70.84,68.86 72.94,68.47 74.34,67.01 C76.07,65.19 76.13,62.38 74.47,60.54 C74.16,60.19 71.78,58.5 66.62,54.94 L59.22,49.85 L57.77,46.95 C51.34,34.17 48.46,28.54 48.11,28.07 C45.54,24.58 41.55,22.82 37.65,23.45 z" fill="#000000"/>
</g>
</g>`
var path = svg.append("path")
.attr("d", line(lineData))
.style("stroke", "black")
.style("fill", "none");
svg.append('g').attr('id','person').html(person)
.attr("cx", 25) //Starting x
.attr("cy", 25) //Starting y
.attr("r", 25)
.transition()
.delay(250)
.duration(1000)
.ease("linear")
.tween("pathTween", function(){return pathTween(path)})
// .tween("pathTween", pathTween); //Custom tween to set the cx and cy attributes
function pathTween(path){
var length = path.node().getTotalLength(); // Get the length of the path
var r = d3.interpolate(0, length); //Set up interpolation from 0 to the path length
return function(t){
var point = path.node().getPointAtLength(r(t)); // Get the next point along the path
d3.select(this) // Select the circle
.attr("cx", point.x) // Set the cx
.attr("cy", point.y) // Set the cy
}
}
</script>
</body>
</html>
cx and cy are attributes valid for the circle only. To animate a group, use a transform attribute with the translate(x,y) value. More about transform here: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/transform
You could define your person with a <svg> element - they can be nested, and you can use x and y attributes for positioning:
var person = `
<g transform="scale(.4 .4)">
<path d="M40.84,0.14 C36.68,0.82 33.28,3.92 32.24,7.99 C30.99,12.84 33.29,17.87 37.77,20.09 C39.42,20.91 40.54,21.15 42.55,21.14 C43.94,21.13 44.28,21.1 45.12,20.87 C48.75,19.9 51.61,17.13 52.64,13.59 C53.39,10.99 53.15,8.39 51.95,5.95 C50.56,3.14 48.08,1.12 45.05,0.34 C43.82,0.03 42.04,-0.06 40.84,0.14 z" fill="#000000"/>
<path d="M37.65,23.45 C36.16,23.69 34.56,24.31 33.22,25.17 C32.48,25.63 11.89,42.32 11.2,43 C10.6,43.59 10.21,44.21 9.96,44.97 C9.74,45.67 5.22,66.05 5.14,66.7 C5.06,67.42 5.29,68.5 5.7,69.3 C6.57,71.02 8.63,72.08 10.53,71.79 C11.44,71.65 12.48,71.11 13.19,70.39 C14.1,69.48 14.41,68.76 14.96,66.3 C15.51,63.8 18.47,50.32 18.52,50.07 C18.54,49.94 25.9,43.87 25.97,43.93 C25.98,43.94 23.43,55.27 20.3,69.11 L14.62,94.27 L7.77,105.77 C4,112.1 0.8,117.54 0.67,117.86 C-0.24,120.04 -0.21,121.98 0.76,123.99 C1.14,124.77 1.36,125.07 2.08,125.8 C3.07,126.79 3.94,127.32 5.12,127.67 C7.62,128.39 10.09,127.8 12.01,126.03 C12.34,125.72 12.81,125.19 13.04,124.86 C13.85,123.72 28.21,99.4 28.8,98.17 L29.39,96.95 L31.17,88.72 C32.14,84.2 32.97,80.51 33.01,80.52 C33.13,80.58 48.3,97.21 48.3,97.29 C48.3,97.45 52.89,121.54 53.06,122.25 C53.64,124.78 55.56,126.91 57.94,127.66 C61.24,128.7 64.9,127.16 66.46,124.08 C67.17,122.68 67.39,120.84 67.06,119.09 C66.98,118.68 66.61,116.76 66.25,114.84 C65.89,112.92 65.1,108.76 64.5,105.6 C63.9,102.44 63.11,98.27 62.75,96.35 C62.1,92.9 61.8,91.73 61.41,91.09 C61.3,90.91 56.99,85.77 51.83,79.67 C46.67,73.58 42.46,68.53 42.47,68.47 C42.55,68.03 47.22,47.54 47.25,47.51 C47.27,47.49 48.21,49.32 49.33,51.57 C50.91,54.72 51.48,55.77 51.84,56.17 C52.41,56.8 67.96,67.56 68.95,68.01 C70.84,68.86 72.94,68.47 74.34,67.01 C76.07,65.19 76.13,62.38 74.47,60.54 C74.16,60.19 71.78,58.5 66.62,54.94 L59.22,49.85 L57.77,46.95 C51.34,34.17 48.46,28.54 48.11,28.07 C45.54,24.58 41.55,22.82 37.65,23.45 z" fill="#000000"/>
</g>`
....
svg.append('svg').attr('id','person').html(person)
.attr("x", 25) //Starting x, left side
.attr("y", 25) //Starting y, top side
.transition()
....
I'm trying to draw pie charts in Meteor, but I'm very new to both d3 and Meteor and am not really understanding what is going on.
The following d3 code to draw pie charts from a csv file works for me outside of Meteor:
<!DOCTYPE html>
<meta charset="utf-8">
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<style>
body {
font: 30px "Montserrat";
text-transform:uppercase;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 150,
padding = 10;
var color = d3.scale.ordinal()
.range(["#f65c55","#c8e7ec"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 40);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Criteria"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 50 + ")"; });
legend.append("rect")
.attr("width", 40)
.attr("height", 40)
.style("fill", color);
legend.append("text")
.attr("x", 50)
.attr("y", 20)
.attr("dy", ".35em")
.attr("font-size","20px")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.Criteria; });
});
</script>
I also have a Meteor template as follows that I want to draw these charts in:
<div class="tab-pane active" id="playback">
{{> playback}}
</div>
However, when I try and follow web tutorials to integrate the two, the graphs don't get drawn. Can anyone help me understand why? Thanks in advance!
EDIT: forgot to mention, data.csv looks like this:
Criteria,Disapproval, Approval
Too Fast,1,2
Too Slow,5,6
Clarity,2,3
Legibility,202070,343207
The first line is for the legend, and the rest are for 4 separate graphs.
You have to make sure that the template is rendered before you access the DOM elements by code. So put your D3 code inside a template rendered method, like this:
Template.playback.rendered = function() {
// your D3 code
}
or on the body tag e.g.:
UI.body.rendered = function() {
// your D3 code
}
Template.chart.rendered = function(){
Deps.autorun(function () {
//d3 codeing here!!
}
}
It's working for me. If you're coding without Deps.autorun() it's will not render.
Oh!! one morething at html page in you case maybe .
However for my case I using nvd3.org http://nvd3.org/livecode/index.html#codemirrorNav. And this I hope you will clearify.
Been away from d3.js for a few months... and I've inherited a simple US map with features that someone else started.
The features are represented by simple dots of varying sizes.
I want to add emanating concentric circles to each dot, similar to the classic Onion example by Mike Bostock: http://bl.ocks.org/mbostock/4503672 (maybe not so ominous looking though)
I've got a block set up here: http://bl.ocks.org/mbostock/4503672
(Not sure why the states aren't rendering correctly in the block, but it probably doesn't matter for this.)
In Mike's example there is only one dot, so I'm have trouble understanding how to translate what he did to what I've got (many dots).
Here's my script:
/**
* Page initialization
*/
$(function() {
renderMap('#map-container');
});
function renderMap(container) {
var width = 960,
height = 500,
active;
var projection = d3.geo.albersUsa()
.scale(960)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var radius = d3.scale.sqrt()
.domain([0, 1e7])
.range([0, 10]);
var path2 = d3.geo.path()
.projection(projection);
// Remove svg, if already exist
d3.select(container).select('svg').remove();
var svg = d3.select(container).append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height);
//.on("click", reset);
var g = svg.append("g");
queue()
.defer(d3.json, "/mbostock/raw/4090846/us.json")
.defer(d3.json, "dots.json")
.await(function (error, us, centroid) {
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", "state");
//.on('click', click);
g.append('path')
.attr("id", "state-borders")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "mesh");
var dots = g.append("g")
.attr("id", "dots")
.selectAll("path")
.data(centroid.data)
.enter().append("path")
.attr("class", "dot")
.attr("d", path2.pointRadius(function(d) { return radius(d.properties.pool); }));
}
);
}
and the key part of Mike's example for making the rings is:
setInterval(function() {
svg.append("circle")
.attr("class", "ring")
.attr("transform", "translate(" + projection([100, -8]) + ")")
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750);
how do I get the rings positioned on the dots?
Review the differences between the two methods to learn a little bit more about how functional/declarative programming abstracts away the pain of iterative programming.
Approach with D3 idioms:
fiddle: http://jsfiddle.net/blakedietz/E66eT/1/
update: D3 Way
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
svg
.selectAll("ring")
.data(data)
.enter()
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle)
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>
So I didn't create a fully d3 idiomatic approach for this solution, but it will work. If you can get this to work implicitly within a svg.selectAll("circle"/"unique selection"...), etc, that would be even more awesome. I'll work on that in the mean time. Until then there's this more explicitly iterative approach.
With Mike's example you're only appending a single element to the D.O.M. in the setInterval call. In order to expedite the binding process, I've created a projection method which operates on a set of coordinates: the translateCircle will operate on a datum within a collection of coordinates allowing access to the internal attributes of each collection element.
Within each setInterval call the forEach method iterates over the collection of coordinates and then calls the same internals within the setInterval method that was called by Mike originally.
Not So D3
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
data.forEach(function(datum)
{
svg
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle(datum))
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
})
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>