如何給拖拽交互添加緩動

前言

移動端的拖拽交互,在項目中常常使用。javascript

實現拖拽也不復雜,經過綁定 touchstarttouchmove、和 touchend 事件,就能夠完成 自定義滾輪遊戲人物拖拽 等各類交互。java

若是想讓體驗更好一些,添加一些緩動效果,是必不可少的。git

沒有添加緩動

以上圖片是沒有添加緩動的,能夠看到拖拽起來畫面至關靈活,可是隨着手指離開屏幕,畫面也就戛然而止了。github

new.gif

以上圖片是添加了緩動的,手指快速的滑動後離開,畫面沒有快速中止,而是通過一段慣性動做後中止。typescript

添加緩動以後的效果,更符合現實中的慣性習慣。npm

實現思路

思路一

以前製做緩動,會在 touchmove 事件中經過多個參數來記錄實時的拖拽速度,在 touchend 的時候,在經過必定的加速度來讓速度逐步減到 0 ,完整代碼較長,放在了 文末markdown

這樣實現的缺點不少,好比參數不少、邏輯複雜,不能知足快速開發的需求。oop

思路二

touchmove 階段,不直接賦值,而是記錄下來,經過 requestAnimationFrame 計算差值,逐步賦值給運動的元素,便可實現。ui

這樣實現的優勢就很明確了,參數少、邏輯清晰。this

接下來詳細介紹一下思路二:

代碼詳解

首先不考慮緩動,先完成基本的拖拽事件:

拖拽事件

let initX = 0;
let initY = 0;
let changeX = 0;
let changeY = 0;
let element = window.document;

element.addEventListener('touchstart',(e)=>{
    initX = e.touches[0].clientX;
    initY = e.touches[0].clientY;
});

element.addEventListener('touchmove',(e)=>{
    const { clientX: moveX, clientY: moveY } = e.touches[0];
    changeX += (moveX - initX);
    changeY += (moveY - initY);
    initX = moveX;
    initY = moveY;
});
複製代碼

經過以上簡單的代碼,咱們就拿到了實時拖拽的 changeXchangeY ,而後賦值給元素便可。

添加緩動

按照以前的思路,咱們在 touchmove 中不直接賦值,而是隻記錄值:

let targetX = 0;
let targetY = 0;

element.addEventListener('touchmove',(e)=>{
    const { clientX: moveX, clientY: moveY } = e.touches[0];
    targetX += (moveX - initX);
    targetY += (moveY - initY);
    initX = moveX;
    initY = moveY;
});
複製代碼

拿到在 touchmove 中記錄的 targetXtargetY 以後,只須要思考如何緩慢的把 target 的值賦值給 change ,在這裏只須要給 changeXchangeY 賦值的時候,添加一個減速倍率便可:

function touchAnimate(){
    requestAnimationFrame(touchAnimate);
    changeX += (targetX - changeX) * 0.1; // 此處減速倍率設置爲 0.1
    changeY += (targetY - changeY) * 0.1;
}
複製代碼

至此,就完成了拖拽的緩動操做,完整代碼查看 文末附錄

總結

上述的簡單代碼就實現了拖拽的緩動,同時也是這個邏輯的核心代碼。

實際開發中還要考慮添加 touchFlag 、手指移出規定的區域怎麼處理等狀況。所以我把該拖拽緩動功能製做成了一個組件,方便後續各類相似的項目使用。

組件使用

能夠 查看源碼 或者使用 npm 安裝使用:

npm i drag-easing --save
複製代碼

基礎示例

import DragEasing from 'drag-easing';

// 初始化
const de = new DragEasing({
    onDragging: (e)=>{
        // e.changeX 即爲添加了緩動的拖拽 X 座標
        // e.changeY 即爲添加了緩動的拖拽 Y 座標
    },
});
複製代碼

同時 組件 還提供了指定綁定元素、x y 方向的區間限制、以及手指移出交互區域的操做等,更多 API 能夠在 點此 查看。

(完)

附錄

思路一完整代碼

tip: 此處使用到了 ts 語法,代碼未處理,但只要閱讀思路便可:

private timer:number = null;
private easeTimer:number = null;
private rotateNum:number = 359;
private startX:number = -1;
private moveX:number = -1;
private scaleNum:number = 750 / window.innerWidth;
private easeTime:number = -1;
private tempUnit:number = null;
private easeUnit:number = -1;
private easeDirection:string = '';
private rotateToTimer:number = null;
private rotateToNum:number = null;
private rotateToSpeed:number = null;
private touchStartCtrl(e){
    if(this.rotateToSpeed !== null) return;
    this.easeStop();
    this.startX = e.touches[0].clientX * this.scaleNum;
    this.animate();
}
private touchMoveCtrl(e){
    this.moveX = e.touches[0].clientX * this.scaleNum;
    if(this.moveX < 0 || this.moveX > 750){
        this.touchFinish(false);
    }
}
private touchEndCtrl(e){
    this.touchFinish(this.moveX !== -1);
}
private touchFinish(easeFlag:boolean = true){
    if(easeFlag) this.easing();
    this.startX = -1;
    this.moveX = -1;
    window.cancelAnimationFrame(this.timer);
}
private animate(){
    if(this.startX>-1&&this.moveX>-1){
        const changeNum:number = (this.moveX - this.startX) / 30;
        this.rotateNum = this.rotateNum += changeNum;
        this.tempUnit = 0.05 * (this.moveX-this.startX);
        this.startX = this.moveX;
    }
    this.timer = requestAnimationFrame(()=>{
        this.animate();
    })
}
private easing(){
    if(this.easeTime === -1){
        this.easeTime = 100;
        if(this.tempUnit === 0){
            this.easeStop();
            return;
        }
        this.easeDirection = this.tempUnit > 0 ? 'right' : 'left';
        const maxUnit = 1;
        this.easeUnit = this.tempUnit > 0 ? Math.max(maxUnit, this.tempUnit) : Math.min(maxUnit*-1, this.tempUnit);
    }
    if(this.easeTime > 0){
        this.easeTime = this.easeTime - 1;
        this.easeUnit -= this.easeUnit*0.1;
        this.rotateNum += this.easeUnit;
        this.easeTimer = requestAnimationFrame(()=>{
            this.easing();
        })
    }else{
        this.easeStop();
    }
}
private easeStop(){
    window.cancelAnimationFrame(this.easeTimer);
    this.easeTime = -1;
    this.easeUnit = -1;
}
複製代碼

思路二完整代碼

let initX = 0;
let initY = 0;
let changeX = 0;
let changeY = 0;
let targetX = 0;
let targetY = 0;
let element = window.document;

element.addEventListener('touchstart',(e)=>{
    initX = e.touches[0].clientX;
    initY = e.touches[0].clientY;
});

element.addEventListener('touchmove',(e)=>{
    const { clientX: moveX, clientY: moveY } = e.touches[0];
    targetX += (moveX - initX);
    targetY += (moveY - initY);
    initX = moveX;
    initY = moveY;
});

function touchAnimate(){
    requestAnimationFrame(touchAnimate);
    changeX += (targetX - changeX) * 0.1;
    changeY += (targetY - changeY) * 0.1;
}
touchAnimate();
複製代碼
相關文章
相關標籤/搜索