问题
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