React和d3.js -创建播放/暂停按钮



我正在使用d3.js库在React中制作条形赛车图。我实现了这个例子,但我不能添加播放/暂停功能来停止和恢复动画,而不重新渲染整个图表。下面是我的代码:

export default function RacerBar(){
const ref = useRef()
const [animationEnabled, setAnimationEnabled] = useState(true);
const [isFetched, setIsFetched] = useState(false);
const [data, setData] = useState([{}]);
const [selectedIndicator, setSelectedIndicator] = useState("indicator_1");

useEffect(()=>{
const fetchData = async () => {
await fetch("/api/chart").then(
res => res.json())
.then((data) => {
setData(data)
setIsFetched(true)
})
}
fetchData()
},[])

useEffect(()=>{
if (isFetched) {
const names = new Set(data.map(d => d.client))
const datavalues = Array.from(d3.rollup(data, ([d]) => d[selectedIndicator], d => +d.year, d => d.client))
const svg = d3.select(ref.current)
svg.selectAll("*").remove()
//other svg settings
const ticker = 2500;

async function plotChart(data) {        
const dateList = new Set(data.map(d=>d.year));
const fontSize = 16;
const rectProperties = {height: 20, padding: 10}
const container = svg.append("g")
.classed("container", true)
.style("transform", "translateY(25px)")
const widthScale = d3.scaleLinear()
const axisTop = svg
.append('g')
.classed('axis', true)
.style("transform", "translate(10px, 20px)")
.call(d3.axisTop(widthScale))

//This part is responsible for the animation. 
const update = (date) =>  {
const presentData = [];
for (var i=0; i < data.length; i++){
if (data[i].year === date){
var key=data[i].client;
var value = data[i][selectedIndicator]
presentData.push({"key": key, "value": value})
}
}

//svg maniplation, drawing bars and labels similar to the example

}
// iterating through the data
for (const date of dateList) {
update(date)    
await new Promise(done => setTimeout(() => done(), ticker));
} 
}
plotChart(data)
}
},[isFetched, selectedIndicator])


const handleIndicatorChange = (event) => {
setSelectedIndicator(event.target.value);
}
return (
<div className="RacerBar">
<Dropdown label="Chooes option"
options={dropDownItems}
value={selectedIndicator}
onChange={handleIndicatorChange}/>
<div className="container">
<svg
ref={ref}  
style={{width: parseInt(width, 10) || 1000, 
height: parseInt(height, 10) || 1000}}
id={`racerbar_chart`}
/>
</div>  
<Button
onClick={() => { setAnimationEnabled(!animationEnabled) }}>
{animationEnabled ? "Pause" : "Play"}
</Button>
</div>
)
}

这里有一个小的解释:

我必须等到数据被获取。为此,我使用了useEffect。这段代码运行良好。

当抓取完成时,另一个调用plotChart函数的useEffect被触发。该函数的第一部分对svg对象进行进一步操作,然后是updatater函数。这部分负责绘制条形图,创建标签等。

这个updatater函数由for循环调用,该循环遍历数据端更新图表。我尝试了多种可能性,但我无法找到一个适当的解决方案来隔离更新函数并通过useEffect调用它。

不用说,我是React的初学者,我不完全了解它的DOM管理。

提前感谢!

编辑多亏了D.B.K.的帮助,我才找到了一个可以接受的解决方案。我在我的Github页面上上传了最终代码,请随意查看或评论。谢谢你!

尝试提取出plotChart函数,并在单独的useEffect中像这样调用它?

export default function RacerBar() {
const ref = useRef()
const [animationEnabled, setAnimationEnabled] = useState(true);
const [isFetched, setIsFetched] = useState(false);
const [data, setData] = useState([{}]);
const [selectedIndicator, setSelectedIndicator] = useState("indicator_1");
async function plotChart(data, selectedIndicator) {
const svg = d3.select(ref.current)
const dateList = new Set(data.map(d => d.year));
const fontSize = 16;
const rectProperties = { height: 20, padding: 10 }
const container = svg.append("g")
.classed("container", true)
.style("transform", "translateY(25px)")
const widthScale = d3.scaleLinear()
const axisTop = svg
.append('g')
.classed('axis', true)
.style("transform", "translate(10px, 20px)")
.call(d3.axisTop(widthScale))
//This part is responsible for the animation. 
const update = (date) => {
const presentData = [];
for (var i = 0; i < data.length; i++) {
if (data[i].year === date) {
var key = data[i].client;
var value = data[i][selectedIndicator]
presentData.push({ "key": key, "value": value })
}
}
//svg maniplation, drawing bars and labels similar to the example
}
// iterating through the data
for (const date of dateList) {
update(date)
await new Promise(done => setTimeout(() => done(), ticker));
}
}
useEffect(() => {
const fetchData = async () => {
await fetch("/api/chart").then(
res => res.json())
.then((data) => {
setData(data)
setIsFetched(true)
})
}
fetchData()
}, [])
useEffect(() => {
if (isFetched) {
const names = new Set(data.map(d => d.client))
const datavalues = Array.from(d3.rollup(data, ([d]) => d[selectedIndicator], d => +d.year, d => d.client))
const svg = d3.select(ref.current)
svg.selectAll("*").remove()
//other svg settings
const ticker = 2500;
}
}, [isFetched])
useEffect(() => {
plotChart(data, selectedIndicator);
}, [data, selectedIndicator])

const handleIndicatorChange = (event) => {
setSelectedIndicator(event.target.value);
}
return (
<div className="RacerBar">
<Dropdown label="Chooes option"
options={dropDownItems}
value={selectedIndicator}
onChange={handleIndicatorChange} />
<div className="container">
<svg
ref={ref}
style={{
width: parseInt(width, 10) || 1000,
height: parseInt(height, 10) || 1000
}}
id={`racerbar_chart`}
/>
</div>
<Button
onClick={() => { setAnimationEnabled(!animationEnabled) }}>
{animationEnabled ? "Pause" : "Play"}
</Button>
</div>
)
}

最新更新