簡體   English   中英

用戶滑動(觸摸開始,1個或更多觸摸移動,觸摸結束)后,如何計算將元素移動多遠?

[英]After a user swipes (touchstart, 1 or more touchmove, touchend), how calculate how far to keep moving an element?

我想根據用戶滑動的速度來移動元素。 我具有緩動功能,可以使其快速啟動並隨時間變慢,但是我需要根據其滑動來計算的一個變量是totalTime (或totalSteps )。

我將如何計算?

我知道的:

  1. 他們開始滑動的時間
  2. 每次touchmove的時間和距離
  3. 滑動結束的時間( touchend

據此,我需要計算將它們移動多遠(緩動函數將處理每個單獨步驟的距離)。 我該如何計算?

緩動功能:

function easeOutCubic(currTime, beginningValue, change, duration)
{
    return change * ( ( currTime = currTime / duration - 1 ) * currTime * currTime + 1 ) + beginningValue;
}

change是我需要計算的。

為了使它起作用,您需要像我的示例一樣循環:

首先,您需要獲取事件內第一次觸摸和最后一次觸摸的坐標,並將其存儲在觸摸事件外部的某個位置:

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

獲得坐標后,在touchend事件中執行以下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延遲內燒成。

為了獲得在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> 

我不是代碼的創建者,如果您需要性能前端的更多示例,請在此處找到並關注Aerotwist

編輯

因此,現在它使用實際拖動的速度,您可能仍需要進行一些調整以獲得正確的感覺,我在此處的更新功能中添加了注釋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM