javascript - d3js v4 气泡图 - 恢复力/重力效果
问题描述
我正在开发一个 d3 应用程序 - 它具有气泡图。我有一个正在显示的版本 - 但版本 3 中的旧强制代码 - 但我不确定如何合并版本 4 强制效果。我想给气泡一些动画——电荷/重力类型的效果,所以总是有一些运动。
//没有强制效果的旧代码 http://jsfiddle.net/xzd9eamt/2/
var $this = $('.bubblechart');
var data = [{
"label": "Chinese",
"value": 20
}, {
"label": "American",
"value": 10
}, {
"label": "Indian",
"value": 50
}];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#ff5200", "red", "green"]);
var margin = {
top: 20,
right: 15,
bottom: 30,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'bubblechart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bubbles = svg.append('g').attr('class', 'bubbles');
var force = d3.forceSimulation()
.force("collide", d3.forceCollide(12))
.force("center", d3.forceCenter(width / 2, height / 2))
.nodes(data)
//.on("tick", tick);
var bubbles = svg.append("g")
.attr("class", "bubbles")
data = funnelData(data, width, height);
var padding = 4;
var maxRadius = d3.max(data, function(d) {
return parseInt(d.radius)
});
var scale = (width / 6) / 100;
var nodes = bubbles.selectAll("circle")
.data(data);
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 10)
.style("fill", function(d, i) {
return color(i);
})
//.call(d3.drag());
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function(d) {
return d.radius * scale;
})
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 1)
.remove();
draw('all');
function funnelData(data, width, height) {
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var max_amount = d3.max(data, function(d) {
return parseInt(d.value)
})
var radius_scale = d3.scalePow().exponent(0.5).domain([0, max_amount]).range([2, 85])
$.each(data, function(index, elem) {
elem.radius = radius_scale(elem.value) * .8;
elem.all = 'all';
elem.x = getRandom(0, width);
elem.y = getRandom(0, height);
});
return data;
}
function draw(varname) {
var foci = {
"all": {
name: "All",
x: width / 2,
y: height / 2
}
};
//force.on("tick", tick(foci, varname, .55));
//force.start();
}
function tick(foci, varname, k) {
return function(e) {
data.forEach(function(o, i) {
var f = foci[o[varname]];
o.y += (f.y - o.y) * k * e.alpha;
o.x += (f.x - o.x) * k * e.alpha;
});
nodes
.each(collide(.1))
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
}
function collide(alpha) {
var quadtree = d3.geom.quadtree(data);
return function(d) {
var r = d.radius + maxRadius + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
body {
background: #eeeeee;
}
.line {
fill: none;
stroke-width: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>BubbleChart I</h1>
<div class="bubblechart" data-width="300" data-height="300" />
解决方案
对于 d3 和 d4,您需要了解几件事。
首先,nodes
是空的,因为新节点没有自动添加到选择中。从 d3 v4 开始,您需要调用.merge()
以组合更新并输入部分选择。这篇文章是一个非常好的总结。
其次,没有必要自己做所有这些数据争论。d3-force
使用正确的值自动更新d.x
并d.y
自动更新,因此您可以删除collide
函数和计算d.x
和的部分d.y
。您需要做的就是在正确的位置画圆圈。
var $this = $('.bubblechart');
var data = [{
"label": "Chinese",
"value": 20
}, {
"label": "American",
"value": 10
}, {
"label": "Indian",
"value": 50
}];
var width = $this.data('width'),
height = $this.data('height');
var color = d3.scaleOrdinal()
.range(["#ff5200", "red", "green"]);
var margin = {
top: 20,
right: 15,
bottom: 30,
left: 20
},
width = width - margin.left - margin.right,
height = height - margin.top - margin.bottom;
var svg = d3.select($this[0])
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr('class', 'bubblechart')
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var bubbles = svg.append('g').attr('class', 'bubbles');
var force = d3.forceSimulation()
.force("collide", d3.forceCollide(12))
.force("center", d3.forceCenter(width / 2, height / 2))
.nodes(data);
var bubbles = svg.append("g")
.attr("class", "bubbles")
data = funnelData(data, width, height);
var padding = 4;
var maxRadius = d3.max(data, function(d) {
return parseInt(d.radius)
});
var scale = (width / 6) / 100;
var nodes = bubbles.selectAll("circle")
.data(data);
// Enter
nodes.enter()
.append("circle")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 10)
.style("fill", function(d, i) {
return color(i);
})
.call(d3.drag());
// Update
nodes
.transition()
.delay(300)
.duration(1000)
.attr("r", function(d) {
return d.radius * scale;
})
// Exit
nodes.exit()
.transition()
.duration(250)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 1)
.remove();
draw('all');
function funnelData(data, width, height) {
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
var max_amount = d3.max(data, function(d) {
return parseInt(d.value)
})
var radius_scale = d3.scalePow().exponent(0.5).domain([0, max_amount]).range([2, 85])
$.each(data, function(index, elem) {
elem.radius = radius_scale(elem.value) * .8;
elem.all = 'all';
elem.x = width / 2;
elem.y = height / 2;
});
return data;
}
function draw(varname) {
var foci = {
"all": {
name: "All",
x: width / 2,
y: height / 2
}
};
force.on("tick", tick(foci, varname, .55));
}
function tick(foci, varname, k) {
return function(e) {
bubbles.selectAll("circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
}
body {
background: #eeeeee;
}
.line {
fill: none;
stroke-width: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<h1>BubbleChart I</h1>
<div class="bubblechart" data-width="300" data-height="300" />
推荐阅读
- arrays - Mongodb聚合来自子文档嵌套数组的N个随机项
- istio - 无法使基于 lua 的 EnvoyFilter 工作
- c - 在终端中运行命令问题
- android - 为什么 Android Studio 北极狐将 rememberSaveable 变量标记为未使用?
- vb.net - 在 Inno Setup 创建的 exe 文件上,所有程序文件夹未显示 Ion
- git - 运行基本 git 命令(如“git fetch”)时出现 kex_exchange_identification 错误
- crystal-reports - 使用参数或其他东西来改进我正在做的事情的最佳方法是什么?
- javascript - 在 React 表中单击时访问“更多详细信息”
- asp.net-core - 如何在 Razor 页面中发布表格单元格值
- amazon-web-services - AWS QuickSight - 两个文件的联合