D3 图表在 React.js 组件上复制
D3 chart is duplicating on React.js component
这是在 React 应用程序上,可能出了什么问题?每次都会附加一个新的 svg,而不是存在更新的 svg...
我不确定我应该添加哪些其他详细信息,我已经包含了您可能需要帮助我的所有信息,请让我知道是否还有其他我应该添加的内容。谢谢你。
这是我的组件,我正在通过 props 传递数据。
export const FourDirectionsTimeChart = ({data}) => {
useEffect(() => {
const width = document.getElementById("container").clientWidth;
const height = document.getElementById("container").clientHeight;
const R = (width + height) / 8;
const CX = width / 2;
const CY = height / 2;
const smallR = R * 0.1;
const circleColor = "bisque";
const itemColor = "#3F4200";
let svg = d3.select("#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${width} ${height}`)
let mainCircle = svg.append("circle")
.attr("fill", circleColor)
.attr("r", R)
.attr("cx", CX)
.attr("cy", CY);
let centerCircle = svg.append("circle")
.attr("fill", "white")
.attr("r", smallR)
.attr("cx", CX)
.attr("cy", CY);
const timePercentage = (time) => {
const percentage = (time * 100 / 23) / 100;
return percentage;
};
function timeToRadius(time) {
return (smallR + timePercentage(time) * (R - smallR))
}
// Times concentric circles ---
for (let i = 0; i <= 23; i += 4) {
svg.append("circle")
.attr("fill", "none")
.attr("cx", CX)
.attr("cy", CY)
.attr("stroke-dasharray", "4 20")
.attr("stroke", "gray")
.attr("stroke-width", 1)
.attr("r", timeToRadius(i))
}
// Cardinal points ---
const textTime = 25;
const fontSize = R * 0.25;
svg.append("text")
.attr("dx", getPosition(0, textTime).x)
.attr("dy", getPosition(0, textTime).y)
.text("N")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "middle")
.style("font-family", "serif")
svg.append("text")
.attr("dx", getPosition(180, textTime).x)
.attr("dy", getPosition(180, textTime).y)
.text("S")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "middle")
.style("font-family", "serif")
.attr("alignment-baseline", "hanging")
svg.append("text")
.attr("dx", getPosition(-90, textTime).x)
.attr("dy", getPosition(-90, textTime).y)
.text("E")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "start")
.attr("alignment-baseline", "middle")
.style("font-family", "serif");
svg.append("text")
.attr("dx", getPosition(90, textTime).x)
.attr("dy", getPosition(90, textTime).y)
.text("O")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "end")
.attr("alignment-baseline", "middle")
.style("font-family", "serif")
// Ships positions ---
function getPosition(degrees, time) {
const getRadians = (degrees) => degrees * Math.PI / 180 + Math.PI / 2;
let x = (smallR + timePercentage(time) * (R - smallR)) * Math.cos(getRadians(degrees)) + CX;
let y = (smallR + timePercentage(time) * (R - smallR)) * Math.sin(getRadians(degrees)) * -1 + CY;
return { x, y };
}
// Data mapping ---
let parsedData = [];
(() => {
data?.forEach((x) => {
parsedData.push({
id: x.Patente,
course: x.Rumbo,
speed: x.Velocidad,
time: new Date(x.Fecha_gps).getHours()
})
});
let position;
parsedData.map(d => {
position = getPosition(d.course, d.time);
svg.append("circle")
.attr("fill", () => {
if (d.speed == 0) return "brown";
else return "brown";
})
.attr("r", R * 0.015)
.attr("cx", position.x)
.attr("cy", position.y)
.attr("opacity", 0.4);
});
})();
}, [data]);
return (
<div id="container" style={{ width: '100%', height: '100%' }}></div>
)
}
您认为 let svg = d3.select("#container").append("svg")
行的作用是什么?它选择 ID 为 "container"
的元素并向其附加一个新的 SVG 元素。
您基本上可以做以下两件事之一:
- Select 和 remove 容器中所有现有的 SVG 元素,然后在更新时从头开始绘制图表:
d3.select("#container svg").remove()
,然后 d3.select("#container").append("svg")
;
- 将初始化(附加 svg、设置大小、绘制轴)逻辑与可能需要 运行 多次(更新值)的逻辑分开,并确保在更新时仅调用第二部分.这更复杂,但也更有效,尤其是在像 React 这样已经 DOM 的生态系统中。
这是在 React 应用程序上,可能出了什么问题?每次都会附加一个新的 svg,而不是存在更新的 svg...
我不确定我应该添加哪些其他详细信息,我已经包含了您可能需要帮助我的所有信息,请让我知道是否还有其他我应该添加的内容。谢谢你。
这是我的组件,我正在通过 props 传递数据。
export const FourDirectionsTimeChart = ({data}) => {
useEffect(() => {
const width = document.getElementById("container").clientWidth;
const height = document.getElementById("container").clientHeight;
const R = (width + height) / 8;
const CX = width / 2;
const CY = height / 2;
const smallR = R * 0.1;
const circleColor = "bisque";
const itemColor = "#3F4200";
let svg = d3.select("#container")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", `0 0 ${width} ${height}`)
let mainCircle = svg.append("circle")
.attr("fill", circleColor)
.attr("r", R)
.attr("cx", CX)
.attr("cy", CY);
let centerCircle = svg.append("circle")
.attr("fill", "white")
.attr("r", smallR)
.attr("cx", CX)
.attr("cy", CY);
const timePercentage = (time) => {
const percentage = (time * 100 / 23) / 100;
return percentage;
};
function timeToRadius(time) {
return (smallR + timePercentage(time) * (R - smallR))
}
// Times concentric circles ---
for (let i = 0; i <= 23; i += 4) {
svg.append("circle")
.attr("fill", "none")
.attr("cx", CX)
.attr("cy", CY)
.attr("stroke-dasharray", "4 20")
.attr("stroke", "gray")
.attr("stroke-width", 1)
.attr("r", timeToRadius(i))
}
// Cardinal points ---
const textTime = 25;
const fontSize = R * 0.25;
svg.append("text")
.attr("dx", getPosition(0, textTime).x)
.attr("dy", getPosition(0, textTime).y)
.text("N")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "middle")
.style("font-family", "serif")
svg.append("text")
.attr("dx", getPosition(180, textTime).x)
.attr("dy", getPosition(180, textTime).y)
.text("S")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "middle")
.style("font-family", "serif")
.attr("alignment-baseline", "hanging")
svg.append("text")
.attr("dx", getPosition(-90, textTime).x)
.attr("dy", getPosition(-90, textTime).y)
.text("E")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "start")
.attr("alignment-baseline", "middle")
.style("font-family", "serif");
svg.append("text")
.attr("dx", getPosition(90, textTime).x)
.attr("dy", getPosition(90, textTime).y)
.text("O")
.attr("fill", "black")
.attr("font-size", fontSize)
.attr("text-anchor", "end")
.attr("alignment-baseline", "middle")
.style("font-family", "serif")
// Ships positions ---
function getPosition(degrees, time) {
const getRadians = (degrees) => degrees * Math.PI / 180 + Math.PI / 2;
let x = (smallR + timePercentage(time) * (R - smallR)) * Math.cos(getRadians(degrees)) + CX;
let y = (smallR + timePercentage(time) * (R - smallR)) * Math.sin(getRadians(degrees)) * -1 + CY;
return { x, y };
}
// Data mapping ---
let parsedData = [];
(() => {
data?.forEach((x) => {
parsedData.push({
id: x.Patente,
course: x.Rumbo,
speed: x.Velocidad,
time: new Date(x.Fecha_gps).getHours()
})
});
let position;
parsedData.map(d => {
position = getPosition(d.course, d.time);
svg.append("circle")
.attr("fill", () => {
if (d.speed == 0) return "brown";
else return "brown";
})
.attr("r", R * 0.015)
.attr("cx", position.x)
.attr("cy", position.y)
.attr("opacity", 0.4);
});
})();
}, [data]);
return (
<div id="container" style={{ width: '100%', height: '100%' }}></div>
)
}
您认为 let svg = d3.select("#container").append("svg")
行的作用是什么?它选择 ID 为 "container"
的元素并向其附加一个新的 SVG 元素。
您基本上可以做以下两件事之一:
- Select 和 remove 容器中所有现有的 SVG 元素,然后在更新时从头开始绘制图表:
d3.select("#container svg").remove()
,然后d3.select("#container").append("svg")
; - 将初始化(附加 svg、设置大小、绘制轴)逻辑与可能需要 运行 多次(更新值)的逻辑分开,并确保在更新时仅调用第二部分.这更复杂,但也更有效,尤其是在像 React 这样已经 DOM 的生态系统中。