在用户滑动(touchstart,1或更多touchmove,touchend)之后,如何计算移动元素的距离?

时间:2017-01-12 22:33:06

标签: javascript

我想根据用户滑动的速度移动一个元素。我有缓动功能使它快速启动并随着时间的推移变慢,但我需要根据滑动计算的一个变量是totalSteps(或touchmove)。

我该如何计算?

我所知道的:

  1. 他们开始滑动的时间
  2. 每个touchend
  3. 的时间和距离
  4. 滑动结束的时间(function easeOutCubic(currTime, beginningValue, change, duration) { return change * ( ( currTime = currTime / duration - 1 ) * currTime * currTime + 1 ) + beginningValue; }
  5. 由此我需要计算移动它们的距离(缓动函数将处理每个步骤的距离)。我该如何计算?

    缓和功能:

    change

    TypeError: must be str, not bytes是我需要计算的。

2 个答案:

答案 0 :(得分:0)

为了使其成功,你需要像我的例子那样循环:

首先,您需要获取事件内第一次和最后一次触摸的坐标,并将其存储在触摸事件之外的某处:

let startCoords = { x: event.touches[0].pageX, y : event.touches[0].pageY } 
let endCoords = { /* same way */ } 

获得完成坐标后,在touchend事件中执行此操作:

const animationTime = 0.5; // Animation time in seconds
const frameRate = 60;

var currentIteration = 0;
var iterationsCount = Math.round(frameRate * animationTime);



(function animate() {

    var x = easeOutCubic(currentIteration, startCoords.x, endCoords.x, iterationsCount);
    var y = easeOutCubic(currentIteration, startCoords.y, endCoords.y, iterationsCount);

                //here you set new x,y to your target element like this
                element.style.top = y +'px';
                element.style.left = x + 'px';


                currentIteration++;

                if (currentIteration < iterationsCount) {
                        requestAnimationFrame(animate);
                }
        })();

<强>已更新

为了提高动画效率,您需要使用touchmove事件,而不是touchend在延迟内触发它。

答案 1 :(得分:-2)

要获得dragstartdragend之间的时间:

var el = document.getElementById("foo");
var startTime = 0
var timeDelta = 0;

el.addEventListener('dragstart', function(evt){
  startTime = Date.now()/1000;
  });

el.addEventListener('dragend', function(evt){
  var endTime = Date.now()/1000;
  timeDelta = endTime - startTime;
  console.log(timeDelta);
  });
#foo {
  height: 100px;
  width: 100px;
  background: red;
  }
<div id="foo" draggable="true">
<div>

显然,您还需要附加其他事件touchstarttouchend等。

这种方法的实施

如果我理解你的问题,那么这应该会有所帮助。它几乎以touchstarttouchend为参考点并与之合作。

/**
 *
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

class Cards {
  constructor () {
    this.cards = Array.from(document.querySelectorAll('.card'));

    this.onStart = this.onStart.bind(this);
    this.onMove = this.onMove.bind(this);
    this.onEnd = this.onEnd.bind(this);
    this.update = this.update.bind(this);
    this.targetBCR = null;
    this.target = null;
    this.startX = 0;
    this.startTime = 0;
    this.endTime = 0;
    this.currentX = 0;
    this.screenX = 0;
    this.targetX = 0;
    this.lastVelocity = 0;
    this.draggingCard = false;

    this.addEventListeners();

    requestAnimationFrame(this.update);
  }

  addEventListeners () {
    document.addEventListener('touchstart', this.onStart);
    document.addEventListener('touchmove', this.onMove);
    document.addEventListener('touchend', this.onEnd);

    document.addEventListener('mousedown', this.onStart);
    document.addEventListener('mousemove', this.onMove);
    document.addEventListener('mouseup', this.onEnd);
  }

  onStart (evt) {
    if (this.target)
      return;

    if (!evt.target.classList.contains('card'))
      return;

    this.target = evt.target;
    this.targetBCR = this.target.getBoundingClientRect();

    this.startX = evt.pageX || evt.touches[0].pageX;
    this.startTime = Date.now()/1000;
    this.currentX = this.startX;

    this.draggingCard = true;
    this.target.style.willChange = 'transform';

    evt.preventDefault();
  }

  onMove (evt) {
    if (!this.target)
      return;

    this.currentX = evt.pageX || evt.touches[0].pageX;
  }

  onEnd (evt) {
    if (!this.target)
      return;

    this.targetX = 0;
    this.endTime = Date.now() /1000;
    let screenX = this.currentX - this.startX;
    const threshold = this.targetBCR.width * 0.35;
    if (Math.abs(screenX) > threshold) {
      this.targetX = (screenX > 0) ?
           this.targetBCR.width :
          -this.targetBCR.width;
    }

    this.draggingCard = false;
    
    // calculate velocity
    this.lastVelocity = (evt.pageX - this.startX )/ (this.endTime - this.startTime);
    console.log(this.lastVelocity);
  }

  update () {

    requestAnimationFrame(this.update);

    if (!this.target)
      return;

    if (this.draggingCard) {
      this.screenX = this.currentX - this.startX;
    } else {
      this.screenX += this.lastVelocity / 20; // change the number 20 to change the velocity applied to animation
    }
   

    const normalizedDragDistance =
        (Math.abs(this.screenX) / this.targetBCR.width);
    const opacity = 1 - Math.pow(normalizedDragDistance, 3);

    this.target.style.transform = `translateX(${this.screenX}px)`;
    this.target.style.opacity = opacity;

    // User has finished dragging.
    if (this.draggingCard)
      return;

    const isNearlyAtStart = (Math.abs(this.screenX) < 0.1);
    const isNearlyInvisible = (opacity < 0.01);

    // If the card is nearly gone.
    if (isNearlyInvisible) {

      // Bail if there's no target or it's not attached to a parent anymore.
      if (!this.target || !this.target.parentNode)
        return;

      this.target.parentNode.removeChild(this.target);

      const targetIndex = this.cards.indexOf(this.target);
      this.cards.splice(targetIndex, 1);

      // Slide all the other cards.
      this.animateOtherCardsIntoPosition(targetIndex);

    } else if (isNearlyAtStart) {
      this.resetTarget();
    }
  }

  animateOtherCardsIntoPosition (startIndex) {
    // If removed card was the last one, there is nothing to animate.
    // Remove the target.
    if (startIndex === this.cards.length) {
      this.resetTarget();
      return;
    }

    const onAnimationComplete = evt => {
      const card = evt.target;
      card.removeEventListener('transitionend', onAnimationComplete);
      card.style.transition = '';
      card.style.transform = '';

      this.resetTarget();
    };

    // Set up all the card animations.
    for (let i = startIndex; i < this.cards.length; i++) {
      const card = this.cards[i];

      // Move the card down then slide it up.
      card.style.transform = `translateY(${this.targetBCR.height + 20}px)`;
      card.addEventListener('transitionend', onAnimationComplete);
    }

    // Now init them.
    requestAnimationFrame(_ => {
      for (let i = startIndex; i < this.cards.length; i++) {
        const card = this.cards[i];

        // Move the card down then slide it up, with delay according to "distance"
        card.style.transition = `transform 150ms cubic-bezier(0,0,0.31,1) ${i*50}ms`;
        card.style.transform = '';
      }
    });
  }

  resetTarget () {
    if (!this.target)
      return;

    this.target.style.willChange = 'initial';
    this.target.style.transform = 'none';
    this.target = null;
  }
}

window.addEventListener('load', () => new Cards());
/**
 *
 * Copyright 2016 Google Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
html, body {
  margin: 0;
  padding: 0;
  background: #FAFAFA;
  font-family: Arial;
  font-size: 30px;
  color: #333;
}

* {
  box-sizing: border-box;
}

.card-container {
  width: 100%;
  max-width: 450px;
  padding: 16px;
  margin: 0 auto;
}

.card {
  background: #FFF;
  border-radius: 3px;
  box-shadow: 0 3px 4px rgba(0,0,0,0.3);
  margin: 20px 0;
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: space-around;
  cursor: pointer;
}
<!--
Copyright 2016 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<div class="card-container">
    <div class="card">Das Surma</div>
    <div class="card">Aerotwist</div>
    <div class="card">Kinlanimus Maximus</div>
    <div class="card">Addyoooooooooo</div>
    <div class="card">Gaunty McGaunty Gaunt</div>
    <div class="card">Jack Archibungle</div>
    <div class="card">Sam "The Dutts" Dutton</div>
  </div>

我不是代码的创建者,如果您需要更多高性能前端样本,可以找到它们here并按照aerotwist

修改

所以现在它使用实际拖动的速度,你仍然可能需要一些调整来获得正确的感觉,我在更新功能中添加了注释。