android仿陌陌tab,Vue|Nuxt.js仿探探卡片式左右拖拽|vue仿Tinder
开场技术宅男对探探/陌陌并不陌生,一款专注于陌生人的社交App。里面的左右滑动翻牌子效果更是让人眼前一亮,似乎有一种古时君王选妃子的感觉。让人玩的爱不释手。
一睹风采
哈哈,效果还行吧。下面就简单的讲解下具体的实现方法。
页面布局
页面整体分为 顶部Navbar、卡片区域、底部Tabbar 三个部分。
侧边弹出框
点击筛选,在侧边会出现弹窗。其中范围滑块、switch开关、Rate评分等组件则是使用Vant组件库。
侧边弹窗模板
范围
{{distanceVal}}
自动增加范围
性别
女生
好评度
{{starVal}}星
优先在线用户
优先新用户
export default {
// 用于配置应用默认的 meta 标签
head() {
return {
title: `${this.title} - 翻一翻`,
meta: [
{name:'keywords',hid: 'keywords',content:`${this.title} | 翻一翻 | 翻动卡片`},
{name:'description',hid:'description',content:`${this.title} | 仿探探卡片翻动`}
]
}
},
middleware: 'auth',
data () {
return {
title: 'Nuxt',
showFilter: false,
distanceRange: 1,
distanceVal: '<1km',
autoExpand: true,
starVal: 5,
firstOnline: false,
firstNewUser: true,
// ...
}
},
methods: {
/* @@左侧筛选函数 */
// 范围选择
handleDistanceRange(val) {
if(val == 1) {
this.distanceVal = '<1km';
} else if (val == 100) {
this.distanceVal = "100km+"
}else {
this.distanceVal = val+'km';
}
},
// 好评度
handleStar(val) {
this.starVal = val;
},
// ...
},
}
仿探探翻牌子
卡片区单独封装了一个组件flipcard,只需传入pages数据就可以。
在四周拖拽卡片会出现不同的斜切视角。
pages数据格式module.exports = [
{
avatar: '/assets/img/avatar02.jpg',
name: '放荡不羁爱自由',
sex: 'female',
age: 23,
starsign: '天秤座',
distance: '艺术/健身',
photos: [...],
sign: '交个朋友,非诚勿扰'
},
...
]
flipcard组件模板
@touchmove.stop.capture="touchmove"
@touchstart.stop.capture="touchstart"
@touchend.stop.capture="touchend($event, index)"
@touchcancel.stop.capture="touchend($event, index)"
@mousedown.stop.capture.prevent="touchstart"
@mouseup.stop.capture.prevent="touchend($event, index)"
@mousemove.stop.capture.prevent="touchmove"
@mouseout.stop.capture.prevent="touchend($event, index)"
@webkit-transition-end="onTransitionEnd(index)"
@transitiοnend="onTransitionEnd(index)"
>
{{item.name}}
{{item.age}}
{{item.starsign}}
{{item.distance}}
/**
* @Desc Vue仿探探|Tinder卡片滑动FlipCard
* @Time andy by 2020-10-06
* @About Q:282310962 wx:xy190310
*/
export default {
props: {
pages: {
type: Array,
default: {}
}
},
data () {
return {
basicdata: {
start: {},
end: {}
},
temporaryData: {
isStackClick: true,
offsetY: '',
poswidth: 0,
posheight: 0,
lastPosWidth: '',
lastPosHeight: '',
lastZindex: '',
rotate: 0,
lastRotate: 0,
visible: 3,
tracking: false,
animation: false,
currentPage: 0,
opacity: 1,
lastOpacity: 0,
swipe: false,
zIndex: 10
}
}
},
computed: {
// 划出面积比例
offsetRatio () {
let width = this.$el.offsetWidth
let height = this.$el.offsetHeight
let offsetWidth = width - Math.abs(this.temporaryData.poswidth)
let offsetHeight = height - Math.abs(this.temporaryData.posheight)
let ratio = 1 - (offsetWidth * offsetHeight) / (width * height) || 0
return ratio > 1 ? 1 : ratio
},
// 划出宽度比例
offsetWidthRatio () {
let width = this.$el.offsetWidth
let offsetWidth = width - Math.abs(this.temporaryData.poswidth)
let ratio = 1 - offsetWidth / width || 0
return ratio
}
},
methods: {
touchstart (e) {
if (this.temporaryData.tracking) {
return
}
// 是否为touch
if (e.type === 'touchstart') {
if (e.touches.length > 1) {
this.temporaryData.tracking = false
return
} else {
// 记录起始位置
this.basicdata.start.t = new Date().getTime()
this.basicdata.start.x = e.targetTouches[0].clientX
this.basicdata.start.y = e.targetTouches[0].clientY
this.basicdata.end.x = e.targetTouches[0].clientX
this.basicdata.end.y = e.targetTouches[0].clientY
// offsetY在touch事件中没有,只能自己计算
this.temporaryData.offsetY = e.targetTouches[0].pageY - this.$el.offsetParent.offsetTop
}
// pc操作
} else {
this.basicdata.start.t = new Date().getTime()
this.basicdata.start.x = e.clientX
this.basicdata.start.y = e.clientY
this.basicdata.end.x = e.clientX
this.basicdata.end.y = e.clientY
this.temporaryData.offsetY = e.offsetY
}
this.temporaryData.isStackClick = true
this.temporaryData.tracking = true
this.temporaryData.animation = false
},
touchmove (e) {
this.temporaryData.isStackClick = false
// 记录滑动位置
if (this.temporaryData.tracking && !this.temporaryData.animation) {
if (e.type === 'touchmove') {
e.preventDefault()
this.basicdata.end.x = e.targetTouches[0].clientX
this.basicdata.end.y = e.targetTouches[0].clientY
} else {
e.preventDefault()
this.basicdata.end.x = e.clientX
this.basicdata.end.y = e.clientY
}
// 计算滑动值
this.temporaryData.poswidth = this.basicdata.end.x - this.basicdata.start.x
this.temporaryData.posheight = this.basicdata.end.y - this.basicdata.start.y
let rotateDirection = this.rotateDirection()
let angleRatio = this.angleRatio()
this.temporaryData.rotate = rotateDirection * this.offsetWidthRatio * 15 * angleRatio
}
},
touchend (e, index) {
if(this.temporaryData.isStackClick) {
this.$emit('click', index)
this.temporaryData.isStackClick = false
}
this.temporaryData.isStackClick = true
this.temporaryData.tracking = false
this.temporaryData.animation = true
// 滑动结束,触发判断
// 判断划出面积是否大于0.4
if (this.offsetRatio >= 0.4) {
// 计算划出后最终位置
let ratio = Math.abs(this.temporaryData.posheight / this.temporaryData.poswidth)
this.temporaryData.poswidth = this.temporaryData.poswidth >= 0 ? this.temporaryData.poswidth + 200 : this.temporaryData.poswidth - 200
this.temporaryData.posheight = this.temporaryData.posheight >= 0 ? Math.abs(this.temporaryData.poswidth * ratio) : -Math.abs(this.temporaryData.poswidth * ratio)
this.temporaryData.opacity = 0
this.temporaryData.swipe = true
this.nextTick()
// 不满足条件则滑入
} else {
this.temporaryData.poswidth = 0
this.temporaryData.posheight = 0
this.temporaryData.swipe = false
this.temporaryData.rotate = 0
}
},
nextTick () {
// 记录最终滑动距离
this.temporaryData.lastPosWidth = this.temporaryData.poswidth
this.temporaryData.lastPosHeight = this.temporaryData.posheight
this.temporaryData.lastRotate = this.temporaryData.rotate
this.temporaryData.lastZindex = 20
// 循环currentPage
this.temporaryData.currentPage = this.temporaryData.currentPage === this.pages.length - 1 ? 0 : this.temporaryData.currentPage + 1
// currentPage切换,整体dom进行变化,把第一层滑动置最低
this.$nextTick(() => {
this.temporaryData.poswidth = 0
this.temporaryData.posheight = 0
this.temporaryData.opacity = 1
this.temporaryData.rotate = 0
})
},
onTransitionEnd (index) {
let lastPage = this.temporaryData.currentPage === 0 ? this.pages.length - 1 : this.temporaryData.currentPage - 1
// dom发生变化正在执行的动画滑动序列已经变为上一层
if (this.temporaryData.swipe && index === lastPage) {
this.temporaryData.animation = true
this.temporaryData.lastPosWidth = 0
this.temporaryData.lastPosHeight = 0
this.temporaryData.lastOpacity = 0
this.temporaryData.lastRotate = 0
this.temporaryData.swipe = false
this.temporaryData.lastZindex = -1
}
},
prev () {
this.temporaryData.tracking = false
this.temporaryData.animation = true
// 计算划出后最终位置
let width = this.$el.offsetWidth
this.temporaryData.poswidth = -width
this.temporaryData.posheight = 0
this.temporaryData.opacity = 0
this.temporaryData.rotate = '-3'
this.temporaryData.swipe = true
this.nextTick()
},
next () {
this.temporaryData.tracking = false
this.temporaryData.animation = true
// 计算划出后最终位置
let width = this.$el.offsetWidth
this.temporaryData.poswidth = width
this.temporaryData.posheight = 0
this.temporaryData.opacity = 0
this.temporaryData.rotate = '3'
this.temporaryData.swipe = true
this.nextTick()
},
rotateDirection () {
if (this.temporaryData.poswidth <= 0) {
return -1
} else {
return 1
}
},
angleRatio () {
let height = this.$el.offsetHeight
let offsetY = this.temporaryData.offsetY
let ratio = -1 * (2 * offsetY / height - 1)
return ratio || 0
},
inStack (index, currentPage) {
let stack = []
let visible = this.temporaryData.visible
let length = this.pages.length
for (let i = 0; i < visible; i++) {
if (currentPage + i < length) {
stack.push(currentPage + i)
} else {
stack.push(currentPage + i - length)
}
}
return stack.indexOf(index) >= 0
},
// 非首页样式切换
transform (index) {
let currentPage = this.temporaryData.currentPage
let length = this.pages.length
let lastPage = currentPage === 0 ? this.pages.length - 1 : currentPage - 1
let style = {}
let visible = this.temporaryData.visible
if (index === this.temporaryData.currentPage) {
return
}
if (this.inStack(index, currentPage)) {
let perIndex = index - currentPage > 0 ? index - currentPage : index - currentPage + length
style['opacity'] = '1'
style['transform'] = 'translate3D(0,0,' + -1 * 60 * (perIndex - this.offsetRatio) + 'px' + ')'
style['zIndex'] = visible - perIndex
if (!this.temporaryData.tracking) {
style['transitionTimingFunction'] = 'ease'
style['transitionDuration'] = 300 + 'ms'
}
} else if (index === lastPage) {
style['transform'] = 'translate3D(' + this.temporaryData.lastPosWidth + 'px' + ',' + this.temporaryData.lastPosHeight + 'px' + ',0px) ' + 'rotate(' + this.temporaryData.lastRotate + 'deg)'
style['opacity'] = this.temporaryData.lastOpacity
style['zIndex'] = this.temporaryData.lastZindex
style['transitionTimingFunction'] = 'ease'
style['transitionDuration'] = 300 + 'ms'
} else {
style['zIndex'] = '-1'
style['transform'] = 'translate3D(0,0,' + -1 * visible * 60 + 'px' + ')'
}
return style
},
// 首页样式切换
transformIndex (index) {
if (index === this.temporaryData.currentPage) {
let style = {}
style['transform'] = 'translate3D(' + this.temporaryData.poswidth + 'px' + ',' + this.temporaryData.posheight + 'px' + ',0px) ' + 'rotate(' + this.temporaryData.rotate + 'deg)'
style['opacity'] = this.temporaryData.opacity
style['zIndex'] = 10
if (this.temporaryData.animation) {
style['transitionTimingFunction'] = 'ease'
style['transitionDuration'] = (this.temporaryData.animation ? 300 : 0) + 'ms'
}
return style
}
},
}
}
组件支持touch和mouse事件,在移动端和PC端均可滑动。
另外,点击卡片跳转到卡片详细页面。
好了,基于Vue实现探探卡片效果就分享到这里。希望能喜欢~~ ✍
android仿陌陌tab,Vue|Nuxt.js仿探探卡片式左右拖拽|vue仿Tinder相关推荐
- 多款顶级好用的 Vue 表单设计器测评推荐,可拖拽生成表单
本文完整版:<多款顶级好用的 Vue 表单设计器测评推荐,可拖拽生成表单> Vue 表单设计器 form-generator - 适配 Element Plus UI 框架的表单设计器 f ...
- JS组件系列——Bootstrap Table 表格行拖拽(二:多行拖拽)
原文:JS组件系列--Bootstrap Table 表格行拖拽(二:多行拖拽) 前言:前天刚写了篇JS组件系列--Bootstrap Table 表格行拖拽,今天接到新的需要,需要在之前表格行拖拽的 ...
- JS组件系列——Bootstrap Table 表格行拖拽
JS组件系列--Bootstrap Table 表格行拖拽 原文:JS组件系列--Bootstrap Table 表格行拖拽 前言:之前一直在研究DDD相关知识,好久没更新JS系列文章了.这两天做了一 ...
- Vue/Nuxt.js仿Tinder|探探翻牌特效|vue仿探探卡片滑动
基于Vue.js|Nuxt.js实现探探卡片滑动切换效果 陌陌|探探社交App中拖拽滑动翻牌子效果让人印象深刻,最近在开发Nuxt项目,需要实现类似这个效果,于是经过多次调试,最终实现了,现整理作些简 ...
- 老弟,来了?VUE+Nuxt.js+Koa+Vuex入门教程(一)仿写一个cnode网站
if(有工作){if(工作地址 == "深圳" || 工作地址 == "广州" ){do(请联系作者,qq:1172081598)} } 何为Nuxt.js N ...
- Android仿探探卡片拖拽,Vue 仿探探拖拽卡片的效果
原标题:Vue 仿探探拖拽卡片的效果 已更新Vue3版,请给前端大全发送关键字vue3仿探探获取Vue3版 类似 Tinder 和 探探 的卡片效果的组件,社区中已经非常多了.我这一版除了可以实现和他 ...
- vue js 可随意拖动盒子 以及禁止拖拽
可拖动弹窗: 1.新建一个js,放置如下js代码 import Vue from 'vue'; //使用Vue.directive()定义一个全局指令 //1.参数一:指令的名称,定义时指令前面不需要 ...
- vue手势滚动_vue + any-touch实现一个iscroll 实现拖拽和滑动动画效果
https://github.com/383514580/any-touch 先看demo demo 说点湿的 iscroll其实代码量挺大的(近2100行, 还有另一个类似的库betterScrol ...
- 安卓开发仿微信图片拖拽_仿微信朋友圈发表图片拖拽和删除功能
原标题:仿微信朋友圈发表图片拖拽和删除功能 中国联通在香港公布了上市公司2017年中期业绩.2017年上半年,公司主要业绩指标持续向好,收入稳步回升,服务收入达到人民币1,241.1亿元,同比增长3. ...
最新文章
- 自己动手写一个能操作redis的客户端
- Oracle用户密码过期和用户被锁解决方法【转】
- 浅析Microsoft .net PetShop程序中的购物车和订单处理模块(Profile技术,异步MSMQ消息)转...
- 【阿里云API】 阿里云API调用的若干说明
- CTFshow 命令执行 web44
- bzoj3895: 取石子(博弈论,记忆化搜索)
- 最近找工作面的面试题目汇总(一)
- 第一百五十二期:白话Entity Framework Core数据验证
- Delphi 中的字符串函数(6) - StrUtils 中的 Ansi 字符串函数
- django+nginx+uwsgi项目部署文档整理
- 复合梯形公式与复合辛普森公式matlab_时尚女装套装的公式图纸分享
- html框架集frame是啥意思,HTML框架集frameset和内嵌框架iframe
- 校园网如何实现网络共享
- C++: decay关键字的作用
- 华为员工工资曝光:入职12年月薪31万,小编我瑟瑟发抖
- ftc文件_美国参议员指责FTC拒绝收集防病毒数据
- Arthas Spring Boot Starter工程启动报错
- 机器自动翻译古文拼音 - 十大宋词 - 青玉案 凌波不过横塘路 贺铸
- [实训题目EmoProfo]基于深度学习的表情识别服务搭建(一)
- 数据分析指标体系搭建实战!
热门文章
- 正则表达式 捕获分组的理解
- 关于spring boot的Bean named ‘aaa‘ is expected to be of type ‘bbb‘ but was actually of type ‘bbb‘问题的解决方案
- 211北京科技大学,计算机专硕考研变难了!
- 阿里云+tp5.1 实现语音合成(文字转音频)
- 顾比倒数线——出入场管理的概述与解读
- QVTKWidget控件显示二维图片
- AndroidStudio生成MD5、SHA1
- MOS管和IGBT管有什么区别?
- NB-IoT标准演进R13-R14
- HashMap 21 问!