一、先画下页面
1、在views下新建4个页面
Home.vue
<template>
<div class="home">首页</div>
</template>
<style lang="less" scoped>
.home {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(deeppink, 0.2);
}
</style>
Address.vue
<template>
<div class="address">通讯录</div>
</template>
<style lang="less" scoped>
.address {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(deepskyblue, 0.2);
}
</style>
Discover.vue
<template>
<div class="discover">发现</div>
</template>
<style lang="less" scoped>
.discover {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(darkgreen, 0.2);
}
</style>
Mine.vue
<template>
<div class="mine">我的</div>
</template>
<style lang="less" scoped>
.mine {
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(peru, 0.2);
}
</style>
2、路由整一下
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{ path: '/', redirect: { name: 'home' } },
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@/views/Home'),
meta: { index: 0 }
},
{
path: '/address',
name: 'address',
component: () =>
import(/* webpackChunkName: "address" */ '@/views/Address'),
meta: { index: 1 }
},
{
path: '/discover',
name: 'discover',
component: () =>
import(/* webpackChunkName: "discover" */ '@/views/Discover'),
meta: { index: 2 }
},
{
path: '/mine',
name: 'mine',
component: () => import(/* webpackChunkName: "mine" */ '@/views/Mine'),
meta: { index: 3 }
}
]
const router = new VueRouter({ routes })
export default router
3、App.vue
<template>
<div id="app">
<router-view class="routerView" />
<ul class="menus">
<li
v-for="item of menus"
:key="item.path"
@click="handleToggle(item.path)"
>
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
menus: [
{ name: '首页', path: '/home' },
{ name: '通讯录', path: '/address' },
{ name: '发现', path: '/discover' },
{ name: '我的', path: '/mine' }
],
slideName: ''
}
},
methods: {
handleToggle(path) {
if (this.$route.path === path) return
this.$router.push({ path })
}
}
}
</script>
<style lang="less" scoped>
#app {
height: 100vh;
position: relative;
overflow: hidden;
.routerView {
width: 100%;
height: calc(100% - 36px);
}
.menus {
position: absolute;
width: 100vw;
bottom: 0;
display: flex;
align-items: center;
border-top: 1px solid #ccc;
border-left: 1px solid #ccc;
border-bottom: 1px solid #ccc;
li {
cursor: pointer;
width: 25%;
text-align: center;
line-height: 36px;
border-right: 1px solid #ccc;
}
}
}
</style>
二、加入过渡动画
App.vue
<transition :name="slideName">
<router-view class="routerView" />
</transition>
监听路由,动态设置过渡动画的类名
watch: {
$route(to, from) {
if (to.meta.index > from.meta.index) {
this.slideName = 'slide-left'
} else {
this.slideName = 'slide-right'
}
}
},
或者改下handleToggle方法
handleToggle(path) {
if (this.$route.path === path) return
const currentIndex = this.$route.meta.index
const toIndex = this.menus.findIndex(n => n.path === path)
this.slideName = toIndex > currentIndex ? 'slide-left' : 'slide-right'
this.$router.push({ path })
}
添加动画样式
// 动画-start
.slide-left-leave-active,
.slide-left-enter-active,
.slide-right-leave-active,
.slide-right-enter-active {
transition: all 0.5s;
position: absolute;
}
.slide-right-leave,
.slide-right-enter-to {
transform: translateX(0);
}
.slide-right-leave-to {
transform: translateX(100%);
}
.slide-right-enter {
transform: translateX(-100%);
}
.slide-left-leave,
.slide-left-enter-to {
transform: translateX(0);
}
.slide-left-leave-to {
transform: translateX(-100%);
}
.slide-left-enter {
transform: translateX(100%);
}
// 动画-end
效果:
动画速度搞慢点看下DOM结构:
三、左右滑动也加上动画
1、安装依赖
npm install vue-directive-touch
2、main.js
import touch from 'vue-directive-touch'
Vue.use(touch)
3、给router-view加上滑动事件
<transition :name="slideName">
<router-view
v-touch:left="onSwipeLeft"
v-touch:right="onSwipeRight"
class="routerView"
/>
</transition>
事件类型:
- 单击: v-touch:tap
- 长按: v-touch:long
- 左滑: v-touch:left
- 右滑: v-touch:right
- 上滑: v-touch:up
- 下滑: v-touch:down
4、滑动事件方法
// 向左滑动
onSwipeLeft() {
const currentIndex = this.$route.meta.index
if (currentIndex < 3) {
const { path } = this.menus[currentIndex + 1]
console.log('向左滑动', currentIndex, path)
this.$router.push({ path })
this.slideName = 'slide-left'
} else {
console.log('已经是最后一页')
}
},
// 向右滑动
onSwipeRight() {
const currentIndex = this.$route.meta.index
if (currentIndex > 0) {
const { path } = this.menus[currentIndex - 1]
console.log('向左滑动', currentIndex, path)
this.$router.push({ path })
this.slideName = 'slide-right'
} else {
console.log('已经是第一页')
}
}
效果:
很丝滑~
四、自定义滑动指令
自定义滑动指令,替代vue-directive-touch
src/directives/v-touch.js
class VueTouch {
constructor(el, binding, type) {
// 触屏函数
var _this = this
this.obj = el
this.binding = binding
this.touchType = type
this.vueTouches = { x: 0, y: 0 } // 触屏坐标
this.vueMoves = true
this.vueLeave = true
this.vueCallBack =
typeof binding.value == 'object' ? binding.value.fn : binding.value
this.obj.addEventListener(
'touchstart',
function (e) {
_this.start(e)
},
false
)
this.obj.addEventListener(
'touchend',
function (e) {
_this.end(e)
},
false
)
this.obj.addEventListener(
'touchmove',
function (e) {
_this.move(e)
},
false
)
}
start(e) {
// 监听touchstart事件
this.vueMoves = true
this.vueLeave = true
this.longTouch = true
this.vueTouches = {
x: e.changedTouches[0].clientX,
y: e.changedTouches[0].clientY
}
// console.log('targetTouches', e.targetTouches[0])
this.time = setTimeout(
function () {
if (this.vueLeave && this.vueMoves) {
this.touchType == 'longtap' && this.vueCallBack(this.binding.value, e)
this.longTouch = false
}
}.bind(this),
1000
)
}
end(e) {
// 监听touchend事件
// console.log('this.vueTouches', this.vueTouches)
// console.log('this.changedTouches', e.changedTouches[0].clientX)
// 此处代码用来解决IOS回弹问题 begain
if (e.changedTouches[0].clientX < 0) {
return
}
// 此处代码用来解决IOS回弹问题 end
var disX = e.changedTouches[0].clientX - this.vueTouches.x // 计算移动的位移差
var disY = e.changedTouches[0].clientY - this.vueTouches.y
// console.log(Math.abs(disX), Math.abs(disY))
clearTimeout(this.time)
if (Math.abs(disX) > 10 || Math.abs(disY) > 100) {
// 当横向位移大于10,纵向位移大于100,则判定为滑动事件
this.touchType == 'swipe' && this.vueCallBack(this.binding.value, e) // 若为滑动事件则返回
if (Math.abs(disX) > Math.abs(disY)) {
// 判断是横向滑动还是纵向滑动
if (disX > 30) {
this.touchType == 'swiperight' &&
this.vueCallBack(this.binding.value, e) // 右滑
}
if (disX < -30) {
this.touchType == 'swipeleft' &&
this.vueCallBack(this.binding.value, e) // 左滑
}
} else {
if (disY > 30) {
this.touchType == 'swipedown' &&
this.vueCallBack(this.binding.value, e) // 下滑
}
if (disY < -30) {
this.touchType == 'swipeup' && this.vueCallBack(this.binding.value, e) // 上滑
}
}
} else if (this.longTouch && this.vueMoves) {
this.touchType == 'tap' && this.vueCallBack(this.binding.value, e)
this.vueLeave = false
}
}
move(e) {
// 监听touchmove事件
this.vueMoves = false
}
}
export default VueTouch
src/directives/index.js
import Vue from 'vue'
import VueTouch from './v-touch'
const touchs = [
{ directive: 'ztap', touchType: 'tap' }, // 点击
{ directive: 'zswipe', touchType: 'swipe' }, // 滑动
{ directive: 'zswipeleft', touchType: 'swipeleft' }, // 左滑
{ directive: 'zswiperight', touchType: 'swiperight' }, // 右滑
{ directive: 'zswipedown', touchType: 'swipedown' }, // 下滑
{ directive: 'zswipeup', touchType: 'swipeup' }, // 上滑
{ directive: 'zlongtap', touchType: 'longtap' } // 长按
]
for (const item of touchs) {
Vue.directive(item.directive, {
bind(el, binding) {
new VueTouch(el, binding, item.touchType)
}
})
}
main.js
import './directives'
App.vue中可以使用v-zswipeleft指令:
<!-- <router-view
v-touch:left="onSwipeLeft"
v-touch:right="onSwipeRight"
class="routerView"
/> -->
<!-- 可改为 -->
<router-view
v-zswipeleft="onSwipeLeft"
v-zswiperight="onSwipeRight"
class="routerView"
/>
如果你觉得这篇文章对你有用,可以看看作者封装的库xtt-utils,里面封装了非常实用的js方法。如果你也是vue开发者,那更好了,除了常用的api,还有大量的基于element-ui组件库二次封装的使用方法和自定义指令等,帮你提升开发效率。不定期更新,欢迎交流~