Using transitions in React Hooks and D3

一个人想着一个人 提交于 2021-01-29 17:22:37

问题


I am trying to replicate the gauge seen here 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!


回答1:


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>


来源:https://stackoverflow.com/questions/65571772/using-transitions-in-react-hooks-and-d3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!