javascript - 获取拖动元素抛出时的距离和时间值

标签 javascript css animation css-animations physics

我想知道当用户停止拖动元素时如何获取距离和时间的值。 经过一番研究,我听说了加速度和速度的概念,但我仍然很困惑。

说明此动画的一些示例:

这是我陷入困境的情况:

let draggableEl = document.getElementById('draggable');
let mousePos = { x: -1, y: -1 };

draggableEl.addEventListener('mousedown', onMousedown);

function onMousedown(e) {
  document.documentElement.addEventListener('mouseup', onMouseup);
  document.documentElement.addEventListener('mousemove', updateMousePos);
  updateMousePos(e);
  updateUI();
}

function onMouseup() {
  document.documentElement.removeEventListener('mousemove', updateMousePos);
  mousePos.x = -1;
  mousePos.y = -1;

  // Now how to get the time and throwVal ?
  // draggableEl.style.transition = `transform ${time} ease-out-in`
  // draggableEl.style.transform = `translate(${throwVal.x}, ${throwVal.y})` 
}

function updateMousePos(e) {
  mousePos.x = e.pageX;
  mousePos.y = e.pageY;
}

function updateUI() {
  if (mousePos.x === -1 && mousePos.y === -1)
    return;
  draggableEl.style.transform = `translate( ${mousePos.x}px, ${mousePos.y}px)`;
  requestAnimationFrame(updateUI);
}
#draggable {
  position: absolute;
  background: red;
  width: 100px;
  height: 100px;
  cursor: grab;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Drag</title>
</head>
<body>

  <div id="draggable">Drag me</div>
  
</body>
</html>

最佳答案

我们可以设置让可拖动元素在一定时间后停止,而不是计算可拖动元素停止所需的时间(这涉及到一些相对复杂的物理公式)。

为了使其更加真实,我们可以使用cubic-bezier函数。使用它,我们可以模拟它,使它看起来像是在失去速度。我们不必完全模仿物理学的工作方式(使用幻觉)。

这是一个工作示例(带有一些关于其工作原理的评论)。我还修改了一些代码,以便初始 mousedown 事件不会移动元素。

let draggableEl = document.getElementById('draggable');
let mousePos = { 
  x: -1, 
  y: -1,
  started: false,
  startX: -1,
  startY: -1
};

// Used to track the last UI's transform position after the deceleration
// and also after any dragging
let uiTransform = {
  x: 0,
  y: 0
};

// Used to simulate UI's deceleration
let uiMotion = {
  oldX: -1,
  oldY: -1,
  x: -1,
  y: -1
};

draggableEl.addEventListener('mousedown', onMousedown);

function onMousedown(e) {
  // Extract the last transform value
  // Necessary because deceleration may be stopped by mousedown
  // before the UI's natural deceleration is finished
  let transformsValue = draggableEl.style.transform.match(/(-?\d*\.?\d+)/g)
  draggableEl.style.transition = 'none'
  uiTransform.x = (transformsValue && parseFloat(transformsValue[0])) || 0
  uiTransform.y = (transformsValue && parseFloat(transformsValue[1])) || 0
  uiTransform.offsetTop // Trigger layout reflow so that transition none is applied
  draggableEl.style.transform = `translate(${uiTransform.x}px, ${uiTransform.y}px)`

  document.documentElement.addEventListener('mouseup', onMouseup);
  document.documentElement.addEventListener('mousemove', updateMousePos);
  updateMousePos(e);
  updateUI();
}

function onMouseup() {
  document.documentElement.removeEventListener('mouseup', onMouseup);
  document.documentElement.removeEventListener('mousemove', updateMousePos);
  uiTransform.x += mousePos.x - mousePos.startX
  uiTransform.y += mousePos.y - mousePos.startY

  // The throwVal you asked for
  // Time to decelerate is 1s
  let throwVal = {
    x: (uiMotion.x - uiMotion.oldX) * 3,
    y: (uiMotion.y - uiMotion.oldY) * 3
  }
  draggableEl.style.transition = `transform 1s cubic-bezier(.27,1.04,.61,.97)`
  draggableEl.style.transform = `translate(${uiTransform.x + throwVal.x}px, ${uiTransform.y + throwVal.y}px)`
  
  mousePos.x = -1;
  mousePos.y = -1;
  mousePos.startX = -1;
  mousePos.startY = -1;
  mousePos.started = false;
  
  uiMotion.x = -1
  uiMotion.y = -1
  uiMotion.oldX = -1
  uiMotion.oldY = -1
}

function updateMousePos(e) {
  if (!mousePos.started) {
    mousePos.startX = e.pageX;
    mousePos.startY = e.pageY;
    mousePos.started = true;
  }
  mousePos.x = e.pageX;
  mousePos.y = e.pageY;
}

function updateUI() {
  if (mousePos.x === -1 && mousePos.y === -1)
    return;
  // Fixed some code
  let xValue = uiTransform.x + mousePos.x - mousePos.startX
  let yValue = uiTransform.y + mousePos.y - mousePos.startY
  draggableEl.style.transform = `translate(${xValue}px, ${yValue}px)`;
  
  if (uiMotion.oldX === -1 && uiMotion.oldY === -1) {
    uiMotion.oldX = xValue
    uiMotion.oldY = yValue
  } else {
    if (uiMotion.x !== -1 && uiMotion.y !== -1) {
      uiMotion.oldX = uiMotion.x
      uiMotion.oldY = uiMotion.y
    }
    uiMotion.x = xValue
    uiMotion.y = yValue
  }
  requestAnimationFrame(updateUI);
}
html {
background: #121212;}

#draggable {
  position: absolute;
  background: #585858;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  cursor: grab;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Drag</title>
</head>
<body>

  <div id="draggable"></div>
  
</body>
</html>

关于javascript - 获取拖动元素抛出时的距离和时间值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61339462/

相关文章:

javascript - 如何进行不区分大小写的搜索?

javascript - 从谷歌浏览器中的任何网页获取所有电话标签

html - CSS溢出-x :none not working

按钮的 CSS 样式 : Using &lt;input type="button> instead of <button>

css - 在我的主要 css 动画之前添加淡入淡出

JavaScript 文本转换

javascript - 表单中的文本幻灯片

html - 宽度和高度为0的DIV影响其他元素的相对位置

html - chrome 中的 css 3d 动画问题

iOS 动画 : CGAffineTransformMakeScale grows before shrinking