【问题标题】:React and d3.js - create Play/Pause buttonReact 和 d3.js - 创建播放/暂停按钮
【发布时间】:2022-08-24 20:10:09
【问题描述】:

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

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 对象进行了进一步的操作,然后是 updater 函数。这部分负责绘制条形图、创建标签等。

此更新器函数由循环调用数据端更新图表的 for 循环。 我尝试了多种可能性,但我找不到合适的解决方案来隔离更新程序功能并通过 useEffect 调用它。

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

先感谢您!

编辑

感谢 DBK 的帮助。我可以找到我的问题的可接受的解决方案。我在我的Github 页面上上传了最终代码,请随时查看或评论。 谢谢!

  • 欢迎来到 StackOverflow! selectedIndicator 在哪里声明?
  • 感谢你的回复!我很抱歉,但是当我试图意外地减少我的代码时,我删除了它。我在代码开头使用 setState 函数声明了它。我还忘了提到代码运行良好,它正在为图表设置动画,但是当我更改指标时,它当然会重新渲染整个图表。
  • 只需从 useEffect 依赖数组中删除 selectedIndicator,它不应该在每次更改时重新渲染
  • 谢谢!当然,你是对的,但我的目标是停止或重新开始迭代而不重新渲染 SVG 对象。为了激活这个,我想,我想修改第二个 useEffect 函数。

标签: javascript reactjs d3.js


【解决方案1】:

尝试提取出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>
  )
}

【讨论】:

  • 对不起我的缺席!您的回答对我解决问题有很大帮助。我编辑了我的原始问题并插入了一个指向我最终解决方案的链接。再次感谢!
猜你喜欢
  • 2021-10-20
  • 2018-10-08
  • 1970-01-01
  • 2019-06-25
  • 2020-12-03
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多