Can*_*ice 2 svg drag-and-drop reactjs react-hooks
我们有以下代码示例:
function DraggableText({ x, y, text }) {
const [position, setPosition] = React.useState({ x: x, y: y });
const [isDragging, setIsDragging] = React.useState(false);
const [mouseOffset, setMouseOffset] = React.useState({ x: 0, y: 0 });
const handleMouseDown = (event) => {
event.preventDefault();
setIsDragging(true);
setMouseOffset({
x: event.clientX - position.x,
y: event.clientY - position.y,
});
};
const handleMouseUp = (event) => {
setIsDragging(false);
};
const handleMouseMove = (event) => {
if (isDragging) {
setPosition({
x: event.clientX - mouseOffset.x,
y: event.clientY - mouseOffset.y,
});
}
};
return (
<text
x={position.x}
y={position.y}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseMove={handleMouseMove}
style={{ cursor: 'move' }}
>
{text}
</text>
);
}
function D3BarChart({ }) {
// And Finally, Return!
return (
<svg
className='cbb-box-shadowed'
width='100%'
viewBox={`0 0 700 450`}
preserveAspectRatio='xMaxYMax'
style={{ background: '#F2F2F2' }}
>
<DraggableText x={100} y={100} text="Drag me!" />
</svg>
);
}
// render both components
ReactDOM.render(
(<div>
<div width='60%'>
<D3BarChart />
</div>
</div>),
document.querySelector('#root'));Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="root">React component will be rendered here.</div>Run Code Online (Sandbox Code Playgroud)
当我们点击拖动文本时,鼠标在各个方向上的移动速度都比文本快5%左右。结果是使用文本拖动时出现错误的体验。
viewBox该问题似乎与中的有关<svg>。我们正在寻找一种解决方案,可以保留SVG 上的viewBox和width: 100%,以及<div width='60%'>内部D3BarChart返回的 ,因为这些是如何在我们的 Web 应用程序上创建和显示图形的关键部分。
也许有一种方法可以抵消?viewBox中的值。<DraggableText />
use*_*029 13
基本上,与我自己的组件(面板上的拖动手柄)相比,我可以看到的变化是
useCallback()处理程序,仅根据依赖项更改进行重建如果您移动鼠标的速度比 React 处理事件的速度快,则给予 mousemove 和 mouseup 更宽的范围可以让文本“赶上”鼠标。
否则,鼠标可能会与文本分离并且mousemove不再被处理。
function DraggableText({ x, y, text }) {
const [position, setPosition] = React.useState({ x, y })
const [isDragging, setIsDragging] = React.useState(false)
const [mouseOffset, setMouseOffset] = React.useState({ x: 0, y: 0 })
const textRef = React.useRef()
const [scale, setScale] = React.useState({ x: 1, y: 1 })
const calcScale = React.useCallback(() => {
if (!textRef.current) return
const parent = textRef.current.parentElement
const viewbox = parent.viewBox.baseVal;
const rect = parent.getBoundingClientRect();
const scale = {
x: viewbox.width / Math.round(rect.width),
y: viewbox.height / Math.round(rect.height)
}
return scale
}, [])
const handleMouseDown = React.useCallback(event => {
event.preventDefault()
setIsDragging(true)
const scale = calcScale()
setScale(scale)
const newPosition = {
x: (event.clientX * scale.x) - position.x,
y: (event.clientY * scale.y) - position.y,
}
setMouseOffset(newPosition)
}, [position, calcScale, scale])
const handleMouseUp = React.useCallback(() => {
if (!isDragging) return
setIsDragging(false)
}, [isDragging])
const handleMouseMove = React.useCallback(event => {
if (!isDragging) return
setPosition({
x: scale.x * event.clientX - mouseOffset.x,
y: scale.y * event.clientY - mouseOffset.y,
})
}, [isDragging, mouseOffset, scale])
// External listeners
React.useEffect(() => {
console.log('isDragging', isDragging)
if (!isDragging) return
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('mouseup', handleMouseUp)
return () => {
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('mouseup', handleMouseUp)
};
}, [isDragging, handleMouseMove, handleMouseUp])
return (
<text
ref={textRef}
x={position.x}
y={position.y}
onMouseDown={handleMouseDown}
// onMouseUp={handleMouseUp}
// onMouseMove={handleMouseMove}
style={{ cursor: 'move' }}
>
{text}
</text>
)
}
function D3BarChart({ }) {
// And Finally, Return!
return (
<svg
className='cbb-box-shadowed'
width='100%'
viewBox={`0 0 700 450`}
preserveAspectRatio='xMaxYMax'
style={{ background: '#F2F2F2' }}
>
<DraggableText x={100} y={100} text="Drag me!" />
</svg>
);
}
// render both components
ReactDOM.render(
(<div>
<div width='60%'>
<D3BarChart />
</div>
</div>),
document.querySelector('#root'));Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="root">React component will be rendered here.</div>Run Code Online (Sandbox Code Playgroud)
我还设置position了 mousedown 来消除mouseOffset状态,但没有在这里进行更改,以防发生其他我没有注意到的事情。
无论如何,这将是一个小的优化。
更新
我注意到当片段以全页模式打开时,它不再正确跟踪。
这是由于scale在组件安装上设置的,但如果执行此序列
向上拖动鼠标
调整窗口大小(在代码片段中,选择“整页”)
再次拖动
thenscale对于第二次拖动来说是错误的。
代码现已更新以处理窗口大小调整。
| 归档时间: |
|
| 查看次数: |
322 次 |
| 最近记录: |