d3 force 没有正确应用 x 刻度
d3 force not applying x scale properly
我想对使用数组的 d3 力生成的圆应用比例尺。
这段代码生成了正确的 x 轴,但是 class 'goalamount' 生成了很多圆,它们都在屏幕外数万像素。目标数量中应该只有六个圆圈 class 并且它们都应该缩放到 x 轴 - 我做错了什么?
const data = [{
x: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x === data[dotindex].x) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
var u = d3.select('svg')
.append("g")
.attr("class", "goalAmounts")
.selectAll('goalAmounts')
.data(data)
u.enter()
.append("circle")
// .attr( "transform", "translate(" + 2000 + "," + 50 + ")")
.attr("r", d => amt(d.amt))
.merge(u)
.attr("fill", d => d.colour)
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
u.exit().remove()
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
svg.selectAll("circle")
.data(data)
.enter()
.append("g")
.attr("class", "circles")
.append("circle")
.attr("transform", "translate(" + 100 + "," + 650 + ")")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(pxX / 2, pxY / 2))
.force('collision', d3.forceCollide().radius(function(d) {
return d.amt
}))
.on('tick', ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>
改用 d3.forceX
和 d3.forceY
,这样您就可以将节点绘制到它们的预期位置。此外,d3-force
填充节点的 x
和 y
属性,因此您需要使用 d.x1
或其他东西。 scX(d.x)
造成节点的巨大价值。
const data = [{
x1: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x1: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x1;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x1, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x1, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x1 === data[dotindex].x1) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
circles
.attr("cx", d => d.x)
.attr("cy", d => d.y);
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
const circles = svg.append("g")
.attr("class", "circles")
.attr( "transform", "translate(" + 100 + "," + 100 + ")")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x1))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force("x", d3.forceX(d => scX(d.x1)))
.force("y", d3.forceY(d => scY(d.y2)))
.force('collision', d3.forceCollide().radius(d => rad(d.rad)))
.on("tick", ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>
我想对使用数组的 d3 力生成的圆应用比例尺。
这段代码生成了正确的 x 轴,但是 class 'goalamount' 生成了很多圆,它们都在屏幕外数万像素。目标数量中应该只有六个圆圈 class 并且它们都应该缩放到 x 轴 - 我做错了什么?
const data = [{
x: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x === data[dotindex].x) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
var u = d3.select('svg')
.append("g")
.attr("class", "goalAmounts")
.selectAll('goalAmounts')
.data(data)
u.enter()
.append("circle")
// .attr( "transform", "translate(" + 2000 + "," + 50 + ")")
.attr("r", d => amt(d.amt))
.merge(u)
.attr("fill", d => d.colour)
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
u.exit().remove()
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
svg.selectAll("circle")
.data(data)
.enter()
.append("g")
.attr("class", "circles")
.append("circle")
.attr("transform", "translate(" + 100 + "," + 650 + ")")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(pxX / 2, pxY / 2))
.force('collision', d3.forceCollide().radius(function(d) {
return d.amt
}))
.on('tick', ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>
改用 d3.forceX
和 d3.forceY
,这样您就可以将节点绘制到它们的预期位置。此外,d3-force
填充节点的 x
和 y
属性,因此您需要使用 d.x1
或其他东西。 scX(d.x)
造成节点的巨大价值。
const data = [{
x1: 2020,
cx: 0,
colour: "#69306D",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#247BA0",
scY: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#3F762C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 50,
rad: 10,
amt: 5000
},
{
x1: 2022,
cx: 0,
colour: "#0C3957",
y1: 0,
y2: 170,
rad: 10,
amt: 5000
},
{
x1: 2055,
cx: 0,
colour: "#BF802F",
y1: 0,
y2: 50,
rad: 10,
amt: 15000
}
];
const maxYear = Math.max.apply(Math, data.map(function(o) {
return o.x1;
}));
const svg = d3.select("svg");
const pxX = svg.attr("width");
const pxY = svg.attr("height");
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) {
return o.y1;
}))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = (arr, accessor, range) => {
return d3.scaleLinear()
.domain(d3.extent(arr, accessor))
.range(range)
.nice()
}
const thisYear = new Date().getFullYear()
let tickTens = [];
for (let i = thisYear; i < maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
const scX = makeScale(data, d => d.x1, [0, pxX - 200]);
const scX1 = makeScale(data, d => d.x1, [0, pxX - 2020]);
const scY = d3.scaleLinear().domain([0, 100]).range([0, 100]);
const g = d3.axisBottom(scX).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
const rad = d3.scaleLinear()
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear()
.domain(d3.extent(data, d => d.amt))
.range([20, 50]);
for (let dotindex = 0; dotindex < data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x1 === data[dotindex].x1) {
data[dotindex].scY = data[dotindex - 1].scY - 20
}
}
}
const ticked = () => {
circles
.attr("cx", d => d.x)
.attr("cy", d => d.y);
}
svg.append("g")
.attr("transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call(g)
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
const circles = svg.append("g")
.attr("class", "circles")
.attr( "transform", "translate(" + 100 + "," + 100 + ")")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x1))
.attr("cy", d => scY(d.y2))
.attr("r", d => rad(d.rad));
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr("transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick text")
.attr("font-size", 20)
.attr("transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
d3.forceSimulation(data)
.force("x", d3.forceX(d => scX(d.x1)))
.force("y", d3.forceY(d => scY(d.y2)))
.force('collision', d3.forceCollide().radius(d => rad(d.rad)))
.on("tick", ticked);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="App">
<svg id="demo1" width="1200" height="700">
</svg>
</div>