React 中的 SVG 平移会导致奇怪的行为
Posted
技术标签:
【中文标题】React 中的 SVG 平移会导致奇怪的行为【英文标题】:SVG panning in React results in weird behavior 【发布时间】:2021-05-28 23:45:54 【问题描述】:下面的 React 组件应该允许平移 SVG。确实如此,但由于某种原因,SVG 的移动呈指数级加速,因此鼠标指针的几个像素移动会导致 SVG(或者更确切地说,SVG 视图框)的移动越来越大。例如,轻轻拖动鼠标,圆圈就会从屏幕上拉开。
这里有一个小提琴:https://jsfiddle.net/bupham/ax473r52/4/
似乎可能会发生一些 React 反馈循环,但我不确定。平移行为代码来自另一个 SO 帖子 here。
我尝试将方法调用移至容器或 SVG,但它仍然会发生。我尝试将函数传递给 setState,但仍然会发生。我尝试制作 state.viewBox 的浅拷贝而不是制作浅拷贝——仍然发生。我做错了什么?
export default class SVGContainer extends React.Component
constructor(props)
super(props);
this.state=
viewBox: x:0,y:0,w:500,h:500,
svgSize: w: 500, h: 500,
scale: 1,
isPanning: false,
startPoint: x:0,y:0,
endPoint: x:0,y:0,
handleMouseDown = (e) =>
console.log('handleMouseDown e', e)
this.setState(
isPanning: true,
startPoint: x: e.clientX, y: e.clientY,
)
handleMouseMove = (e) =>
this.setState((prevState, props) =>
if (prevState.isPanning)
console.log('handleMouseMove e', e.clientX, e.clientY)
let startPoint = prevState.startPoint;
const scale = prevState.scale;
const viewBox = prevState.viewBox;
const endPoint = x: e.clientX, y: e.clientY;
const dx = (startPoint.x - endPoint.x) / scale;
const dy = (startPoint.y - endPoint.y) / scale;
const newViewbox = x:viewBox.x+dx, y:viewBox.y+dy, w:viewBox.w, h:viewBox.h;
console.log('the view box', newViewbox)
return viewBox: newViewbox;
)
handleMouseUp = (e) =>
if (this.state.isPanning)
let startPoint = this.state.startPoint;
const scale = this.state.scale;
const viewBox = this.state.viewBox;
const endPoint = x: e.clientX, y: e.clientY;
var dx = (startPoint.x - endPoint.x)/scale;
var dy = (startPoint.y - endPoint.y)/scale;
const endViewBox = x: viewBox.x+dx, y: viewBox.y+dy, w: viewBox.w, h: viewBox.h;
console.log('viewbox at mouseup',endViewBox)
this.setState(
viewBox: endViewBox,
isPanning: false,
);
handleMouseLeave = (e) =>
this.setState(
isPanning: false,
)
render()
return (
<div className="container" >
<svg
onWheel=this.handleWheelZoom
onMouseDown=this.handleMouseDown
onMouseMove=this.handleMouseMove
onMouseUp=this.handleMouseUp
onMouseLeave=this.handleMouseLeave
viewBox=`$this.state.viewBox.x $this.state.viewBox.y $this.state.viewBox.w $this.state.viewBox.h`>
<circle cx="50" cy="50" r="50" />
</svg>
</div>
);
【问题讨论】:
【参考方案1】:从技术上讲,这不是一个答案,而是一个解决方案:我决定改用 d3 (4.13.0),因为缩放的跨浏览器/触摸复杂性太大而无法手动编写。
至于为什么它不起作用,很可能与 React 和 React 状态的异步特性有关。一位朋友建议使用 requestAnimationFrame() 和/或限制鼠标事件。
这是我使用的相关代码的样子。我为需要使用 D3 操作的两个 DOM 节点添加了两个 React 引用:
export default class SVGContainer extends React.Component
constructor(props)
super(props);
this.svgRef = React.createRef();
this.gRef = React.createRef();
this.state=
container: width: 1000, height: 1000,
componentDidMount()
window.addEventListener('resize', this.handleWindowResize);
const width = window.innerWidth;
const height = window.innerHeight;
this.setState(
container: width, height,
)
const svg = d3.select(this.svgRef.current);
// D3 wants you to call zoom on a container and then apply the zoom transformations
// elsewhere...you can read why in the docs.
svg.call(this.handleZoom).call(this.handleZoom.transform, initialZoom);
componentWillUnmount()
window.removeEventListener('resize', this.handleWindowResize);
handleWindowResize = (e) =>
const height = this.svgRef.current.clientHeight;
const width = this.svgRef.current.clientWidth;
this.setState(
container: width, height,
);
handleZoom = d3.zoom().on('zoom', e =>
const g = d3.select(this.gRef.current);
g.attr('transform', d3.event.transform)
)
render()
return (
<div className="SVGContainer">
<svg
width=this.state.container.width height=this.state.container.height
ref=this.svgRef>
<circle cx="50" cy="50" r="50" ref=this.gRef />
</svg>
</div>
);
【讨论】:
以上是关于React 中的 SVG 平移会导致奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章