xxxxxxxxxx
<meta charset="utf-8">
<style>
#tooltip {
position: absolute;
text-align: center;
width: 280px;
height: 80px;
padding: 2px;
border-radius: 2px;
background: #fff;
border: 1px solid #ddd;
pointer-events: none;
border-radius: 4px;
}
#tooltip:after, #tooltip:before {
top: 100%;
left: 50%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
#tooltip:before {
border-color: rgba(221,221,221,0);
border-top-color: #ddd;
border-width: 6px;
margin-left: -6px;
}
#tooltip:after {
border-color: rgba(255,255,255,0);
border-top-color: #fff;
border-width: 5px;
margin-left: -5px;
}
#tooltip #tooltip-content {
padding: 3px 0;
font-size: 80%;
display: table;
text-align: center;
margin: 0 auto;
}
.node {
color: white;
border: solid 1px white;
text-align: right;
vertical-align: top;
position: absolute;
overflow: hidden;
cursor: pointer;
}
.item {
opacity: 0;
margin: 0 auto;
font-size: 10px;
font-family: "YuGothic";
font-weight: bold;
/*writing-mode: vertical-lr;*/
/*text-indent: 2px;*/
transform: translate(6px,4px) rotate(-45deg);
letter-spacing: -0.05em;
line-height: 1em;
fill: #888;
}
text {
font-weight: bold;
font-size: 12px;
/*pointer-events: none;*/
}
.node--hover circle {
stroke: red;
}
.node--hover .item {
fill: #800;
}
.node--leaf.node--hover circle {
fill: #800;
}
.selecting circle {
fill: #800;
fill-opacity: 1.0;
}
.selecting .item {
opacity: 1;
}
.legend-item-g {
cursor: pointer;
}
.legend-item-g text {
fill: #888;
}
.legend-item-g.node--hover text {
fill: #000;
}
</style>
<body>
<form>
<label><input type="radio" name="mode" value="amount" checked>寄附単価</label>
<label><input type="radio" name="mode" value="count">寄附件数</label>
<label><input type="radio" name="mode" value="totalAmount">寄附総額</label>
</form>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.2.7/d3.min.js"></script>
<script src="//d3js.org/d3-selection-multi.v1.min.js"></script>
<script src="//d3js.org/d3-ease.v1.min.js"></script>
<!-- <script src="lib/d3.min.js"></script>
<script src="lib/d3-selection-multi.v1.min.js"></script> -->
<script>
var csvpath = 'gifts.csv';
var totalItem = '総計';
var mode = d3.select('input').attr('value');//'amount';
var tooltip = d3.select("body").append("div").attr("id", "tooltip").style("opacity", 0);
d3.selectAll("input").on("change", change);
function change() {
mode = this.value;
// renderTable();
updateTable();
}
var margin = {top: 75, right: 75, bottom: 75, left: 75},
width = 1024 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// var svg = d3.select('body').append('svg')
// .styles({
// width: width + 'px',
// height: height + 'px'
// });
// svg.append('g')
// .attr('transform', 'translate(1,1)');
var format = d3.format(",d");
var formatAmount = function(num) {
var keta = ['', '万', '億'];
var nums = String(num).replace(/(\d)(?=(\d\d\d\d)+$)/g, "$1,").split(",").reverse();
var data = '';
for (var i=0;i<nums.length;i++){
if(!nums[i].match(/^[0]+$/)){
data = nums[i].replace(/^[0]+/g, "") + keta[i] + data;
}
}
return data;
};
var countFormat = function(d) {
var unit = '';
switch (mode) {
case 'count':
unit = '件';
break;
case 'amount':
case 'totalAmount':
unit = '円';
break;
default:
break;
}
return d3.format(",d")(d) + unit;
};
var pack = d3.pack()
.size([width, height])
.padding(3);
var data;
var xScaleOfRow = function(d, row, value) {
xScale.domain(d3.range(d[i].values.length));
return xScale(value);
}
var xScale = d3.scalePoint()
.padding(0.5)
.range([0, width])
.round(true);
var yScale = d3.scalePoint()
.padding(1.5)
.range([0, height])
.round(true);
var rScale = d3.scaleSqrt()
.range([0, 75]);
function updateTable() {
rScale.domain([0, d3.max(data, function(d) { return d[mode]; })]);
// d3.selectAll('.row-g').selectAll('.node')
// .transition()
// .duration(1000)
// .ease(d3.easeBounce)
// .attrs({
// transform: function(d,i){
// var r = rScale(d[mode]);
// // yScale.domain(d3.range(r))
// yScale.domain(d3.range(rScale.range()[1])+1)
// .range([0,-75]);
// var posScale = d3.scaleLinear()
// .domain([0, d3.max(data, function(d) { return d[mode]; })])
// .range([100, -100]);
// // return 'translate('+ xScale(i) +','+ posScale(d[mode]) +')';
// // return 'translate('+ xScale(i) +','+ yScale(r) +')';
// // return 'translate('+ xScale(i) +',0)';
// },
// });
d3.selectAll('.node circle')
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attrs({
r: function(d){return rScale(d[mode]);},
});
d3.selectAll('.node .item')
.transition()
.duration(1000)
.ease(d3.easeBounce)
.attrs({
x: function(d){return rScale(d[mode]);},
})
.text(function(d){return format(d[mode]);});
}
function renderTable() {
var nestedData = d3.nest()
.key(function(d){return d.amount;}).sortKeys(function(a,b){return b-a;})
// .key(function(d){return d.count;}).sortKeys(function(a,b){return b-a;})
.sortValues(function (a, b) { return d3.descending(a.count, b.count); })
.entries(data);
console.log(nestedData);
var rAmountScale = d3.scaleSqrt()
// .domain([0, d3.max(data, function(d) { return d.amount; })])
.domain([0, 1300000])
.range([1, 50]);
var rTotalScale = d3.scaleSqrt()
// .domain([0, d3.max(data, function(d) { return d.totalAmount; })])
.domain([0, 241620000])
.range([1, 50]);
var g = svg.select('g').selectAll('g')
.data(nestedData);
xScale.domain(d3.range(d3.max(nestedData, function(d){return d.values.length;})));
console.log(xScale.domain());
yScale.domain(nestedData.map(function(d){return d.key;}));
var rowg = g.enter()
.append('g')
.attr('class', 'row-g')
.attr("transform", function(d,i) { return "translate(" + (i%2)* 100 + "," + yScale(d.key) + ")"; })
// console.log(rowg.node());
g.exit()
.remove();
// rowg.append('text')
// .attr('x', '60px')
// .attr('y', '18px')
// .text(function(d){return format(d.key) + '円';});
var node = rowg.selectAll('g')
.data(function(d){
return d.values;
})
.enter()
.append('g')
.attr('class', 'node')
.attr('title', function(d){return d.title;})
.attr("transform", function(d,i) {
return "translate(" + xScale(i) + ",0)";
});
var caption = function(d) {
var caption = '<div id="tooltip-content">' +
'<strong>' + d.item.replace(/^\([^\)]+\)/,'') + "</strong><br />";
switch (mode) {
case 'amount':
caption += formatAmount(d.amount) + '円';
break;
case 'count':
caption += formatAmount(d.amount) + '円' + " x " + format(d.count) + '件';
break;
case 'totalAmount':
caption += formatAmount(d.amount) + '円' + " x " + format(d.count) + '件' + "<br />" +
'総額 ' + formatAmount(d.totalAmount) + '円';
break;
default:
}
caption += '</div>';
return caption;
};
node
.on('mouseover', function(d){
d3.select(this).classed("node--hover", true);
tooltip.html(caption(d))
.style('left', Math.max(0, (d3.event.pageX - parseInt(tooltip.style('width'))/2)) + 'px')
.style('top', Math.max(0, (d3.event.pageY - parseInt(tooltip.style('height')) - 12)) + 'px')
.style('opacity', 1);
})
.on('mousemove', function(d){
tooltip
.style('left', Math.max(0, (d3.event.pageX - parseInt(tooltip.style('width'))/2)) + 'px')
.style('top', Math.max(0, (d3.event.pageY - parseInt(tooltip.style('height')) - 12)) + 'px')
})
.on('mouseout', function(d){
d3.select(this).classed("node--hover", false);
tooltip
.style('opacity', 0);
});
var color = d3.scaleOrdinal(d3.schemeCategory20c);
rScale.domain([0, d3.max(data, function(d) { return d[mode]; })]);
node.append('circle')
.attrs({
cx: 6,
cy: 6,
r: function(d){return rScale(d[mode]);},
fill: function(d){return color(d.amount);},
stroke: function(d){return color(d.amount);},
'fill-opacity': 0.33,
'stroke-opacity': 0.9,
});
node.append('text')
.attrs({
class: 'item',
x: function(d){return rScale(d[mode]);},
y: 0,
})
.text(function(d){return format(d[mode]);});
renderLegend();
};
function renderLegend() {
var nestedData = d3.nest()
.key(function(d){return d.title;})
.sortValues(function (a, b) { return d3.descending(a.count, b.count); })
.rollup(function(v) { return d3.sum(v, function(d) { return +d.count; }); })
// .map(data);
.entries(data);
console.log(nestedData);
var g = svg.select('g')
.append('g')
.attrs({
class: 'legend-g',
transform: 'translate(500, 0)',
});
var item = g.selectAll('g')
.data(nestedData)
.enter()
.append('g')
.attrs({
class: 'legend-item-g',
transform: function(d,i){ return 'translate('+ Math.floor(i / 10) * 100 +', '+ ((i % 10) * 20) +')'; },
});
item.append('text')
.text(function(d){return d.key;})
item.on('mouseover', function(d){
var title = d3.select(this).text();
d3.select(this).classed("node--hover", true);
d3.selectAll('.node[title='+title+']').classed('selecting', true);
})
.on('mouseout', function(d){
var title = d3.select(this).text();
d3.select(this).classed("node--hover", false);
d3.selectAll('.node[title='+title+']').classed('selecting', false);
});
}
var updatePack = function(){
};
var renderPack = function(){
// remove
d3.selectAll('svg > g > g').remove();
var benchmark = function(d) {
return d[mode];
};
var maxBM = d3.max(data, function(d) { return benchmark(d); });
var minBM = d3.min(data, function(d) { return benchmark(d); });
// var color = d3.scaleSequential(d3.interpolateWarm)
var color = d3.scaleLog()
.base(Math.E)
.range(['#f44', '#eee'])
.domain([minBM, maxBM]);
var root = d3.stratify()
.id(function(d) { return d.item; })
.parentId(function(d) {
if (d.item === totalItem) { return ''; }
else { return totalItem; }
})
(data)
.sum(function(d) { return benchmark(d); })
.sort(function(a, b) { return benchmark(b) - benchmark(a); });
console.log(root);
pack(root);
var node = svg.select("g")
.selectAll("g")
.data(root.descendants())
.enter().append("g")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("class", function(d) { return "node" + (!d.children ? " node--leaf" : d.depth ? "" : " node--root"); })
.each(function(d) { d.node = this; })
.on("mouseover", hovered(true))
.on("mouseout", hovered(false));
node.append("circle")
.attr("id", function(d) { return "node-" + d.id; })
.attr("r", function(d) { return d.r; })
.style("fill", function(d) { return color(benchmark(d.data)); });
var leaf = node.filter(function(d) { return !d.children; });
leaf.append("clipPath")
.attr("id", function(d) { return "clip-" + d.id; })
.append("use")
.attr("xlink:href", function(d) { return "#node-" + d.id + ""; });
leaf.append("text")
// .attr("clip-path", function(d) { return "url(#clip-" + d.id + ")"; })
// .selectAll("tspan")
.text(function(d) { return d.data.title; })
.attr("x", 0)
.attr("y", 0);
// .enter().append("tspan")
// .attr("x", 0)
// .attr("y", function(d, i, nodes) { return 13 + (i - nodes.length / 2 - 0.5) * 10; })
// .text(function(d) { return d; });
node.append("title")
.text(function(d) { return d.id + "\n" + countFormat(benchmark(d.data)); });
svg.selectAll('.node--root').on('click', function(e) {
mode = 'totalAmount';
// alert('click');
// pack.value(function(d) {
// return d.totalAmount;
// })
pack(root);
node.selectAll('g').data(pack.nodes);
node.transition()
.duration(4500)
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
});
};
d3.csv(csvpath, function(error, d){
if (error) { throw error; }
data = d;
// add totalAmount property
data.forEach(function(d) {
d.rank = +d.rank;
d.amount = +d.amount;
d.count = +d.count;
d.totalAmount = d.amount * d.count;
})
// sum total
var totalCount = d3.sum(data, function(d) { return d.count; });
var totalAmount = d3.sum(data, function(d) { return d.amount; });
var totalTotalAmount = d3.sum(data, function(d) { return d.totalAmount; });
// add parent node
// data.push({item: totalItem, count: totalCount, amount:totalAmount, totalAmount:totalTotalAmount});
console.log(data);
renderTable();
});
function hovered(hover) {
return function(d) {
d3.selectAll(d.ancestors().map(function(d) { return d.node; }))
.classed("node--hover", hover);
};
}
</script>
</body>
</html>
https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.7/d3.min.js
https://d3js.org/d3-selection-multi.v1.min.js
https://d3js.org/d3-ease.v1.min.js