在React Hooks和D3中使用过渡 [英] Using transitions in React Hooks and D3

查看:96
本文介绍了在React Hooks和D3中使用过渡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试复制此处中看到的量表.这是我第一次使用D3.js,即使我设法复制了量规本身,其行为还是有些奇怪(针的过渡不起作用),而且我还没有完全理解useEffect和D3之间的动态.基本上,我想添加两个指针,每个指针将接收不同的值,并在它们的值更改时使用过渡.

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和angle2通过useState钩子进行了更新,但是过渡不起作用,并且使指针看上去很臭.没有过渡,它可以正常工作,但是针头的运动看起来非常粗糙.我不确定问题是否出在我集成d3部分的方式上还是所有重新渲染(因为我正在并行更改两个值)是否影响了挂钩的性能,也许我应该以其他方式更新角度办法.谢谢您的时间!

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!

推荐答案

我不是 react 专家,但在我看来,您想创建两个 useEffect 块.第一个仅触发一次即可进行初始 d3 设置.第二,更新角度并移动针头.

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>

这篇关于在React Hooks和D3中使用过渡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆