简体   繁体   中英

Using transitions in React Hooks and D3

I am trying to replicate the gauge seenhere in React with Hooks. It's the first time I am working with D3.js and even though I managed to replicate the gauge itself, the behavior is a bit strange (transition of the needles does not work) and I am not fully understanding the dynamics between useEffect and D3. Basically I want to add two needles that will each receive different values and use a transition when their value changes.

//declaring angle value
const [angle1, setAngle1] = useState(0)

//useEffect hook
 useEffect(() => {
    const outerR = Math.min(window.innerWidth, window.innerHeight) / 2

    const svg = select(svgRef.current)
    svg.selectAll('*').remove()

    const EXTRA_ANGLE = 15

    const angleScale = scaleLinear()
      .domain([0, 100])
      .range([-90 - EXTRA_ANGLE, 90 + EXTRA_ANGLE])

    const arcAxis = axisRadialInner(
      angleScale.copy().range(angleScale.range().map(deg2rad)),
      outerR - 5
    )

    svg
      .attr('width', outerR * 2)
      .attr('height', outerR * 1.5)
      .attr(
        'viewBox',
        `${-outerR} ${-outerR * 0.8} ${outerR * 2} ${outerR * 1}`
      )
      .append('g')
      .classed('axis', true)
      .call(arcAxis)

    const needle1 = svg
      .append('g')
      .attr('transform', `scale(${outerR * 0.85})`)
      .append('path')
      .classed('needle1', true)
      .attr(
        'd',
        ['M0 -1', 'L0.03 0', 'A 0.03 0.03 0 0 1 -0.03 0', 'Z'].join(' ')
      )
      .transition()
      .attr('transform', `rotate(${angleScale(angle1)})`)

    const needle2 = svg
      .append('g')
      .attr('transform', `scale(${outerR * 0.85})`)
      .append('path')
      .classed(needle2, true)
      .attr(
        'd',
        ['M0 -1', 'L0.03 0', 'A 0.03 0.03 0 0 1 -0.03 0', 'Z'].join(' ')
      )
      .transition()
      .attr('transform', `rotate(${angleScale(angle2)})`)

    // Add val label
    const label = svg
      .append('text')
      .classed('label', true)
      .attr('x', 0)
      .attr('y', outerR * 0.2)
      .attr('text-anchor', 'middle')
      .text(angle1)

    function deg2rad (deg) {
      return (deg * Math.PI) / 180
    }

  }, [angle1, angle2])

//updating an angle
function updateAngle1(){
   setInterval(() => {
      setAngle1(angle1 => angle1 + 1)
    }, 200); 

Angle1 and angle2 are updated via useState hook, but the transition does not work and makes the needles look bugged. Without the transition it works OK, but the movement of the needles looks very rough. I am not sure if the problem is in the way I am integrating the d3 part or if all the re-renders (because I am changing two values in paralel) affect the performance of the hook and maybe I should update the angles in a different way. Thank you for your time!

I'm not a react expert but it seems to me your want to create two useEffect blocks. The first, only fires once to do the initial d3 set-up. The second, to update your angle and move the needle.

 const {useState, useEffect} = React; const App = () => { //declaring angle value let [angle1, setAngle1] = useState(0); function deg2rad (deg) { return (deg * Math.PI) / 180; } const EXTRA_ANGLE = 15; const angleScale = d3.scaleLinear().domain([0, 100]).range([-90 - EXTRA_ANGLE, 90 + EXTRA_ANGLE]); useEffect(() => { const svg = d3.select('svg'); const outerR = Math.min(window.innerWidth, window.innerHeight) / 2; const arcAxis = d3.axisRadialInner( angleScale.copy().range(angleScale.range().map(deg2rad)), outerR - 5 ); svg.attr('width', outerR * 2).attr('height', outerR * 1.5).attr( 'viewBox', `${-outerR} ${-outerR * 0.8} ${outerR * 2} ${outerR * 1}` ).append('g').classed('axis', true).call(arcAxis); const needle1 = svg.append('g').attr('transform', `scale(${outerR * 0.85})`).append('path').classed('needle1', true).attr( 'd', ['M0 -1', 'L0.03 0', 'A 0.03 0.03 0 0 1 -0.03 0', 'Z'].join(' ') ); // Add val label const label = svg.append('text').classed('label', true).attr('x', 0).attr('y', outerR * 0.2).attr('text-anchor', 'middle'); // kick off updates setInterval(() => { angle1 += 1; if (angle1 > 100) angle1 = 100; setAngle1(angle1); }, 500); },[]) useEffect(() => { d3.select('.needle1').transition().attr('transform', `rotate(${angleScale(angle1)})`); d3.select('.label').text(angle1); }, [angle1]) return <svg></svg>; } // Render it ReactDOM.render( <App />, document.getElementById("react") );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/d3-radial-axis@1.6.4/dist/d3-radial-axis.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="react"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM