touchStart 的 DragStart event.datatransfer.setData 等效项是什么?

发布于 2025-01-10 23:36:29 字数 278 浏览 0 评论 0原文

在反应中,我有一个想要拖放的元素。现在我有一个dragStart和dragEnd,它们使用ev.dataTransfer.setData(data)将我的元素拖放到另一个框中。

但是,我也希望能够在触摸屏上执行此操作。当我使用触摸屏时,dragStart 不执行任何操作。我发现我应该使用 TouchStart 和 TouchEnd 作为触摸屏。但是,touchStart 没有 dataTransfer 事件。我想知道相当于什么?

如果没有等效的方法,那么我在触摸屏上拖放元素的好方法是什么?

谢谢

In react I have an element that I want to drag and drop. Right now I have a dragStart and dragEnd that uses ev.dataTransfer.setData(data) to drag and drop my element to a different box.

However, I also want to be able to do this on a touch screen. When I use a touchscreen, the dragStart doesn't do anything. I figured out instead that I should use TouchStart and TouchEnd for the touchscreen. However, there is no dataTransfer event with touchStart. I'm wondering what the equivalent would be?

If there is no equivalent, what would be a good way for me to drag and drop my elements on touchscreen?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月光色 2025-01-17 23:36:29

Reactjs 对我来说有点陌生,但通常在普通中,触摸数据传输可以通过将数据存储到对象/变量中来完成。基本上,这就是拖动元素时 dataTransfer 所做的全部工作。

为此,在 ontouchstart 事件中,像使用 dataTransfer.setData() 一样存储要携带的数据,然后通过复制元素来模拟元素的拖动。

  touchstart(e, item, index, arr) {
    this.state.touch['item'] = item
    this.state.touch['index'] = index
    this.state.touch['arr'] = arr
    let image = document.createElement("img"); // Create a new element
    image.setAttribute("id", "image-float");


    // get the image from the stored reference
    image.src = 'https://cdn.quasar.dev/img/avatar' + item + '.jpg';
    image.width = 100
    image.height = 100

    // position the image to the touch, can be improve to detect the position of touch inside the image
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    image.style.opacity = 0.5;
    document.getElementById('app').appendChild(image);
  }

然后在ontouchmove事件中,可以让复制的元素跟随光标,模拟拖动效果。在 touchmove 事件中,我们可以检测到光标位于 dropzone 元素内,就像 DnD 的 onDrop 事件一样。这是DnD帮助平滑体验的手动部分,但是对于触摸,我们需要自己进行检测。

  touchmove(e) {
    let image = document.getElementById('image-float')
    // this will give us the dragging feeling of the element while actually it's a different element
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';

    let topDrop = this.getDropzone(e, left, top, 'top')
    let botDrop = this.getDropzone(e, left, top, 'bottom')

    if (!topDrop && !botDrop) this.state.touch.drop = null
    if (topDrop) this.state.touch.drop = 'top'
    if (botDrop) this.state.touch.drop = 'bottom'
  }

  getDropzone(e, left, top, id) {
    let rect1 = document.getElementById(id).getBoundingClientRect();
    // to detect the overlap of mouse into the dropzone, as alternative of mouseover
    var overlap = !(rect1.right < left ||
      rect1.left > left ||
      rect1.bottom < top ||
      rect1.top > top)

    return overlap

  }

最后,在 ontouchend 事件中,我们执行 dataTransfer.getData() 的逻辑。因为我们已经检测到光标是否在拖放区域内,所以我们可以相应地执行逻辑

  touchend() {    
    let image = document.getElementById('image-float')
    image.remove()
    let item = this.state.touch.item
    let index = this.state.touch.index
    let arr = this.state[this.state.touch.arr]
    let drop = this.state[this.state.touch.drop]

    /*   console.log(this.state.touch.arr) */
    if (this.state.touch.drop != null) {
      if (drop != arr) {
        drop.push(item)
        arr.splice(index, 1)
      }
      return this.setState({
        state: {
          drop: this.state[drop],
          arr: this.state[arr]
        }
      })
    }
  }

下面,是我在 Reactjs 中传递此内容的尝试

class TodoApp extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      top: [
        '2',
        '3',
        '4',
      ],
      bottom: [],
      touch: {}
    }
  }

  dragstart(e, item, index, arr) {
    e.dataTransfer.dropEffect = 'move'
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('item', item)
    e.dataTransfer.setData('index', index)
    e.dataTransfer.setData('arr', arr)
  }
  drop(e, drop) {
    let item = e.dataTransfer.getData('item')
    let index = e.dataTransfer.getData('index')
    let arr = e.dataTransfer.getData('arr')

    if (drop != item.arr) {
      this.state[drop].push(item)
      this.state[arr].splice(index, 1)
    }
    return this.setState({
      state: {
        drop: this.state[drop],
        arr: this.state[arr]
      }
    })
  }

  touchstart(e, item, index, arr) {
    this.state.touch['item'] = item
    this.state.touch['index'] = index
    this.state.touch['arr'] = arr
    let image = document.createElement("img"); // Create a new element
    image.setAttribute("id", "image-float");


    // get the image from the stored reference
    image.src = 'https://cdn.quasar.dev/img/avatar' + item + '.jpg';
    image.width = 100
    image.height = 100

    // position the image to the touch, can be improve to detect the position of touch inside the image
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    image.style.opacity = 0.5;
    document.getElementById('app').appendChild(image);
  }

  touchmove(e) {
    let image = document.getElementById('image-float')
    // this will give us the dragging feeling of the element while actually it's a different element
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';

    let topDrop = this.getDropzone(e, left, top, 'top')
    let botDrop = this.getDropzone(e, left, top, 'bottom')

    if (!topDrop && !botDrop) this.state.touch.drop = null
    if (topDrop) this.state.touch.drop = 'top'
    if (botDrop) this.state.touch.drop = 'bottom'
  }

  getDropzone(e, left, top, id) {
    let rect1 = document.getElementById(id).getBoundingClientRect();
    // to detect the overlap of mouse into the dropzone, as alternative of mouseover
    var overlap = !(rect1.right < left ||
      rect1.left > left ||
      rect1.bottom < top ||
      rect1.top > top)

    return overlap

  }

  touchend() {

    let image = document.getElementById('image-float')
    image.remove()
    let item = this.state.touch.item
    let index = this.state.touch.index
    let arr = this.state[this.state.touch.arr]
    let drop = this.state[this.state.touch.drop]

    /*   console.log(this.state.touch.arr) */
    if (this.state.touch.drop != null) {
      if (drop != arr) {
        drop.push(item)
        arr.splice(index, 1)
      }
      return this.setState({
        state: {
          drop: this.state[drop],
          arr: this.state[arr]
        }
      })
    }
  }


  render() {
    return ( <
      div className = "container" >
      <
      div id = "top"
      className = "dropzone"
      onDrop = {
        (event) => this.drop(event, 'top')
      }
      onDragOver = {
        (e) => {
          e.preventDefault()
        }
      }
      onDragEnter = {
        (e) => {
          e.preventDefault()
        }
      } > {
        this.state.top.map((item, i) => {
          return ( < img src = {
              'https://cdn.quasar.dev/img/avatar' + item + '.jpg'
            }
            width = "100"
            height = "100"
            key = {
              i
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'top')
            }
            onTouchStart = {
              (event) => this.touchstart(event, item, i, 'top')
            }
            onTouchMove = {
              (event) => this.touchmove(event)
            }
            onTouchEnd = {
              (event) => this.touchend()
            }
            draggable = 'true' /
            >
          )
        })
      } < /div>

      <
      div id = "bottom"
      className = "dropzone"
      onDrop = {
        (event) => this.drop(event, 'bottom')
      }
      onDragOver = {
        (e) => {
          e.preventDefault()
        }
      }
      onDragEnter = {
        (e) => {
          e.preventDefault()
        }
      } > {
        this.state.bottom.map((item, i) => {
          return ( < img src = {
              'https://cdn.quasar.dev/img/avatar' + item + '.jpg'
            }
            width = "100"
            height = "100"
            key = {
              i
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'bottom')
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'bottom')
            }
            onTouchStart = {
              (event) => this.touchstart(event, item, i, 'bottom')
            }
            onTouchMove = {
              (event) => this.touchmove(event)
            }
            onTouchEnd = {
              (event) => this.touchend()
            }
            draggable = 'true' /
            >
          )
        })
      } < /div>  </div >
    )
  }
}

ReactDOM.render( < TodoApp / > , document.getElementById("app"))
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

button {
  color: #4fc08d;
}

button {
  background: none;
  border: solid 1px;
  border-radius: 2em;
  font: inherit;
  padding: 0.75em 2em;
}

.dropzone {
  display: flex;
  height: fit-content;
  min-width: 50px;
  min-height: 50px;
  background: #2D2D2D;
  margin: 10px;
  padding: 10px;
}

.dropzone>* {
  margin: 0 5px;
}

.container {
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

Reactjs is abit alien to me, but generally in vanilla, dataTransfer for touch can be done by storing your data into object/variable. Basically that's all dataTransfer do while you drag an element.

To do this, ontouchstart event, store the data that you want to carry like you did with dataTransfer.setData(), then simulate the dragging of an element by duplicating it.

  touchstart(e, item, index, arr) {
    this.state.touch['item'] = item
    this.state.touch['index'] = index
    this.state.touch['arr'] = arr
    let image = document.createElement("img"); // Create a new element
    image.setAttribute("id", "image-float");


    // get the image from the stored reference
    image.src = 'https://cdn.quasar.dev/img/avatar' + item + '.jpg';
    image.width = 100
    image.height = 100

    // position the image to the touch, can be improve to detect the position of touch inside the image
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    image.style.opacity = 0.5;
    document.getElementById('app').appendChild(image);
  }

then in ontouchmove event, you can have the duplicated element to follow cursor, to simulate the dragging effect. while on touchmove event, we can detect either our cursor is inside the dropzone element, like you would normally have as onDrop event for DnD. this is the manual part that DnD has helped to smoothen the experience, but for touch, we need to do the detection by ourself.

  touchmove(e) {
    let image = document.getElementById('image-float')
    // this will give us the dragging feeling of the element while actually it's a different element
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';

    let topDrop = this.getDropzone(e, left, top, 'top')
    let botDrop = this.getDropzone(e, left, top, 'bottom')

    if (!topDrop && !botDrop) this.state.touch.drop = null
    if (topDrop) this.state.touch.drop = 'top'
    if (botDrop) this.state.touch.drop = 'bottom'
  }

  getDropzone(e, left, top, id) {
    let rect1 = document.getElementById(id).getBoundingClientRect();
    // to detect the overlap of mouse into the dropzone, as alternative of mouseover
    var overlap = !(rect1.right < left ||
      rect1.left > left ||
      rect1.bottom < top ||
      rect1.top > top)

    return overlap

  }

finally, in our ontouchend event, we do the logic as we have for our dataTransfer.getData(). Because we already detect if our cursor is within the dropzone or not, so we can perform the logic accordingly

  touchend() {    
    let image = document.getElementById('image-float')
    image.remove()
    let item = this.state.touch.item
    let index = this.state.touch.index
    let arr = this.state[this.state.touch.arr]
    let drop = this.state[this.state.touch.drop]

    /*   console.log(this.state.touch.arr) */
    if (this.state.touch.drop != null) {
      if (drop != arr) {
        drop.push(item)
        arr.splice(index, 1)
      }
      return this.setState({
        state: {
          drop: this.state[drop],
          arr: this.state[arr]
        }
      })
    }
  }

Below, is my attempt on having this delivered in Reactjs

class TodoApp extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      top: [
        '2',
        '3',
        '4',
      ],
      bottom: [],
      touch: {}
    }
  }

  dragstart(e, item, index, arr) {
    e.dataTransfer.dropEffect = 'move'
    e.dataTransfer.effectAllowed = 'move'
    e.dataTransfer.setData('item', item)
    e.dataTransfer.setData('index', index)
    e.dataTransfer.setData('arr', arr)
  }
  drop(e, drop) {
    let item = e.dataTransfer.getData('item')
    let index = e.dataTransfer.getData('index')
    let arr = e.dataTransfer.getData('arr')

    if (drop != item.arr) {
      this.state[drop].push(item)
      this.state[arr].splice(index, 1)
    }
    return this.setState({
      state: {
        drop: this.state[drop],
        arr: this.state[arr]
      }
    })
  }

  touchstart(e, item, index, arr) {
    this.state.touch['item'] = item
    this.state.touch['index'] = index
    this.state.touch['arr'] = arr
    let image = document.createElement("img"); // Create a new element
    image.setAttribute("id", "image-float");


    // get the image from the stored reference
    image.src = 'https://cdn.quasar.dev/img/avatar' + item + '.jpg';
    image.width = 100
    image.height = 100

    // position the image to the touch, can be improve to detect the position of touch inside the image
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';
    image.style.opacity = 0.5;
    document.getElementById('app').appendChild(image);
  }

  touchmove(e) {
    let image = document.getElementById('image-float')
    // this will give us the dragging feeling of the element while actually it's a different element
    let left = e.touches[0].pageX;
    let top = e.touches[0].pageY;
    image.style.position = 'absolute'
    image.style.left = left + 'px';
    image.style.top = top + 'px';

    let topDrop = this.getDropzone(e, left, top, 'top')
    let botDrop = this.getDropzone(e, left, top, 'bottom')

    if (!topDrop && !botDrop) this.state.touch.drop = null
    if (topDrop) this.state.touch.drop = 'top'
    if (botDrop) this.state.touch.drop = 'bottom'
  }

  getDropzone(e, left, top, id) {
    let rect1 = document.getElementById(id).getBoundingClientRect();
    // to detect the overlap of mouse into the dropzone, as alternative of mouseover
    var overlap = !(rect1.right < left ||
      rect1.left > left ||
      rect1.bottom < top ||
      rect1.top > top)

    return overlap

  }

  touchend() {

    let image = document.getElementById('image-float')
    image.remove()
    let item = this.state.touch.item
    let index = this.state.touch.index
    let arr = this.state[this.state.touch.arr]
    let drop = this.state[this.state.touch.drop]

    /*   console.log(this.state.touch.arr) */
    if (this.state.touch.drop != null) {
      if (drop != arr) {
        drop.push(item)
        arr.splice(index, 1)
      }
      return this.setState({
        state: {
          drop: this.state[drop],
          arr: this.state[arr]
        }
      })
    }
  }


  render() {
    return ( <
      div className = "container" >
      <
      div id = "top"
      className = "dropzone"
      onDrop = {
        (event) => this.drop(event, 'top')
      }
      onDragOver = {
        (e) => {
          e.preventDefault()
        }
      }
      onDragEnter = {
        (e) => {
          e.preventDefault()
        }
      } > {
        this.state.top.map((item, i) => {
          return ( < img src = {
              'https://cdn.quasar.dev/img/avatar' + item + '.jpg'
            }
            width = "100"
            height = "100"
            key = {
              i
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'top')
            }
            onTouchStart = {
              (event) => this.touchstart(event, item, i, 'top')
            }
            onTouchMove = {
              (event) => this.touchmove(event)
            }
            onTouchEnd = {
              (event) => this.touchend()
            }
            draggable = 'true' /
            >
          )
        })
      } < /div>

      <
      div id = "bottom"
      className = "dropzone"
      onDrop = {
        (event) => this.drop(event, 'bottom')
      }
      onDragOver = {
        (e) => {
          e.preventDefault()
        }
      }
      onDragEnter = {
        (e) => {
          e.preventDefault()
        }
      } > {
        this.state.bottom.map((item, i) => {
          return ( < img src = {
              'https://cdn.quasar.dev/img/avatar' + item + '.jpg'
            }
            width = "100"
            height = "100"
            key = {
              i
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'bottom')
            }
            onDragStart = {
              (event) => this.dragstart(event, item, i, 'bottom')
            }
            onTouchStart = {
              (event) => this.touchstart(event, item, i, 'bottom')
            }
            onTouchMove = {
              (event) => this.touchmove(event)
            }
            onTouchEnd = {
              (event) => this.touchend()
            }
            draggable = 'true' /
            >
          )
        })
      } < /div>  </div >
    )
  }
}

ReactDOM.render( < TodoApp / > , document.getElementById("app"))
body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

button {
  color: #4fc08d;
}

button {
  background: none;
  border: solid 1px;
  border-radius: 2em;
  font: inherit;
  padding: 0.75em 2em;
}

.dropzone {
  display: flex;
  height: fit-content;
  min-width: 50px;
  min-height: 50px;
  background: #2D2D2D;
  margin: 10px;
  padding: 10px;
}

.dropzone>* {
  margin: 0 5px;
}

.container {
  overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文