技术栈:vue
scene : 移动端页面左右滑动切换tab栏,实现页面切换类 app
原生动画,不影响页面正常点击事件
key : 移动端左右滑动,可以从touchstart
, touchmove
, touchend
事件入手,获取 pageX
左右移动的距离来判断方向,pageY
的上下移动距离来确定是否是滚动事件,监听 touch
的 trigger event 来判断是否是 tap
事件,从而实现功能
// template
<div @touchstart.stop = "touchStart" @touchmove.stop = "touchMove" @touchend.stop = "touchEnd" class="touch-box">
</div>
// methods
touchStart(e) {
const touch = e.touches[0]
this.touch.startX = touch.pageX
this.touch.startY = touch.pageY
this.date_start = new Date().getTime();
},
touchEnd(e) {
let touch = e.changedTouches[0]
let date_end = new Date().getTime();
let dur = date_end - this.date_start;
let startX = this.touch.startX
let startY = this.touch.startY
let disX = touch.pageX - startX
let disY = touch.pageY - startY
// 判断是滑动还是点击事件
// 距离和时间可以自主调节
if((Math.abs(disX)>10 && Math.abs(disX)< 130) || Math.abs(disY)>100){
if(Math.abs(disX) > 5*Math.abs(disY) && dur < 200){
let slectTabArr = this.slectTabArr
let number = 0
let selectTabFlag = getStorage('selectTabFlag')
slectTabArr.filter((item,index)=>{
if(selectTabFlag === item){
number = index
}
})
if(disX < -10){
// 向左滑动则向右选中列表中的数据切换
}
if(disX >10){
// 向右滑动则向左选中列表中的数据切换
}
} else {
return false
}
} else {
return false
}
},
注意:这里不能用 e.preventdefault
会发现默认事件都禁止那么滑动事件和点击事件也都禁止了。
scene : **真机测试ios
和 android
的触发机制并不一样,在android中无法主动触发 touchend
事件 **
key : 这里主要去排查了一下,android是通过禁止 touchmove
的 e.preventdefault
默认事件 去触发后面的 touchend
事件,所以兼容 ios
与 Android
touchMove(e){
let u = navigator.userAgent
if(u.indexOf('Android') > -1 || u.indexOf('Linux') >-1 ){
let date_end = new Date().getTime();
let dur = date_end - this.date_start;
let touch = e.touches[0]
let startX = this.touch.startX
let startY = this.touch.startY
let disX = touch.pageX - startX
let disY = touch.pageY - startY
if (Math.abs(disX)>10 && Math.abs(disY) < 20 && dur<200) {
//表示是滚动完成后的那个touch不触发/间隔太短
e.preventDefault();
}
} else {
return false
}
},
touchEnd(e) {
let touch = e.changedTouches[0]
let date_end = new Date().getTime();
let dur = date_end - this.date_start;
let startX = this.touch.startX
let startY = this.touch.startY
let disX = touch.pageX - startX
let disY = touch.pageY - startY
// 判断是滑动还是点击事件
let u = navigator.userAgent
if( u.indexOf('iPhone') > -1){
if((Math.abs(disX)>10 && Math.abs(disX)< 130) || Math.abs(disY)>100){
if(Math.abs(disX) > 5*Math.abs(disY) && dur < 200){
let slectTabArr = this.slectTabArr || []
let number = 0
let selectTabFlag = getStorage('selectTabFlag')
slectTabArr.filter((item,index)=>{
if(selectTabFlag === item){
number = index
}
})
if(disX < -10){
// 向左滑动则向右选中列表中的数据切换
number = (number >= slectTabArr.length-1) ? 0 : ++number
selectTabFlag = slectTabArr[number]
// 点击切换切换到选中tab栏
this.selectTabBar(selectTabFlag)
setStorage("selectTabFlag",selectTabFlag)
}
if(disX >10){
// 向右滑动则向左选中列表中的数据切换
number = number <= 0 ? slectTabArr.length-1 : --number
selectTabFlag = slectTabArr[number]
// 点击切换切换到选中tab栏
this.selectTabBar(selectTabFlag)
setStorage("selectTabFlag",selectTabFlag)
}
}else {
return false
}
} else {
return false
}
} else if(u.indexOf('Android') > -1 || u.indexOf('Linux') > -1){
if(Math.abs(disX)>10 || Math.abs(disY)>10){
if(Math.abs(disX) > 5*Math.abs(disY) && dur < 200){
let slectTabArr = this.slectTabArr || []
let number = 0
let selectTabFlag = getStorage('selectTabFlag')
slectTabArr.filter((item,index)=>{
if(selectTabFlag === item){
number = index
}
})
if(disX < -10){
// 向左滑动则向右选中列表中的数据切换
number = number >= slectTabArr.length-1 ? 0 : ++number
selectTabFlag = slectTabArr[number]
// 点击切换切换到选中tab栏
this.selectTabBar(selectTabFlag)
setStorage("selectTabFlag",selectTabFlag)
}
if(disX >10){
// 向右滑动则向左选中列表中的数据切换
number = number <= 0 ? slectTabArr.length-1 : --number
selectTabFlag = slectTabArr[number]
// 点击切换切换到选中tab栏
this.selectTabBar(selectTabFlag)
setStorage("selectTabFlag",selectTabFlag)
}
}else {
return false
}
} else {
return false
}
}
},
scene : tab栏动画实现
key : 这里遇到一个选择,应该是用transition-group 还是使用class类定义左右不同的动画,应为tab栏本身的切换会有对应的active样式,且不需要显示隐藏,所以选择了加class类来实现
<ul class="company-tab-bar" >
<li class="company-tab-item"
v-for="(item,index) in slectTabArr"
:key = " `current${index}` "
:class="[{'active':selectTabFlag=== item },slideDir]"
@click="selectTabBar(item)">
{{slectTabObj[item]}}
</li>
</ul>
// style
.company-tab-item {
display: inline-block;
margin-right: 28/@r;
position: relative;
&.active {
color: @bgheader;
&::before {
content: "";
width: 100%;
height: 2/@r;
position: absolute;
bottom: 0;
background-color: @bgheader;
border-radius: 1/@r;
}
}
&.left.active {
&::before {
animation: transformAnimate 0.2s ease-in-out;
}
}
&.right.active {
&::before {
animation: transformAnimateRight 0.2s ease-in-out;
}
}
}
@keyframes transformAnimate {
0% {
transform:translateX(-100%);
}
100% {
transform:translateX(0);
}
}
@keyframes transformAnimateRight {
0% {
transform:translateX(100%);
}
100% {
transform:translateX(0);
}
}
scene : tab栏切换,对应的内容页的动画用, transition-group实现, 使用的时报错 <transition-group> children must be keyed:
key : 这里使用是 vue
中自带的组建 transition-group
,其具体实现原理可以参考官方文档,这里不再赘述,关键字是key , 这里的key是识别当前选中的唯一标识。 name 中的 transitionDir
代表 style 中 class 类的前缀 slide-right || slide-left
<transition-group
tag="div"
appear
:name="transitionDir" >
<div v-show="selectTabFlag==='businessInformation' " :key=" `businessInformation`">
<BusinessInformation :businessData ="businessInfo" ></BusinessInformation>
</div>
</transition-group>
// style
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active {
will-change: transform;
transition: all .3s;
position: absolute;
width:100%;
left:0;
}
.slide-right-enter {
transform: translateX(-100%);
}
.slide-right-leave-active {
transform: translateX(100%);
}
.slide-left-enter {
transform: translateX(100%);
}
.slide-left-leave-active {
transform: translateX(-100%);
}
总结 : 类似抛转引玉,transition transition-group 也可以结合 animate.css
其用法及原理还是比较的有意思,有时间会总结一版 ;在实现tab切换的时候,搜索,发现同类总结并不多,大多一些小demo,这里列举希望提供一种实现的思路方法发散大家的思维,时间紧凑并没有延伸,如有错误还望指正。