Javascript 如何在React中实现拖放界面,并具有放大和缩小功能?

Javascript 如何在React中实现拖放界面,并具有放大和缩小功能?,javascript,reactjs,Javascript,Reactjs,我正在尝试构建一个web应用程序,其中一个核心功能是将图标拖放到地图的蓝图上。为此,我使用React,因为我需要能够存储蓝图中各种对象的位置,以便准确地复制它们 到目前为止,我有一个存储对象数组的父组件,每个对象都有一个x和一个y属性。该数组被传递给画布组件,该组件在数组上迭代,并通过将top和left属性设置为相应对象的适当x和y属性,在画布中绝对定位每个数组 我已经在这个代码笔中演示了这个原则: 我遇到的问题是增加了缩放功能。父组件存储传递到画布的缩放属性,并且使用CSS中的缩放变换根据缩放

我正在尝试构建一个web应用程序,其中一个核心功能是将图标拖放到地图的蓝图上。为此,我使用React,因为我需要能够存储蓝图中各种对象的位置,以便准确地复制它们

到目前为止,我有一个存储对象数组的父组件,每个对象都有一个x和一个y属性。该数组被传递给画布组件,该组件在数组上迭代,并通过将top和left属性设置为相应对象的适当x和y属性,在画布中绝对定位每个数组

我已经在这个代码笔中演示了这个原则:

我遇到的问题是增加了缩放功能。父组件存储传递到画布的缩放属性,并且使用CSS中的缩放变换根据缩放因子缩放画布的大小

class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            zoom: 1, // ratio to scale canvas
            map: map // root object that stores positions of items within canvas
        }  
    }
    updatePositions(newPositions) {} // updates positions in state when element is finished dragging
    updateZoom(e) {} // updates zoom factor
    render() {
        return (
            <div>
                <Canvas 
                    positions={this.state[...].positions} 
                    updatePositions={this.updatePositions}
                    zoom={this.state.zoom}/>
            </div>
        )
    }
}
类应用程序扩展了React.Component{
建造师(道具){
超级(道具);
此.state={
缩放:1,//与画布比例
映射:映射//在画布中存储项目位置的根对象
}  
}
updatePositions(newPositions){}//在元素拖动完成时更新状态中的位置
updateZoom(e){}//更新缩放因子
render(){
返回(
)
}
}
子组件迭代位置数组并渲染每个位置

class Canvas extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            index: 0
        }

        // Logic to get bounds of rendered element to calculate positions within canvas
        this.selector = React.createRef();
    }
    // Select element, set active index to track position object to update, add event listener to get new position
    selectElement(index) {
        this.setState({
             index: index,
             drag: true
        }, () => {
          document.addEventListener("mousemove", this.onMouseMove);
          document.addEventListener("mouseup", this.onMouseUp);
        });
      }
      // Track mouse within bounding box to move object
      onMouseMove(e) {
        let width = 100;
        let height = 100;
        let elements = [...this.props.operatorPositions];
        let newX = e.pageX - (width / 2);
        let newY = e.pageY - height;
        // Make sure object is within canvas
        if (newX < this.state.bounds.left) {
          newX = this.state.bounds.left - this.state.bounds.x;
        } else if (newX> this.state.bounds.right - width - this.state.bounds.x) {
          newX = this.state.bounds.right - width - this.state.bounds.x;
        } 
        if (newY < this.state.bounds.top - this.state.bounds.y) {
          newY = this.state.bounds.top - this.state.bounds.y;
        } else if (newY > this.state.bounds.bottom - height - this.state.bounds.y) {
          newY = this.state.bounds.bottom - height - this.state.bounds.y;
        }
        elements[this.state.index].x = newX;
        elements[this.state.index].y = newY;
        this.props.updatePositions(elements);
      }
      // Stop tracking mouse when object is dropped
      onMouseUp(e) {
        document.removeEventListener("mousemove", this.onMouseMove);
        document.removeEventListener("mouseup", this.onMouseUp);
        this.setState({
          drag: false
        });
      }
      // Get bounding box of canvas
      componentDidMount() {
        if (!this.state.bounds) {
          this.setState({
            bounds: this.selector.current.getBoundingClientRect()
          })
        }
      }
    render() {
        const boxes = this.props.operatorPositions.map((op, index) => {
          return <Box name={this.props.operators[index]} x={op.x} y={op.y} key={index} index={index} 
           selectElement={this.selectElement} drag={index === this.state.index && this.state.drag}/>
        });
        return (
          <div id="canvas" ref={this.selector} style={{ transform: `scale(${this.props.zoom}`}}>
            { boxes }
          </div>
        )
      }
}
类画布扩展React.Component{
建造师(道具){
超级(道具);
此.state={
索引:0
}
//获取渲染元素边界以计算画布内位置的逻辑
this.selector=React.createRef();
}
//选择元素,设置活动索引以跟踪要更新的位置对象,添加事件侦听器以获取新位置
选择元素(索引){
这是我的国家({
索引:索引,,
拖动:正确
}, () => {
document.addEventListener(“mousemove”,this.onMouseMove);
document.addEventListener(“mouseup”,this.onMouseUp);
});
}
//在边界框内跟踪鼠标以移动对象
onMouseMove(e){
宽度=100;
设高度=100;
让元素=[…this.props.operatorPositions];
设newX=e.pageX-(宽度/2);
设newY=e.pageY-高度;
//确保对象位于画布内
if(newXthis.state.bounds.right-width-this.state.bounds.x){
newX=this.state.bounds.right-width-this.state.bounds.x;
} 
if(newYthis.state.bounds.bottom-height-this.state.bounds.y){
newY=this.state.bounds.bottom-height-this.state.bounds.y;
}
元素[this.state.index].x=newX;
元素[this.state.index].y=newY;
this.props.updatePosition(元素);
}
//物体下落时停止跟踪鼠标
onMouseUp(e){
document.removeEventListener(“mousemove”,this.onMouseMove);
document.removeEventListener(“mouseup”,this.onMouseUp);
这是我的国家({
拖动:false
});
}
//获取画布的边界框
componentDidMount(){
如果(!this.state.bounds){
这是我的国家({
边界:this.selector.current.getBoundingClientRect()
})
}
}
render(){
常量框=this.props.operatorPositions.map((op,index)=>{
返回
});
返回(
{box}
)
}
}
我不赞成这种方法。从演示中可以看出,缩放限制了画布的可访问区域,并破坏了当前在画布中拖动和定位元素的能力

这个问题与其说是找到一个bug,不如说是请求帮助开发一种方法来实现所描述系统的缩放。我不难使用当前的方法,但任何方法都需要满足一些参数:

  • 可以存储和重新创建画布中所有元素的位置
  • 可以在画布中拖动元素
  • 画布可以放大或缩小以获得更好的可见性,而不会影响元素相对于背景的移动或位置
我简化了演示中的一些逻辑,因为完整的实现需要几个复杂的组件,但要点是根组件存储数据并将适当的位置传递到画布以供显示。单击对象时,它指定要更新的对象位置的索引,并将事件侦听器添加到窗口中,以更新mousemove上的对象位置,并删除mouseup上的活动对象和事件侦听器

非常感谢您在开发解决方案时提供的任何帮助