zhangxinxu/quiz

DOM基础测试48期

Opened this issue · 5 comments

移动端,页面上A,B两个div元素,请实现:

拖拽A元素到B元素上,A元素append到B元素中。


本题考点在于移动端的拖拽功能实现。大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```

其他
本期小测没有直播,也没有打分,但是会反馈要点。

jsbin

#draggable {
  width: 200px;
  height: 20px;
  text-align: center;
  background: white;
}

#dropzone {
  position: absolute;
  top: 300px;
  width: 200px;
  height: 20px;
  background: blueviolet;
  margin-bottom: 10px;
  padding: 10px;
}

.abs {
  position: absolute;
  z-index: 1;
}
<div id="draggable">
  被拖拽的元素
</div>
<div id="dropzone"></div>
var draggable = document.querySelector('#draggable')
var dropzone = document.querySelector('#dropzone')
var { width, height } = draggable.getBoundingClientRect()
var hasTouch = false
draggable.addEventListener('touchstart', touchstart)
document.addEventListener('touchmove', touchmove)
draggable.addEventListener('touchend', touchend)
draggable.addEventListener('touchcancel', reset)

function touchstart() {
  hasTouch = true
  draggable.classList.add('abs')
}
function touchmove(e) {
  if (hasTouch) {
    var touch = e.changedTouches[0]
    var pageX = touch.pageX
    var pageY = touch.pageY
    Object.assign(draggable.style, {
      left: pageX - width / 2 + 'px',
      top: pageY - height / 2 + 'px'
    })
  }
}
function touchend(e) {
  hasTouch = false
  var touch = e.changedTouches[0]
  var pageX = touch.pageX
  var pageY = touch.pageY
  var elements = document.elementsFromPoint(pageX, pageY)
  if (elements.includes(dropzone)) {
    dropzone.appendChild(draggable)
    draggable.removeEventListener('touchstart', touchstart)
    document.removeEventListener('touchmove', touchmove)
    draggable.removeEventListener('touchend', touchend)
    draggable.removeEventListener('touchcancel', reset)
  }
  reset()
}
function reset() {
  draggable.classList.remove('abs')
  Object.assign(draggable.style, {
    left: '',
    top: ''
  })
}

移动端可以使用touchEvent来处理拖拽操作;

demo

<div id="t1">A</div>
<div id="t2">B</div>
body {
  max-width: 400px;
}
#t1 {
  width: 100px;
  height: 100px;
  background-color: lightcoral;
}
#t2 {
  min-height: 200px;
  margin-top: 30px;
  background-color: lightcyan;
}
const A = document.getElementById('t1')
const B = document.getElementById('t2')
let first = null

function start (e) {
  first = e.touches[0]
}

function move (e) {
  const cur = e.touches[0]
  if (first) {
    A.style.transform = `translate(${cur.pageX - first.pageX}px, ${cur.pageY - first.pageY}px)`
  }
}

function end (e) {
  const boxA = A.getBoundingClientRect()
  const boxB = B.getBoundingClientRect()

  if ( boxA.left > boxB.left
      && boxA.top > boxB.top
      && boxA.right < boxB.right
      && boxA.bottom < boxB.bottom ) { // 判断拖放停止的位置是否完全处于B之中
    A.removeEventListener('touchstart', start)
    A.removeEventListener('touchmove', move)
    A.removeEventListener('touchend', end)
    B.appendChild(A)
  }
  A.style.transform = 'none'
}

A.addEventListener('touchstart', start)
A.addEventListener('touchmove', move)
A.addEventListener('touchend', end)

在线DEMO: jsbin

并没有实现 移动中的效果,只判断结束时候的状态

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <style>
    .ele{
      min-height: 100px;
      background-color:rgba(255,0,0,0.4);
      padding:8px;
    }
  </style>
</head>
<body>
 <div draggable="true" class="ele" id="eleA"></div>
 <hr>  
 <div class="ele" id="eleB"></div>
</body>
</html>
var eleA = document.getElementById("eleA");
var eleB = document.getElementById("eleB");
document.addEventListener("touchend", function (e) {
  // 移动的是 A 元素,并且A 元素不在B元素之内
  if (e.target === eleA && !eleB.contains(eleA)) {
    var objTouch = e.changedTouches[0];
    var rect = eleB.getBoundingClientRect();
    var xIn = objTouch.clientX < rect.right && objTouch.clientX > rect.left;
    var yIn = objTouch.clientY < rect.bottom && objTouch.clientY > rect.top;
    xIn && yIn && eleB.appendChild(eleA);
  }
});

DEMO

  <div class="a"> </div>
  <div class="b"></div>
let inDrag = false, timer = null, touchpos = { x: 0, y: 0 }
  const a = document.querySelector('.a'), b = document.querySelector('.b')
  b.addEventListener('touchstart', (e) => {
    const { pageX, pageY } = e.changedTouches[0];
    timer = setTimeout(() => {
      inDrag = true
      touchpos.x = pageX
      touchpos.y = pageY
      b.style.opacity = '.5'
    }, 50)
  })
  b.addEventListener('touchend', (e) => {
    if (!inDrag) {
      return
    }
    // 防止 elementFromPoint 拿到b
    b.style.display = 'none'
    clearTimeout(timer)
    const { pageX, pageY } = e.changedTouches[0];
    let isIna = false,
      element = document.elementFromPoint(pageX, pageY);
    while (element && element.parentElement) {
      if (element.classList.contains('a')) {
        isIna = true
        break
      }
      element = element.parentElement
    }
    if (isIna) {
      a.appendChild(b)
    } else {
      document.body.appendChild(b)
    }
    inDrag = false
    // 还原
    b.style.display = ''
    b.style.transform = ''
    b.style.opacity = ''
  })
  b.addEventListener('touchmove', (e) => {
    if (!inDrag) {
      return
    }
    e.preventDefault()
    e.stopPropagation()
    const { pageX, pageY } = e.changedTouches[0]
    b.style.transform = `translate(${-touchpos.x + pageX}px,${-touchpos.y + pageY}px)`
  })

Demo

    div {
      background-color: rgba(0,0,0,0.08);
      border: 1px solid
    }
    #dragable {
      height: 100px;
      width: 100px;
    }
    #container {
      height: 200px;
      width: 200px;
    }
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div id="dragable">可拖动</div>
  <div id="container"></div>
</body>
</html>
    let dragable = document.getElementById('dragable'),
        container = document.getElementById('container')
    // console.log(dragable, container)
    dragable.addEventListener('touchend', (e) => {
      let b = container.getBoundingClientRect()
      // console.log(a, b)
      // console.log(e.touches, e.changedTouches, e.targetTouches)
      let touches = e.changedTouches[0]
      console.log(e.changedTouches)
      if(
        touches.clientX > b.left &&
        touches.clientX < b.right &&
        touches.clientY > b.top &&
        touches.clientY < b.bottom
      ) {
        container.appendChild(dragable)
      }
    })