d3js v4:缩放圆圈
d3js v4: Scale circles with zoom
我有一张用 d3js v4 和 topojson 制作的世界地图,它有缩放/拖动/圆圈。一切似乎都很好,除了我无法通过缩放将圆圈一起缩放。
当我滚动到地图时,我的圈子保持相同大小,这使得它们比地图大得多。
缩放时如何将变换应用于圆圈?
var width = 660,
height = 400;
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var projection = d3.geoMercator()
.center([50, 10]) //long and lat starting position
.scale(150) //starting zoom position
.rotate([10,0]); //where world split occurs
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);
var path = d3.geoPath()
.projection(projection);
var g = svg.append("g");
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
}
d3.select(".zoom-in").on("click", function() {
zoom.scaleBy(svg.transition().duration(750), 1.2);
});
d3.select(".zoom-out").on("click", function() {
zoom.scaleBy(svg.transition().duration(750), 0.8);
});
// load and display the world and locations
d3.json("https://gist.githubusercontent.com/d3noob/5193723/raw/world-110m2.json", function(error, topology) {
var world = g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries).geometries)
.enter()
.append("path")
.attr("d", path)
;
var locations = g.selectAll("circle")
.data(devicesAll)
.enter()
.append("circle")
.attr("cx", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0];})
.attr("cy", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1];})
.attr("r", 2)
.style("fill", "black")
.style("opacity", 1)
;
var simulation = d3.forceSimulation()
.force('x', d3.forceX().x(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0]}))
.force('y', d3.forceY().y(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1]}))
.force("charge", d3.forceManyBody().strength(0.5)) // Nodes are attracted one each other of value is > 0
.force("collide", d3.forceCollide().strength(.1).radius(2).iterations(2)) // Force that avoids circle overlapping
// Apply these forces to the nodes and update their positions.
// Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
simulation
.nodes(devicesAll)
.on("tick", function(d){
locations
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
});
如果我正确理解了你的问题,你需要将它添加到你的缩放行为中。
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
}
在这里,您将转换应用于元素,这很好。但是,您没有对半径应用任何逻辑。
该逻辑由您决定,并且取决于转换事件 (currentTransform.k
) 的 k
属性。
我将为您的半径使用一些虚拟逻辑。您的缩放范围在 1 到 10 之间,您需要一个逻辑,其中半径随着缩放的增加而减小(更大的 k)。同样重要的是你的半径不要低于 1,因为圆的面积会减少得更快(记住面积取决于 r^2,r^2 < r for r < 1)
所以我的逻辑是:半径是 2.1 - (k / 10)。同样,我过于简单化了,您可以根据您的具体情况更改或调整它。
最后,它应该看起来像这样:
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
g.selectAll("circle")
.attr("r", 2.1 - (currentTransform.k / 10))
}
我还没有测试代码,但请告诉我这是否有效!如果需要,也许你可以将它添加到 jsfiddle
我有一张用 d3js v4 和 topojson 制作的世界地图,它有缩放/拖动/圆圈。一切似乎都很好,除了我无法通过缩放将圆圈一起缩放。
当我滚动到地图时,我的圈子保持相同大小,这使得它们比地图大得多。
缩放时如何将变换应用于圆圈?
var width = 660,
height = 400;
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var projection = d3.geoMercator()
.center([50, 10]) //long and lat starting position
.scale(150) //starting zoom position
.rotate([10,0]); //where world split occurs
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);
var path = d3.geoPath()
.projection(projection);
var g = svg.append("g");
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
}
d3.select(".zoom-in").on("click", function() {
zoom.scaleBy(svg.transition().duration(750), 1.2);
});
d3.select(".zoom-out").on("click", function() {
zoom.scaleBy(svg.transition().duration(750), 0.8);
});
// load and display the world and locations
d3.json("https://gist.githubusercontent.com/d3noob/5193723/raw/world-110m2.json", function(error, topology) {
var world = g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries).geometries)
.enter()
.append("path")
.attr("d", path)
;
var locations = g.selectAll("circle")
.data(devicesAll)
.enter()
.append("circle")
.attr("cx", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0];})
.attr("cy", function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1];})
.attr("r", 2)
.style("fill", "black")
.style("opacity", 1)
;
var simulation = d3.forceSimulation()
.force('x', d3.forceX().x(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[0]}))
.force('y', d3.forceY().y(function(d) {return projection([d.LastLocation.lon, d.LastLocation.lat])[1]}))
.force("charge", d3.forceManyBody().strength(0.5)) // Nodes are attracted one each other of value is > 0
.force("collide", d3.forceCollide().strength(.1).radius(2).iterations(2)) // Force that avoids circle overlapping
// Apply these forces to the nodes and update their positions.
// Once the force algorithm is happy with positions ('alpha' value is low enough), simulations will stop.
simulation
.nodes(devicesAll)
.on("tick", function(d){
locations
.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; })
});
如果我正确理解了你的问题,你需要将它添加到你的缩放行为中。
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
}
在这里,您将转换应用于元素,这很好。但是,您没有对半径应用任何逻辑。
该逻辑由您决定,并且取决于转换事件 (currentTransform.k
) 的 k
属性。
我将为您的半径使用一些虚拟逻辑。您的缩放范围在 1 到 10 之间,您需要一个逻辑,其中半径随着缩放的增加而减小(更大的 k)。同样重要的是你的半径不要低于 1,因为圆的面积会减少得更快(记住面积取决于 r^2,r^2 < r for r < 1)
所以我的逻辑是:半径是 2.1 - (k / 10)。同样,我过于简单化了,您可以根据您的具体情况更改或调整它。
最后,它应该看起来像这样:
//Zoom functionality
function zoomed() {
const currentTransform = d3.event.transform;
g.attr("transform", currentTransform);
g.selectAll("circle")
.attr("r", 2.1 - (currentTransform.k / 10))
}
我还没有测试代码,但请告诉我这是否有效!如果需要,也许你可以将它添加到 jsfiddle