关键词

js实现盒子拖拽动画效果

实现盒子拖拽动画效果需要以下步骤:

1. 为拖拽目标元素绑定事件监听器

首先需要为需要拖拽的目标元素添加事件监听器,通常是mousedown事件或者touchstart事件。

let target = document.getElementById('drag-target');
target.addEventListener('mousedown', dragStart);
target.addEventListener('touchstart', dragStart);

其中dragStart函数是拖拽开始的处理函数,我们接下来会讲到。

2. 实现拖拽功能

当用户开始拖拽目标元素时,我们需要对鼠标或手指的移动进行监听,并实时更新目标元素的位置,以实现拖拽效果。我们需要给目标元素的父元素绑定事件监听器,监听mousemove事件或者touchmove事件,并在事件处理函数中更新目标元素的位置。

let container = document.getElementById('drag-container');
container.addEventListener('mousemove', dragging);
container.addEventListener('touchmove', dragging);

其中dragging函数是拖拽中的处理函数,我们接下来会讲到。

在处理函数中,我们通过事件对象获取鼠标或手指的位置,然后计算出目标元素相对于父元素的位置,再设置目标元素的样式即可。

function dragging(e) {
  e.preventDefault(); // 防止默认行为(比如滚动)
  let offsetX, offsetY;
  if (e.type === 'mousemove') {
    offsetX = e.clientX - container.offsetLeft;
    offsetY = e.clientY - container.offsetTop;
  } else if (e.type === 'touchmove') {
    offsetX = e.touches[0].clientX - container.offsetLeft;
    offsetY = e.touches[0].clientY - container.offsetTop;
  }
  target.style.left = offsetX + 'px';
  target.style.top = offsetY + 'px';
}

需要注意的是,在触摸设备上,我们需要获取touches数组中第一个触摸点的位置。

3. 实现拖拽动画效果

在上一步拖动中,我们已经可以实现目标元素的拖动。接下来我们需要添加缓动效果,使目标元素在松开鼠标或手指时能够平稳地移动到目标位置。我们可以使用requestAnimationFrame函数来实现动画效果。

首先,我们需要在mousedowntouchstart事件中记录目标元素的初始位置和事件发生的时间。

function dragStart(e) {
  let offsetX, offsetY;
  if (e.type === 'mousedown') {
    offsetX = e.clientX - target.offsetLeft;
    offsetY = e.clientY - target.offsetTop;
  } else if (e.type === 'touchstart') {
    offsetX = e.touches[0].clientX - target.offsetLeft;
    offsetY = e.touches[0].clientY - target.offsetTop;
  }
  let startTime = Date.now();
  let startPos = {x: target.offsetLeft, y: target.offsetTop};
  // 在这里保存初始位置和时间
}

然后,我们可以在mousemovetouchmove事件中更新目标元素的位置,并计算出目标位置和当前位置之间的距离和时间间隔,以便计算缓动效果。

function dragging(e) {
  // 在这里更新目标元素的位置
  let offsetX, offsetY;
  if (e.type === 'mousemove') {
    offsetX = e.clientX - container.offsetLeft;
    offsetY = e.clientY - container.offsetTop;
  } else if (e.type === 'touchmove') {
    offsetX = e.touches[0].clientX - container.offsetLeft;
    offsetY = e.touches[0].clientY - container.offsetTop;
  }
  target.style.left = offsetX + 'px';
  target.style.top = offsetY + 'px';

  // 在这里计算移动距离和时间间隔
  let distance = Math.sqrt(Math.pow(offsetX-startPos.x,2) + Math.pow(offsetY-startPos.y,2));
  let duration = Date.now() - startTime;
}

最后,在mouseuptouchend事件中,我们可以根据移动距离和时间间隔计算出缓动效果的参数(速度和加速度),然后使用requestAnimationFrame函数实现动画效果。

function dragEnd(e) {
  // 在这里计算速度和加速度
  let velocity = distance / duration;
  let direction = Math.atan2(offsetY-startPos.y, offsetX-startPos.x);
  let acceleration = velocity * 10;
  let startX = target.offsetLeft, startY = target.offsetTop;

  // 定义动画函数
  function animate() {
    let elapsed = Date.now() - startTime;
    let dx = startX + velocity * elapsed * Math.cos(direction);
    let dy = startY + velocity * elapsed * Math.sin(direction) + acceleration * Math.pow(elapsed, 2) / 2;
    // 判断动画是否结束,如果未结束继续更新位置,否则停止动画
    if (elapsed < duration) {
      target.style.left = dx + 'px';
      target.style.top = dy + 'px';
      requestAnimationFrame(animate);
    } else {
      target.style.left = startX + distance * Math.cos(direction) + 'px';
      target.style.top = startY + distance * Math.sin(direction) + 'px';
    }
  }
  animate();
}

这样,我们就实现了盒子拖拽动画效果。下面给出两个示例,分别实现了鼠标和触摸设备的拖拽效果。

示例 1:鼠标拖拽

<div id="drag-container" style="position: relative;width: 300px;height: 300px;background-color: #eee;">
  <div id="drag-target" style="position: absolute;width: 50px;height: 50px;background-color: red;"></div>
</div>

<script>
let target = document.getElementById('drag-target');
let container = document.getElementById('drag-container');
let startPos, startTime, distance, duration;

target.addEventListener('mousedown', dragStart);
container.addEventListener('mousemove', dragging);
container.addEventListener('mouseup', dragEnd);

function dragStart(e) {
  let offsetX = e.clientX - target.offsetLeft;
  let offsetY = e.clientY - target.offsetTop;
  startTime = Date.now();
  startPos = {x: target.offsetLeft, y: target.offsetTop};
}

function dragging(e) {
  e.preventDefault();
  let offsetX = e.clientX - container.offsetLeft;
  let offsetY = e.clientY - container.offsetTop;
  target.style.left = offsetX + 'px';
  target.style.top = offsetY + 'px';
  distance = Math.sqrt(Math.pow(offsetX-startPos.x,2) + Math.pow(offsetY-startPos.y,2));
  duration = Date.now() - startTime;
}

function dragEnd(e) {
  let velocity = distance / duration;
  let direction = Math.atan2(offsetY-startPos.y, offsetX-startPos.x);
  let acceleration = velocity * 10;
  let startX = target.offsetLeft, startY = target.offsetTop;

  function animate() {
    let elapsed = Date.now() - startTime;
    let dx = startX + velocity * elapsed * Math.cos(direction);
    let dy = startY + velocity * elapsed * Math.sin(direction) + acceleration * Math.pow(elapsed, 2) / 2;
    if (elapsed < duration) {
      target.style.left = dx + 'px';
      target.style.top = dy + 'px';
      requestAnimationFrame(animate);
    } else {
      target.style.left = startX + distance * Math.cos(direction) + 'px';
      target.style.top = startY + distance * Math.sin(direction) + 'px';
    }
  }
  animate();
}
</script>

示例 2:触摸设备拖拽

<div id="drag-container" style="position: relative;width: 300px;height: 300px;background-color: #eee;">
  <div id="drag-target" style="position: absolute;width: 50px;height: 50px;background-color: red;"></div>
</div>

<script>
let target = document.getElementById('drag-target');
let container = document.getElementById('drag-container');
let startPos, startTime, distance, duration;

target.addEventListener('touchstart', dragStart);
container.addEventListener('touchmove', dragging);
container.addEventListener('touchend', dragEnd);

function dragStart(e) {
  e.preventDefault();
  let offsetX = e.touches[0].clientX - target.offsetLeft;
  let offsetY = e.touches[0].clientY - target.offsetTop;
  startTime = Date.now();
  startPos = {x: target.offsetLeft, y: target.offsetTop};
}

function dragging(e) {
  e.preventDefault();
  let offsetX = e.touches[0].clientX - container.offsetLeft;
  let offsetY = e.touches[0].clientY - container.offsetTop;
  target.style.left = offsetX + 'px';
  target.style.top = offsetY + 'px';
  distance = Math.sqrt(Math.pow(offsetX-startPos.x,2) + Math.pow(offsetY-startPos.y,2));
  duration = Date.now() - startTime;
}

function dragEnd(e) {
  let velocity = distance / duration;
  let direction = Math.atan2(offsetY-startPos.y, offsetX-startPos.x);
  let acceleration = velocity * 10;
  let startX = target.offsetLeft, startY = target.offsetTop;

  function animate() {
    let elapsed = Date.now() - startTime;
    let dx = startX + velocity * elapsed * Math.cos(direction);
    let dy = startY + velocity * elapsed * Math.sin(direction) + acceleration * Math.pow(elapsed, 2) / 2;
    if (elapsed < duration) {
      target.style.left = dx + 'px';
      target.style.top = dy + 'px';
      requestAnimationFrame(animate);
    } else {
      target.style.left = startX + distance * Math.cos(direction) + 'px';
      target.style.top = startY + distance * Math.sin(direction) + 'px';
    }
  }
  animate();
}
</script>

本文链接:http://task.lmcjl.com/news/9660.html

展开阅读全文