已更新Vue3版,請給 前端大全 發送關鍵字 vue3仿探探 獲取Vue3版
類似Tinder和探探的卡片效果的組件,社區中已經非常多了。我這一版除了可以實現和他們一樣的效果外。還增加了飛卡的效果,就是類似我的女神邱淑貞這樣。
可以將卡片朝任意拖拽方向飛出去,必須得帥是不是!
下面帶大家一步步來實現這個效果
先疊起來其實我最早實現這個效果,是在2018年的時候,在weex上實現過一版。原帖請看這裡:https://zhuanlan.zhihu.com/p/37482853
咱們這次不要求在weex裡可用,所以疊起來非常簡單。
首先讓三個卡片按照近大遠小的原則分別設置設置z-index,寬和高,比如每一層卡片的寬和高比上一層卡片要縮小20個像素(還有一種做法是通過zoom或者scale來設置遠處卡片的縮小級別)。然後加入絕對定位position:absolute和z-index就可以將卡片層疊起來了。
拖動第一張卡因為只有第一張卡片可以拖動,所以我們只要監聽第一張卡片的拖動事件。比如touchstart,touchmove,touchcancel,touchend。
拖動的時候需要注意,在touchstart的時候記錄一下手指按下的位置,在touchmove時要減去這個位置,看上去就是點哪兒從哪兒拖。
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);
第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="";
}
}
源碼倉庫https://github.com/ezshine/ezflycard
已更新Vue3版,請給 前端大全 發送關鍵字 vue3仿探探 獲取Vue3版