2020更新,Vue仿探探拖拽卡片的效果

已更新Vue3版,請看此篇 備戰2021,仿探探拖拽卡片效果Vue3實現javascript


相似Tinder探探的卡片效果的組件,社區中已經很是多了。我這一版除了能夠實現和他們同樣的效果外。還增長了飛卡的效果,就是相似個人女神邱淑貞這樣。css

能夠將卡片朝任意拖拽方向飛出去,必須得是否是!前端

下面帶你們一步步來實現這個效果vue

絕對原創,若有雷同,純屬對方抄襲😁java

先疊起來

其實我最先實現這個效果,是在2018年的時候,在weex上實現過一版。原帖請看這裏:zhuanlan.zhihu.com/p/37482853git

我們此次不要求在weex裏可用,因此疊起來很是簡單。github

首先讓三個卡片按照近大遠小的原則分別設置設置z-index,寬和高,好比每一層卡片的寬和高比上一層卡片要縮小20個像素(還有一種作法是經過zoom或者scale來設置遠處卡片的縮小級別)。而後加入絕對定位position:absolutez-index就能夠將卡片層疊起來了。web

拖動第一張卡

由於只有第一張卡片能夠拖動,因此咱們只要監聽第一張卡片的拖動事件。好比touchstart,touchmove,touchcancel,touchendmarkdown

拖動的時候須要注意,在touchstart的時候記錄一下手指按下的位置,在touchmove時要減去這個位置,看上去就是點哪兒從哪兒拖。weex

touchStart:function(e){
	var curTouch=e.touches[0];
	this.startLeft=curTouch.clientX-this.left;
	this.startTop=curTouch.clientY-this.top;
}
touchMove:function(e){
	var curTouch=e.touches[0];
	this.left=curTouch.clientX-this.startLeft;
	this.top=curTouch.clientY-this.startTop;
}
複製代碼

飛出去

要實現超任意拖拽方向飛出去這個效果,須要用到一些數學公式。

計算卡片當前拖拽的座標和起始座標的夾角

var angle=Math.atan2((當前座標.y-起點座標.y), (當前座標.x-起點座標.x));
複製代碼

飛出去的落點x軸座標經過計算angle的餘弦值再乘以力度得出

this.left=Math.cos(angle)*this.throwDistance;
複製代碼

飛出去的落點y軸座標經過計算angle的正弦值再乘以力度得出

this.top=Math.sin(angle)*this.throwDistance;
複製代碼

這裏我們作得再完善一些,在拖動結束時去判斷一下當前拖動的距離是否足夠觸發飛卡效果。若是不觸發飛卡效果,則觸發回位效果。這樣的話也能夠防止用戶誤操做。

//計算兩點之間的直線距離
getDistance:function(x1, y1, x2, y2) {
    var _x = Math.abs(x1 - x2); 
    var _y = Math.abs(y1 - y2); 
    return Math.sqrt(_x * _x + _y * _y);
}
            
var distance=this.getDistance(0,0,this.left,this.top);
if(distance>this.throwTriggerDistance){
    this.makeCardThrow();
}else{
    this.makeCardBack();
}
複製代碼

下層的卡片上推

上推其實很簡單,一開始的時候,我就定義了四張(不是3張嗎?怎麼變4張了)卡片的大小和位置。

當第一張卡飛出去後

  • 第2張卡片變動爲本來第1張卡片的位置和大小
this.width2=this.cardWidth;
this.height2=this.cardHeight;
this.left2=0;
this.top2=0;
複製代碼
  • 第3張卡片變動爲本來第2張卡片的位置和大小
this.width3=(this.cardWidth-this.leftPad*2);
this.height3=(this.cardHeight-this.topPad*2);
this.left3=this.leftPad;
this.top3=(this.topPad*3);
複製代碼
  • 第4張卡片本來是透明的,如今變爲第3張卡片的位置和大小
this.width4=(this.cardWidth-this.leftPad*4);
this.height4=(this.cardHeight-this.topPad*4);
this.left4=this.leftPad*2;
this.top4=(this.topPad*6);
this.opacity4=1;
複製代碼

我把陰影效果先去掉,你們觀察一下這個細節

重置全部卡片

底層的卡片上推和第一張卡片的飛出效果是同時進行的,由css的transition來控制。不過期間是咱們設定好的,因此只要在上推和飛出的動畫時間結束後,咱們重置一下全部4張卡片的大小和位置便可。

this.onThrowStart();
setTimeout(function(){
    that.isThrow=false;
    that.isAnimating=false;
    that.onThrowDone();
    that.resetAllCard();
},400);
複製代碼

這裏須要注意,全部四張卡片都須要瞬間完成重置,因此這步以前應該禁用掉transition動畫。

組件化

爲了適應各類使用場景,咱們要將這個效果封裝一下。

//提供幾個事件,分別是拖動時,拖動結束,飛卡結束,飛卡失敗(回位)
@onDragMove='onCardDragMove'
@onDragStop='onCardDragStop'
@onThrowDone='onCardThrowDone'
@onThrowFail='onCardThrowFail'

//參數就不細說了,都能看明白
:cardWidth="200" 
:cardHeight="200"
cardBgColor="#fff"
:leftPad="10"
:topPad="6"
:borderRadius="8"
:throwTriggerDistance="100"
dragDirection="all"
:hasShadow="false"
:hasBorder="true"
複製代碼

提供三個slot,你能夠很是方便的往卡片裏塞內容

//firstCard,secondCard,thirdCard
<slot name="firstCard"></slot>
複製代碼

如今來模仿幾個效果

某乎的推薦回答

@onDragMove='onCardDragMove'
@onDragStop='onCardDragStop'
@onThrowDone='onCardThrowDone'
:cardWidth="300" 
:cardHeight="120"
:throwTriggerDistance="100"
dragDirection="horizontal"
:hasShadow="true"
複製代碼

僅容許水平拖動

因爲改變寬高會致使文字換行變化,也許卡片用縮放的話,用戶體驗會更好一些吧

探探的效果

實現探探效果的核心是監聽卡片拖動的位置

onCardDragMove(obj){
    if(obj.left<-10){
        this.actionName="不喜歡";
    }else if(obj.left>10){
        this.actionName="喜歡";
    }else{
        this.actionName="";
    }
}
複製代碼

源碼倉庫

github.com/ezshine/ezf…

這周我會抽空再實現一個vue3版本的~

關注大帥搞全棧

近期文章(感謝掘友的鼓勵與支持🌹🌹🌹)


歡迎拍磚,一塊兒探討更優雅的實現

相關文章
相關標籤/搜索