d3 radial wipe animation clips one element - javascript

The intent of this code is to click on a pie slice and have all of slices animated in a shrinking and expanding fashion. When I run the following code for a single element, they all work. When I try to animate all arcs at the same time, the smallest one disappears. Fiddle http://jsfiddle.net/fk03xyap/
var data = [50,20,30];
var svgConfig = {
height: 1000,
width: 1000,
id: 'mySvg',
transform: 'translate(500,300)'
};
var colors = ['blue','gray','green'];
var pie = d3.layout.pie().value(function(d) { return d;});
var arcGen = d3.svg.arc()
.outerRadius(100)
.innerRadius(10);
var svg = d3.select('body').append('svg').attr(svgConfig);
var g = svg.selectAll('.arc')
.data(pie(data))
.enter()
.append('g')
.attr('transform','translate(100,200)')
.attr('class','arc');
var paths = g.append('path').attr('d',arcGen).style('fill',function(d,i){
return colors[i];
});
d3.selectAll('path').on('click', function () {
var el = d3.select(this);
var g = d3.select('g');
var paths = d3.selectAll('path');
var endAngle,startAngle,orig;
paths.transition().duration(250).attrTween('d',function(d){
orig = d;
console.log('orig',orig);
endAngle = d.endAngle;
startAngle = d.startAngle;
console.log('first');
var interpolate = d3.interpolate(d.endAngle, d.startAngle +.01);
return function(t){
d.endAngle = interpolate(t);
return arcGen(d);
};
}).transition().duration(755).attrTween('d',function(r){
console.log('orig new',orig);
var interpolate = d3.interpolate(r.startAngle,endAngle);
return function(t){
r.endAngle = interpolate(t);
return arcGen(r);
};
});
});

You are overwriting the "original" end angle. I would stash it in the d element for later use. Cleaning this up, the code simplifies to:
d3.selectAll('path').on('click', function () {
paths.transition().duration(250).attrTween('d',function(d){
d.origEnd = d.endAngle;
var interpolate = d3.interpolate(d.endAngle, d.startAngle +.01);
return function(t){
d.endAngle = interpolate(t);
return arcGen(d);
};
}).transition().duration(755).attrTween('d',function(r){
var interpolate = d3.interpolate(r.startAngle,r.origEnd);
return function(t){
r.endAngle = interpolate(t);
return arcGen(r);
};
});
});
Updated example.

Related

I cannot enlarge my map with D3.js and always get a black block

I try to enlarge my map with geoProjection or geoMercator. The mapsvg is hidden befind a black block, and my map cannot be seen. My code is as follows:
fetch("./data/chinaMapDataAll.json")
.then(d1 => d1.json())
.then(d1 => {
var mapdata = [];
var mapsvg = d3.select("#mapsvg")
var mapwidth = mapsvg.attr("width")
var mapheight = mapsvg.attr("height")
for(let i=0;i<=d1.features.length-1;i++){
mapdata.push(d1.features[i].geometry.coordinates[0][0]);
}
console.log(mapdata);
var mapProjection=d3.geoProjection(function(x,y){
return [x,y];
});
var mapPath = d3.geoPath(mapProjection);
mapsvg.selectAll("path")
.data(d1.features)
.join("path")
.attr("d", function(d) { return mapPath(d) })
.attr("fill",function(d,i) { return scacolor[i]; })

dc.js - Rendering two objects (one chart - renders, one shape - doesn't) together in one group?

I have two elements I need to render and a context of the big picture I am trying to achieve (a complete dashboard).
One is a chart that renders fine.
$scope.riskChart = new dc.pieChart('#risk-chart');
$scope.riskChart
.width(width)
.height(height)
.radius(Math.round(height/2.0))
.innerRadius(Math.round(height/4.0))
.dimension($scope.quarter)
.group($scope.quarterGroup)
.transitionDuration(250);
The other is a triangle, to be used for a more complex shape
$scope.openChart = d3.select("#risk-chart svg g")
.enter()
.attr("width", 55)
.attr("height", 55)
.append('path')
.attr("d", d3.symbol('triangle-up'))
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ")"; })
.style("fill", fill);
On invocation of render functions, the dc.js render function is recognized and the chart is seen, but the d3.js render() function is not recognized.
How do I add this shape to my dc.js canvas (an svg element).
$scope.riskChart.render(); <--------------Works!
$scope.openChart.render(); <--------------Doesn't work (d3.js)!
How do I make this work?
EDIT:
I modified dc.js to include my custom chart, it is a work in progress.
dc.starChart = function(parent, fill) {
var _chart = {};
var _count = null, _category = null;
var _width, _height;
var _root = null, _svg = null, _g = null;
var _region;
var _minHeight = 20;
var _dispatch = d3.dispatch('jump');
_chart.count = function(count) {
if(!arguments.length)
return _count;
_count = count;
return _chart;
};
_chart.category = function(category) {
if(!arguments.length)
return _category
_category = category;
return _chart;
};
function count() {
return _count;
}
function category() {
return _category;
}
function y(height) {
return isNaN(height) ? 3 : _y(0) - _y(height);
}
_chart.redraw = function(fill) {
var color = fill;
var triangle = d3.symbol('triangle-up');
this._g.attr("width", 55)
.attr("height", 55)
.append('path')
.attr("d", triangle)
.attr("transform", function(d) { return "translate(" + 25 + "," + 25 + ")"; })
.style("fill", fill);
return _chart;
};
_chart.render = function() {
_g = _svg
.append('g');
_svg.on('click', function() {
if(_x)
_dispatch.jump(_x.invert(d3.mouse(this)[0]));
});
if (_root.select('svg'))
_chart.redraw();
else{
resetSvg();
generateSvg();
}
return _chart;
};
_chart.on = function(event, callback) {
_dispatch.on(event, callback);
return _chart;
};
_chart.width = function(w) {
if(!arguments.length)
return this._width;
this._width = w;
return _chart;
};
_chart.height = function(h) {
if(!arguments.length)
return this._height;
this._height = h;
return _chart;
};
_chart.select = function(s) {
return this._root.select(s);
};
_chart.selectAll = function(s) {
return this._root.selectAll(s);
};
function resetSvg() {
if (_root.select('svg'))
_chart.select('svg').remove();
generateSvg();
}
function generateSvg() {
this._svg = _root.append('svg')
.attr({width: _chart.width(),
height: _chart.height()});
}
_root = d3.select(parent);
return _chart;
}
I think I confused matters by talking about how to create a new chart, when really you just want to add a symbol to an existing chart.
In order to add things to an existing chart, the easiest thing to do is put an event handler on its pretransition or renderlet event. The pretransition event fires immediately once a chart is rendered or redrawn; the renderlet event fires after its animated transitions are complete.
Adapting your code to D3v4/5 and sticking it in a pretransition handler might look like this:
yearRingChart.on('pretransition', chart => {
let tri = chart.select('svg g') // 1
.selectAll('path.triangle') // 2
.data([0]); // 1
tri = tri.enter()
.append('path')
.attr('class', 'triangle')
.merge(tri);
tri
.attr("d", d3.symbol().type(d3.symbolTriangle).size(200))
.style("fill", 'darkgreen'); // 5
})
Some notes:
Use chart.select to select items within the chart. It's no different from using D3 directly, but it's a little safer. We select the containing <g> here, which is where we want to add the triangle.
Whether or not the triangle is already there, select it.
.data([0]) is a trick to add an element once, only if it doesn't exist - any array of size 1 will do
If there is no triangle, append one and merge it into the selection. Now tri will contain exactly one old or new triangle.
Define any attributes on the triangle, here using d3.symbol to define a triangle of area 200.
Example fiddle.
Because the triangle is not bound to any data array, .enter() should not be called.
Try this way:
$scope.openChart = d3.select("#risk-chart svg g")
.attr("width", 55)
.attr("height", 55)
.append('path')
.attr("d", d3.symbol('triangle-up'))
.attr("transform", function(d) { return "translate(" + 100 + "," + 100 + ")"; })
.style("fill", fill);

D3 v4 Animation: How to loop over multiple circles over multiple paths?

Inspired by https://www.nytimes.com/interactive/2018/03/19/upshot/race-class-white-and-black-men.html
I am trying to create a animation that will have markers move from one point to multiple levels in another point in the y axis. Thanks to detailed pages by Amelia and Mike Bostock in both bl.ocks.org and stackoverflow. I have got so far to get the circles and animate it. But, I am not able to make each marker loop over by the pathlevel and do the transition
Pathlevel, here, indicates whether they are high, middle or low (1,2,3).
The entire code in using d3.v4 has been pasted below. what I am missing?
Thanks for your help.
<!DOCTYPE html>
<meta charset="utf-8">
<title>SANKEY Experiment</title>
<style>
</style>
<body>
<script type="text/javascript" src="d3.v4.js"></script>
<script>
//ref: very important for path animation: https://stackoverflow.com/questions/21226523/trace-path-with-dom-object/21228701#21228701
//ref: clustered force layout: https://bl.ocks.org/mbostock/7881887
//ref: data manipulation: http://learnjsdata.com/iterate_data.html
var margin = {top: 30, right: 10, bottom: 30, left: 20},
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var series = [{x:5,y:10},{x:150,y:10}],
series2 = [{x:5,y:10},{x:50,y:15},{x:100,y:30},{x:150,y:30}],
series3 = [{x:5,y:10},{x:50,y:22},{x:100,y:50},{x:150,y:50}];
var data = [{"dim":"a","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
{"dim":"b","pos":"high","pathlevel":1,"x1":1,"y1":10,"x2":150,"y2":8},
{"dim":"a","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
{"dim":"b","pos":"mid","pathlevel":2,"x1":1,"y1":10,"x2":150,"y2":28},
{"dim":"a","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48},
{"dim":"b","pos":"low","pathlevel":3,"x1":1,"y1":10,"x2":150,"y2":48}]
var x = d3.scaleLinear()
.domain([5,150])
.range([0,width]);
var y = d3.scaleLinear()
.domain([10,50])
.range([0,height]);
var line = d3.line()
.curve(d3.curveCardinal)
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y); });
var svg = d3.select("body").append("svg").attr("height",600).attr("width",600);
var chart = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");
var update = function(series,k){
chart.append("path")
.attr("class","route"+k)
.attr("fill","none")
.attr("stroke","blue")
.attr("d",line(series));
}
update(series,1);
update(series2,2);
update(series3,3);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//create transistions along the path //////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var colorScale = d3.scaleOrdinal()
.domain(['a','b'])
.range(['orange','darkblue']);
//Get path start point for placing marker
var path = d3.select('.route1')
var startPoint = path.attr("d").split(",")[1];
//path example["M12.885906040268456", "84.48979591836735C12.885906040268456", "84.48979591836735", "241.07382550335572", "84.48979591836735",
//"318.9261744966443", "84.48979591836735C396.7785234899329", "84.48979591836735", "480", "84.48979591836735", "480", "84.48979591836735"]
//selecting class route which represents the path. d represents the path that is held by the path object. in that we split by comma and take the first
console.log(startPoint);
var glider = function(data,p){//p for path level
var marker = chart.selectAll(".marker")
.data(data)
.enter().append('circle')
.attr("class","marker")
.attr("fill",function(d){ return colorScale(d.dim);})
//.attr("x",function(d,i){return x(d.x1)+i*10;})
//.attr("y",function(d,i){return y(d.y1)+i*10;})
.attr("r",5);
//.attr("width",10)
//.attr("height",10);
var simulation = d3.forceSimulation()
.force("x",d3.forceX().strength(0.05))
.force("y",d3.forceY().strength(0.01))
.force('charge', d3.forceManyBody().strength(20))
.force("collide",d3.forceCollide(function(d){return y(d.y1)+4;}))
.alphaTarget(.03)
.restart();
simulation.nodes(data)
.on('tick',ticked);
function ticked(){
marker
.attr("cx",function(d){ return d.x;})
.attr("cy",function(d){ return d.y;})
}//end of ticked
//marker.transition().duration(3000).delay(200)
// .attr("x",function(d,i){return x(d.x2)+i*10;});
function translateAlong(path) {
var l = path.getTotalLength();
return function (d) {
return function (t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";//Move marker
}
}
}//end of translateAlong
console.log(marker);
function transition(){
var path2 = d3.select('.route'+p);
marker.attr("transform", "translate(" + startPoint + ")").transition().duration(3000).delay(function(d,i) { return i * 100; })
.attrTween("transform",translateAlong(path2.node()));
//.attr("x",function(d,i){return x(d.x2)+i*10;});
}//end of transition
transition();
}
/*var check = d3.map(data, function(d){return d.pathlevel;}).keys(); //for getting unique values from a column
check.forEach(function(i){
datapoints = data.filter(function(d){return d.pathlevel==i});
console.log(i);
glider(datapoints,i);
});*/
data1 = data.filter(function(d){return d.pathlevel==1});
data2 = data.filter(function(d){return d.pathlevel==2});
data3 = data.filter(function(d){return d.pathlevel==3});
//glider(data1,1);
//glider(data2,2);
glider(data3,3);
//glider(data,2);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*function createPathTween(d, i, a) {
var path = this.parentNode.getElementsByTagName("path")[1];
//i.e., go from this <circle> -> parent <g> -> array of child <path> elements
//-> first (and only) element in that array
//console.log(path);
var l = path.getTotalLength();
return function(t) {
var p = path.getPointAtLength(t * l);
console.log(p);
return "translate(" + p.x + "," + p.y + ")";
};
}//end of tweenpath*/
</script>
</body>

Custom animation in plottable.js line chart

I am trying to use custom animation in plottable.js when data updates.
Below is my code : -
<script type="text/javascript">
var xScale = new Plottable.Scales.Category();
var yScale = new Plottable.Scales.Linear().domain([0,30]);
var xAxis = new Plottable.Axes.Category(xScale, "bottom");
var yAxis = new Plottable.Axes.Numeric(yScale, "left");
var dataset;
var data;
function createChart() {
data = [];
for(var i=0; i<10; i++) {
data.push({x:"Data" + (i + 1),y:Math.abs(Math.random() * 10)});
}
dataset = new Plottable.Dataset(data);
makeChart();
}
function updateChart() {
data = [];
for(var i=0; i<10; i++) {
data.push({x:"Data" + (i + 1),y:Math.abs(Math.random() * 10)});
}
dataset.data(data);
}
function makeChart() {
var linePlot = new Plottable.Plots.Line()
.addDataset(dataset)
.x(function(d) { return d.x; }, xScale)
.y(function(d) { return d.y; }, yScale)
.attr("stroke","#FA8116")
.animated(true)
.animator("test",new Plottable.Animators.Easing().easingMode("bounce"));
var label_y = new Plottable.Components.AxisLabel("Parameter 2", -90);
var label_x = new Plottable.Components.AxisLabel("Parameter 1", 0);
var chart = new Plottable.Components.Table([
[label_y, yAxis, linePlot],
[null, null, xAxis],
[null, null, label_x]
]);
chart.renderTo("svg#lineChart");
// Responsive Layout
window.addEventListener("resize", function() {
chart.redraw();
});
}
$(document).ready(function() {
createChart();
setInterval(function(d) {
updateChart();
},5000);
});
</script>
I want to animate lineplot other than default and I did this :-
var linePlot = new Plottable.Plots.Line()
.addDataset(dataset)
.x(function(d) { return d.x; }, xScale)
.y(function(d) { return d.y; }, yScale)
.attr("stroke","#FA8116")
.animated(true)
.animator("test",new Plottable.Animators.Easing().easingMode("bounce"));
I don`t understand where I am in correct and since I am new to plottable can you guys help me out, also is there a way to use d3 based animation with plottable ?? If yes can you provide a code snippet
Thanx in advance
Plots normally have two Animators: MAIN and RESET. You need to specify that you want to change the primary Animator on the Plot:
plot.animator(Plottable.Plots.Animator.MAIN,
new Plottable.Animators.Easing().easingMode("bounce"));

No map rendered in D3

I am trying to follow the Choropleth example by Scott Murray (shown in his text book)using a timezone geojson file. The problem is my map ain't getting rendered and the entire SVG is getting filled with green color. My code is below, and I would like to know if I'm doing anything wrong in rendering the map. I fill colors in the map using the values under each timezone.(shown in the picture below).
<script>
var width = 1000;
var height = 600;
var d = [];
var projection = d3.geo.mercator()
.translate([width/2, height/2])
.scale([500]);
var path = d3.geo.path().projection(projection);
var color = d3.scale.ordinal().range(colorbrewer.YlGn[9]);
var svg = d3.select("#viz").append("svg")
.attr("width",width)
.attr("height",height);
d3.json('scm-timezone.json', function(data){
var com = data.commits;
d.push(com);
color.domain([
d3.min(d[0]),
d3.max(d[0])
]);
d3.json("world.json", function(json){
for(var i = 0;i < data.tz.length;i++){
var dataTZ = data.tz[i];
var commitValue = parseInt(data.commits[i]);
for(var j = 0;j<json.features.length;j++){
var jsonTZ = json.features[j].properties.description;
if(dataTZ == jsonTZ) {
json.features[j].properties.value = commitValue;
}
}
}
svg.selectAll("path")
.data(json.features)
.enter()
.insert("path")
.attr("d",path)
.style("fill", function(d) {
var val = d.properties.value;
if(val){ return color(val); }
else { return "white"; }
});
});
});
</script>
And my world.json finally looks like this-

Categories

Resources