已更新Vue3版,請看此篇 備戰2021,仿探探拖拽卡片效果Vue3實現javascript
相似Tinder
和探探
的卡片效果的組件,社區中已經很是多了。我這一版除了能夠實現和他們同樣的效果外。還增長了飛卡
的效果,就是相似個人女神邱淑貞這樣。css
能夠將卡片朝任意拖拽方向飛出去,必須得帥
是否是!前端
下面帶你們一步步來實現這個效果vue
絕對原創,若有雷同,純屬對方抄襲
😁java
其實我最先實現這個效果,是在2018年的時候,在weex上實現過一版。原帖請看這裏:zhuanlan.zhihu.com/p/37482853git
我們此次不要求在weex裏可用,因此疊起來很是簡單。github
首先讓三個卡片按照近大遠小
的原則分別設置設置z-index
,寬和高,好比每一層卡片的寬和高比上一層卡片要縮小20個像素(還有一種作法是經過zoom或者scale來設置遠處卡片的縮小級別)。而後加入絕對定位position:absolute
和z-index
就能夠將卡片層疊起來了。web
由於只有第一張卡片能夠拖動,因此咱們只要監聽第一張卡片的拖動事件。好比touchstart
,touchmove
,touchcancel
,touchend
。markdown
拖動的時候須要注意,在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張了
)卡片的大小和位置。
當第一張卡飛出去後
this.width2=this.cardWidth;
this.height2=this.cardHeight;
this.left2=0;
this.top2=0;
複製代碼
this.width3=(this.cardWidth-this.leftPad*2);
this.height3=(this.cardHeight-this.topPad*2);
this.left3=this.leftPad;
this.top3=(this.topPad*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="";
}
}
複製代碼
這周我會抽空再實現一個vue3版本的~
近期文章(感謝掘友的鼓勵與支持🌹🌹🌹)
歡迎拍磚,一塊兒探討更優雅的實現